Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repl: refactor to avoid unsafe array iteration #36444

Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b81f448
lib: refactor to remove for-let-of loop in repl.js
RaisinTen Dec 8, 2020
f5d6c33
Apply suggestion from code review
RaisinTen Dec 10, 2020
b6b3c5b
lib: require ArrayPrototypeForEach in repl.js
RaisinTen Dec 10, 2020
0a167a1
lib: fix syntax error repl.js
RaisinTen Dec 10, 2020
3a9491e
lib: replace another loop repl.js
RaisinTen Dec 10, 2020
846ac65
lib: fix lint error repl.js
RaisinTen Dec 10, 2020
9367f9b
Apply suggestions from code review
RaisinTen Dec 11, 2020
61cb3b7
lib: replace all for loops with ArrayPrototypeForEach in repl.js
RaisinTen Dec 11, 2020
10c1759
lib: fix syntax error repl.js
RaisinTen Dec 11, 2020
669f93e
lib: fix lint error
RaisinTen Dec 11, 2020
dc678b0
lib: replace ArrayPrototypeSort with ArrayPrototypeForEach in repl.js
RaisinTen Dec 16, 2020
ddfbfc4
test: add unsafe array iteration test to repl
RaisinTen Dec 18, 2020
77a6be5
test: add notStrictEqual check instead for repl.js
RaisinTen Dec 18, 2020
6c19991
Update test/parallel/test-repl-unsafe-array-iteration.js
RaisinTen Dec 18, 2020
ccc860a
test: add test using child_process instead
RaisinTen Dec 19, 2020
81776d6
test: remove error code check
RaisinTen Dec 19, 2020
a45f1ed
Update test/parallel/test-repl-unsafe-array-iteration.js
RaisinTen Dec 19, 2020
c61b0b9
Update test/parallel/test-repl-unsafe-array-iteration.js
RaisinTen Dec 19, 2020
71350bd
Update test/parallel/test-repl-unsafe-array-iteration.js
RaisinTen Dec 20, 2020
1ec7036
Update test/parallel/test-repl-unsafe-array-iteration.js
RaisinTen Dec 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 18 additions & 20 deletions lib/repl.js
Expand Up @@ -46,6 +46,7 @@ const {
ArrayPrototypeConcat,
ArrayPrototypeFilter,
ArrayPrototypeFindIndex,
ArrayPrototypeForEach,
ArrayPrototypeIncludes,
ArrayPrototypeJoin,
ArrayPrototypeMap,
Expand Down Expand Up @@ -662,7 +663,7 @@ function REPLServer(prompt,
let matched = false;

errStack = '';
for (const line of lines) {
ArrayPrototypeForEach(lines, (line) => {
if (!matched &&
RegExpPrototypeTest(/^\[?([A-Z][a-z0-9_]*)*Error/, line)) {
errStack += writer.options.breakLength >= line.length ?
Expand All @@ -672,7 +673,7 @@ function REPLServer(prompt,
} else {
errStack += line;
}
}
});
if (!matched) {
const ln = lines.length === 1 ? ' ' : ':\n';
errStack = `Uncaught${ln}${errStack}`;
Expand Down Expand Up @@ -753,9 +754,7 @@ function REPLServer(prompt,
const prioritizedSigintQueue = new SafeSet();
self.on('SIGINT', function onSigInt() {
if (prioritizedSigintQueue.size > 0) {
for (const task of prioritizedSigintQueue) {
task();
}
ArrayPrototypeForEach(prioritizedSigintQueue, (task) => task());
return;
}

Expand Down Expand Up @@ -1009,13 +1008,13 @@ REPLServer.prototype.createContext = function() {
}, () => {
context = vm.createContext();
});
for (const name of ObjectGetOwnPropertyNames(global)) {
ArrayPrototypeForEach(ObjectGetOwnPropertyNames(global), (name) => {
// Only set properties that do not already exist as a global builtin.
if (!globalBuiltins.has(name)) {
ObjectDefineProperty(context, name,
ObjectGetOwnPropertyDescriptor(global, name));
}
}
});
context.global = context;
const _console = new Console(this.output);
ObjectDefineProperty(context, 'console', {
Expand Down Expand Up @@ -1229,7 +1228,7 @@ function complete(line, callback) {
paths = ArrayPrototypeConcat(module.paths, CJSModule.globalPaths);
}

for (let dir of paths) {
ArrayPrototypeForEach(paths, (dir) => {
dir = path.resolve(dir, subdir);
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
for (const dirent of dirents) {
Expand Down Expand Up @@ -1257,7 +1256,7 @@ function complete(line, callback) {
}
}
}
}
});
if (group.length) {
ArrayPrototypePush(completionGroups, group);
}
Expand Down Expand Up @@ -1349,11 +1348,11 @@ function complete(line, callback) {

if (memberGroups.length) {
expr += chaining;
for (const group of memberGroups) {
ArrayPrototypeForEach(memberGroups, (group) => {
ArrayPrototypePush(completionGroups,
ArrayPrototypeMap(group,
(member) => `${expr}${member}`));
}
});
if (filter) {
filter = `${expr}${filter}`;
}
Expand All @@ -1372,15 +1371,15 @@ function complete(line, callback) {
// Filter, sort (within each group), uniq and merge the completion groups.
if (completionGroups.length && filter) {
const newCompletionGroups = [];
for (const group of completionGroups) {
ArrayPrototypeForEach(completionGroups, (group) => {
const filteredGroup = ArrayPrototypeFilter(
group,
(str) => StringPrototypeStartsWith(str, filter)
);
if (filteredGroup.length) {
ArrayPrototypePush(newCompletionGroups, filteredGroup);
}
}
});
completionGroups = newCompletionGroups;
}

Expand All @@ -1389,20 +1388,20 @@ function complete(line, callback) {
const uniqueSet = new SafeSet(['']);
// Completion group 0 is the "closest" (least far up the inheritance
// chain) so we put its completions last: to be closest in the REPL.
for (const group of completionGroups) {
ArrayPrototypeForEach(completionGroups, (group) => {
ArrayPrototypeSort(group, (a, b) => (b > a ? 1 : -1));
const setSize = uniqueSet.size;
for (const entry of group) {
ArrayPrototypeForEach(group, (entry) => {
if (!uniqueSet.has(entry)) {
ArrayPrototypeUnshift(completions, entry);
uniqueSet.add(entry);
}
}
});
// Add a separator between groups.
if (uniqueSet.size !== setSize) {
ArrayPrototypeUnshift(completions, '');
}
}
});

// Remove obsolete group entry, if present.
if (completions[0] === '') {
Expand Down Expand Up @@ -1566,14 +1565,13 @@ function defineDefaultCommands(repl) {
const longestNameLength = MathMax(
...ArrayPrototypeMap(names, (name) => name.length)
);
for (let n = 0; n < names.length; n++) {
const name = names[n];
ArrayPrototypeForEach(names, (name) => {
const cmd = this.commands[name];
const spaces =
StringPrototypeRepeat(' ', longestNameLength - name.length + 3);
const line = `.${name}${cmd.help ? spaces + cmd.help : ''}\n`;
this.output.write(line);
}
});
this.output.write('\nPress Ctrl+C to abort current expression, ' +
'Ctrl+D to exit the REPL\n');
this.displayPrompt();
Expand Down
23 changes: 23 additions & 0 deletions test/parallel/test-repl-unsafe-array-iteration.js
@@ -0,0 +1,23 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { spawn } = require('child_process');

function run(input, expectation) {
const node = spawn(process.argv0);

node.stderr.on('data', common.mustCall((data) => {
assert.ok(data.includes(expectation));
}));
RaisinTen marked this conversation as resolved.
Show resolved Hide resolved

node.stdin.write(input);
node.stdin.end();
}

run('delete Array.prototype[Symbol.iterator];',
'TypeError: Found non-callable @@iterator');

run('const ArrayIteratorPrototype =' +
'Object.getPrototypeOf(Array.prototype[Symbol.iterator]());' +
'delete ArrayIteratorPrototype.next;',
'TypeError: fn is not a function');