Skip to content

Commit 3683f6b

Browse files
XadillaXaddaleax
authored andcommitted
repl: fix crash with large buffer tab completion
If the buffer or array is too large to completion, make a dummy smallest substitute object for it and emit a warning. PR-URL: #13817Fixes: #3136 Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Alexey Orlenko <[email protected]>
1 parent f8d76dc commit 3683f6b

File tree

2 files changed

+97
-5
lines changed

2 files changed

+97
-5
lines changed

‎lib/repl.js‎

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -688,8 +688,33 @@ function intFilter(item){
688688
return/^[A-Za-z_$]/.test(item);
689689
}
690690

691+
constARRAY_LENGTH_THRESHOLD=1e6;
692+
693+
functionmayBeLargeObject(obj){
694+
if(Array.isArray(obj)){
695+
returnobj.length>ARRAY_LENGTH_THRESHOLD ? ['length'] : null;
696+
}elseif(utilBinding.isTypedArray(obj)){
697+
returnobj.length>ARRAY_LENGTH_THRESHOLD ? [] : null;
698+
}
699+
700+
returnnull;
701+
}
702+
691703
functionfilteredOwnPropertyNames(obj){
692704
if(!obj)return[];
705+
constfakeProperties=mayBeLargeObject(obj);
706+
if(fakeProperties!==null){
707+
this.outputStream.write('\r\n');
708+
process.emitWarning(
709+
'The current array, Buffer or TypedArray has too many entries. '+
710+
'Certain properties may be missing from completion output.',
711+
'REPLWarning',
712+
undefined,
713+
undefined,
714+
true);
715+
716+
returnfakeProperties;
717+
}
693718
returnObject.getOwnPropertyNames(obj).filter(intFilter);
694719
}
695720

@@ -843,9 +868,11 @@ function complete(line, callback){
843868
if(this.useGlobal||vm.isContext(this.context)){
844869
varcontextProto=this.context;
845870
while(contextProto=Object.getPrototypeOf(contextProto)){
846-
completionGroups.push(filteredOwnPropertyNames(contextProto));
871+
completionGroups.push(
872+
filteredOwnPropertyNames.call(this,contextProto));
847873
}
848-
completionGroups.push(filteredOwnPropertyNames(this.context));
874+
completionGroups.push(
875+
filteredOwnPropertyNames.call(this,this.context));
849876
addStandardGlobals(completionGroups,filter);
850877
completionGroupsLoaded();
851878
}else{
@@ -865,13 +892,13 @@ function complete(line, callback){
865892
}
866893
}else{
867894
constevalExpr=`try{${expr} } catch (e){}`;
868-
this.eval(evalExpr,this.context,'repl',functiondoEval(e,obj){
895+
this.eval(evalExpr,this.context,'repl',(e,obj)=>{
869896
// if (e) console.log(e);
870897

871898
if(obj!=null){
872899
if(typeofobj==='object'||typeofobj==='function'){
873900
try{
874-
memberGroups.push(filteredOwnPropertyNames(obj));
901+
memberGroups.push(filteredOwnPropertyNames.call(this,obj));
875902
}catch(ex){
876903
// Probably a Proxy object without `getOwnPropertyNames` trap.
877904
// We simply ignore it here, as we don't want to break the
@@ -889,7 +916,7 @@ function complete(line, callback){
889916
p=obj.constructor ? obj.constructor.prototype : null;
890917
}
891918
while(p!==null){
892-
memberGroups.push(filteredOwnPropertyNames(p));
919+
memberGroups.push(filteredOwnPropertyNames.call(this,p));
893920
p=Object.getPrototypeOf(p);
894921
// Circular refs possible? Let's guard against that.
895922
sentinel--;

‎test/parallel/test-repl-tab-complete.js‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,71 @@ testMe.complete('.b', common.mustCall((error, data) =>{
305305
assert.deepStrictEqual(data,[['break'],'b']);
306306
}));
307307

308+
// tab completion for large buffer
309+
constwarningRegEx=newRegExp(
310+
'\\(node:\\d+\\) REPLWarning: The current array, Buffer or TypedArray has '+
311+
'too many entries\\. Certain properties may be missing from completion '+
312+
'output\\.');
313+
314+
[
315+
Array,
316+
Buffer,
317+
318+
Uint8Array,
319+
Uint16Array,
320+
Uint32Array,
321+
322+
Uint8ClampedArray,
323+
Int8Array,
324+
Int16Array,
325+
Int32Array,
326+
Float32Array,
327+
Float64Array,
328+
].forEach((type)=>{
329+
putIn.run(['.clear']);
330+
331+
if(type===Array){
332+
putIn.run([
333+
'var ele = [];',
334+
'for (let i = 0; i < 1e6 + 1; i++) ele[i] = 0;',
335+
'ele.biu = 1;'
336+
]);
337+
}elseif(type===Buffer){
338+
putIn.run(['var ele = Buffer.alloc(1e6 + 1); ele.biu = 1;']);
339+
}else{
340+
putIn.run([`var ele = new ${type.name}(1e6 + 1); ele.biu = 1;`]);
341+
}
342+
343+
common.hijackStderr(common.mustCall((err)=>{
344+
process.nextTick(()=>{
345+
assert.ok(warningRegEx.test(err));
346+
});
347+
}));
348+
testMe.complete('ele.',common.mustCall((err,data)=>{
349+
common.restoreStderr();
350+
assert.ifError(err);
351+
352+
constele=(type===Array) ?
353+
[] :
354+
(type===Buffer ?
355+
Buffer.alloc(0) :
356+
newtype(0));
357+
358+
data[0].forEach((key)=>{
359+
if(!key)return;
360+
assert.notStrictEqual(ele[key.substr(4)],undefined);
361+
});
362+
363+
// no `biu`
364+
assert.strictEqual(data.indexOf('ele.biu'),-1);
365+
}));
366+
});
367+
368+
// check Buffer.prototype.length not crashing.
369+
// Refs: https://github.com/nodejs/node/pull/11961
370+
putIn.run['.clear'];
371+
testMe.complete('Buffer.prototype.',common.mustCall());
372+
308373
consttestNonGlobal=repl.start({
309374
input: putIn,
310375
output: putIn,

0 commit comments

Comments
(0)