mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 18:25:13 +08:00
Signed-off-by: Aviv Keller <me@aviv.sh> PR-URL: https://github.com/nodejs/node/pull/64034 Fixes: https://github.com/nodejs/node/issues/64523 Closes: https://github.com/nodejs/node/pull/64534 Fixes: https://github.com/nodejs/node/issues/39387 Closes: https://github.com/nodejs/node/pull/39392 Fixes: https://github.com/nodejs/node/issues/61390 Fixes: https://github.com/nodejs/node/issues/63126 Fixes: https://github.com/nodejs/node/issues/38503 Fixes: https://github.com/nodejs/node/issues/37445 Fixes: https://github.com/nodejs/node/issues/38145 Fixes: https://github.com/nodejs/node/issues/33369 Fixes: https://github.com/nodejs/node/issues/48131 Fixes: https://github.com/nodejs/node/issues/8309 Fixes: https://github.com/nodejs/node/issues/39689 Fixes: https://github.com/nodejs/node/issues/18931 Reviewed-By: Dario Piotrowicz <dario.piotrowicz@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
247 lines
7.6 KiB
JavaScript
247 lines
7.6 KiB
JavaScript
import '../common/index.mjs';
|
|
import assert from 'node:assert';
|
|
import repl from 'node:repl';
|
|
import { startNewREPLServer } from '../common/repl.js';
|
|
|
|
const testingReplPrompt = '_REPL_TESTING_PROMPT_>';
|
|
|
|
// Feed a block of source into the REPL one line at a time, awaiting each line
|
|
// so the asynchronous evaluator settles before the next line is written.
|
|
// Blank/whitespace-only and comment-only lines produce no evaluation output
|
|
// and are skipped (the previous synchronous REPL coalesced the whole block in
|
|
// a single write, so a trailing comment line evaluated to `undefined`; feeding
|
|
// line by line we simply omit it to keep output deterministic).
|
|
async function feed(run, block) {
|
|
for (const line of block.split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (trimmed === '' || trimmed.startsWith('//')) continue;
|
|
await run(`${line}\n`);
|
|
}
|
|
}
|
|
|
|
await testSloppyMode();
|
|
await testStrictMode();
|
|
await testResetContext();
|
|
await testResetContextGlobal();
|
|
await testError();
|
|
|
|
async function testSloppyMode() {
|
|
const { replServer, output, run } = startNewREPLServer({
|
|
prompt: testingReplPrompt,
|
|
mode: repl.REPL_MODE_SLOPPY,
|
|
});
|
|
|
|
// Cannot use `let` in sloppy mode
|
|
await feed(run, `_; // initial value undefined
|
|
var x = 10; // evaluates to undefined
|
|
_; // still undefined
|
|
y = 10; // evaluates to 10
|
|
_; // 10 from last eval
|
|
_ = 20; // explicitly set to 20
|
|
_; // 20 from user input
|
|
_ = 30; // make sure we can set it twice and no prompt
|
|
_; // 30 from user input
|
|
y = 40; // make sure eval doesn't change _
|
|
_; // remains 30 from user input
|
|
`);
|
|
|
|
assertOutput(output, [
|
|
'undefined',
|
|
'undefined',
|
|
'undefined',
|
|
'10',
|
|
'10',
|
|
'Expression assignment to _ now disabled.',
|
|
'20',
|
|
'20',
|
|
'30',
|
|
'30',
|
|
'40',
|
|
'30',
|
|
]);
|
|
|
|
replServer.close();
|
|
}
|
|
|
|
async function testStrictMode() {
|
|
const { replServer, output, run } = startNewREPLServer({
|
|
prompt: testingReplPrompt,
|
|
mode: repl.REPL_MODE_STRICT,
|
|
});
|
|
|
|
await feed(run, `_; // initial value undefined
|
|
var x = 10; // evaluates to undefined
|
|
_; // still undefined
|
|
let _ = 20; // use 'let' only in strict mode - evals to undefined
|
|
_; // 20 from user input
|
|
_ = 30; // make sure we can set it twice and no prompt
|
|
_; // 30 from user input
|
|
var y = 40; // make sure eval doesn't change _
|
|
_; // remains 30 from user input
|
|
function f() { let _ = 50; } // undefined
|
|
f(); // undefined
|
|
_; // remains 30 from user input
|
|
`);
|
|
|
|
assertOutput(output, [
|
|
'undefined',
|
|
'undefined',
|
|
'undefined',
|
|
'undefined',
|
|
'20',
|
|
'30',
|
|
'30',
|
|
'undefined',
|
|
'30',
|
|
'undefined',
|
|
'undefined',
|
|
'30',
|
|
]);
|
|
|
|
replServer.close();
|
|
}
|
|
|
|
async function testResetContext() {
|
|
const { replServer, output, run } = startNewREPLServer({
|
|
prompt: testingReplPrompt,
|
|
});
|
|
|
|
await feed(run, `_ = 10; // explicitly set to 10
|
|
_; // 10 from user input
|
|
.clear // Clearing context...
|
|
_; // remains 10
|
|
x = 20; // but behavior reverts to last eval
|
|
_; // expect 20
|
|
`);
|
|
|
|
assertOutput(output, [
|
|
'Expression assignment to _ now disabled.',
|
|
'10',
|
|
'10',
|
|
'Clearing context...',
|
|
'10',
|
|
'20',
|
|
'20',
|
|
]);
|
|
|
|
replServer.close();
|
|
}
|
|
|
|
async function testResetContextGlobal() {
|
|
const { replServer, output, run } = startNewREPLServer({
|
|
prompt: testingReplPrompt,
|
|
useGlobal: true,
|
|
});
|
|
|
|
await feed(run, `_ = 10; // explicitly set to 10
|
|
_; // 10 from user input
|
|
.clear // No output because useGlobal is true
|
|
_; // remains 10
|
|
`);
|
|
|
|
assertOutput(output, [
|
|
'Expression assignment to _ now disabled.',
|
|
'10',
|
|
'10',
|
|
'10',
|
|
]);
|
|
|
|
replServer.close();
|
|
|
|
// Delete globals leaked by REPL when `useGlobal` is `true`
|
|
delete globalThis.module;
|
|
delete globalThis.require;
|
|
}
|
|
|
|
async function testError() {
|
|
const { replServer, output, run } = startNewREPLServer({
|
|
prompt: testingReplPrompt,
|
|
replMode: repl.REPL_MODE_STRICT,
|
|
preview: false,
|
|
});
|
|
|
|
await feed(run, `_error; // initial value undefined
|
|
throw new Error('foo'); // throws error
|
|
_error; // shows error
|
|
fs.readdirSync('/nonexistent?'); // throws error, sync
|
|
_error.code; // shows error code
|
|
_error.syscall; // shows error syscall
|
|
setImmediate(() => { throw new Error('baz'); }); undefined;
|
|
// throws error, async
|
|
`);
|
|
|
|
const lines = output.accumulator.trim().split('\n').filter(
|
|
(line) => !line.includes(testingReplPrompt) || line.includes('Uncaught Error')
|
|
);
|
|
const expectedLines = [
|
|
'undefined',
|
|
|
|
// The error, both from the original throw and the `_error` echo. The
|
|
// inspector-based evaluator returns the real Error object, so both the
|
|
// thrown report and the echo now include a stack trace rather than the
|
|
// compact `[Error: foo]` form produced by the previous vm-based REPL.
|
|
'Uncaught Error: foo',
|
|
/^ +at REPL\d+:/,
|
|
'Error: foo',
|
|
/^ +at REPL\d+:/,
|
|
|
|
// The sync error, with individual property echoes. The thrown report now
|
|
// includes stack frames before the property listing.
|
|
/^Uncaught Error: ENOENT: no such file or directory, scandir '.*nonexistent\?'/,
|
|
/Object\.readdirSync/,
|
|
/^ +at REPL\d+:.*\{$/,
|
|
/^ {2}errno: -(2|4058),$/,
|
|
" code: 'ENOENT',",
|
|
" syscall: 'scandir',",
|
|
/^ {2}path: '*'/,
|
|
'}',
|
|
"'ENOENT'",
|
|
"'scandir'",
|
|
|
|
// 'undefined' from the explicit silencer on the setImmediate line.
|
|
'undefined',
|
|
|
|
// The message from the asynchronously thrown error.
|
|
/Uncaught Error: baz/,
|
|
];
|
|
for (const line of lines) {
|
|
const expected = expectedLines.shift();
|
|
if (typeof expected === 'string')
|
|
assert.strictEqual(line, expected);
|
|
else
|
|
assert.match(line, expected);
|
|
}
|
|
assert.strictEqual(expectedLines.length, 0);
|
|
|
|
// Reset output, check that '_error' is the asynchronously caught error.
|
|
// Because input is now fed one line at a time and the accumulator is reset
|
|
// mid-stream, the first line's echo is no longer preceded by a prompt and so
|
|
// survives the prompt filter; ignore that leading echo. The thrown error also
|
|
// now reports a stack frame.
|
|
output.accumulator = '';
|
|
await feed(run, `_error.message // show the message
|
|
_error = 0; // disable auto-assignment
|
|
throw new Error('quux'); // new error
|
|
_error; // should not see the new error
|
|
`);
|
|
|
|
const errorLines = output.accumulator.trim().split('\n')
|
|
.filter((line) => !line.includes(testingReplPrompt) &&
|
|
!line.includes('_error.message') &&
|
|
!/^ +at REPL\d+:/.test(line));
|
|
assert.deepStrictEqual(errorLines, [
|
|
"'baz'",
|
|
'Expression assignment to _error now disabled.',
|
|
'0',
|
|
'Uncaught Error: quux',
|
|
'0',
|
|
]);
|
|
|
|
replServer.close();
|
|
}
|
|
|
|
function assertOutput(output, expected) {
|
|
const lines = output.accumulator.trim().split('\n').filter((line) => !line.includes(testingReplPrompt));
|
|
assert.deepStrictEqual(lines, expected);
|
|
}
|