Skip to content

Commit 1a44265

Browse files
islandryuaduh95
authored andcommitted
repl: handle errors from getters during completion
PR-URL: #59044 Reviewed-By: Dario Piotrowicz <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 71bb6cd commit 1a44265

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

lib/repl.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,7 +1837,7 @@ function includesProxiesOrGetters(expr, exprStr, evalFn, ctx, callback) {
18371837
if (astProp.type === 'Literal') {
18381838
// We have something like `obj['foo'].x` where `x` is the literal
18391839

1840-
if (isProxy(obj[astProp.value])) {
1840+
if (safeIsProxyAccess(obj, astProp.value)) {
18411841
return cb(true);
18421842
}
18431843

@@ -1855,7 +1855,7 @@ function includesProxiesOrGetters(expr, exprStr, evalFn, ctx, callback) {
18551855
) {
18561856
// We have something like `obj.foo.x` where `foo` is the identifier
18571857

1858-
if (isProxy(obj[astProp.name])) {
1858+
if (safeIsProxyAccess(obj, astProp.name)) {
18591859
return cb(true);
18601860
}
18611861

@@ -1882,7 +1882,7 @@ function includesProxiesOrGetters(expr, exprStr, evalFn, ctx, callback) {
18821882
}
18831883

18841884
if (typeof evaledProp === 'string') {
1885-
if (isProxy(obj[evaledProp])) {
1885+
if (safeIsProxyAccess(obj, evaledProp)) {
18861886
return cb(true);
18871887
}
18881888

@@ -1899,6 +1899,15 @@ function includesProxiesOrGetters(expr, exprStr, evalFn, ctx, callback) {
18991899
);
19001900
}
19011901

1902+
function safeIsProxyAccess(obj, prop) {
1903+
// Accessing `prop` may trigger a getter that throws, so we use try-catch to guard against it
1904+
try {
1905+
return isProxy(obj[prop]);
1906+
} catch {
1907+
return false;
1908+
}
1909+
}
1910+
19021911
return callback(false);
19031912
}
19041913

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const repl = require('repl');
5+
const ArrayStream = require('../common/arraystream');
6+
const assert = require('assert');
7+
8+
(async function() {
9+
await runTest();
10+
})().then(common.mustCall());
11+
12+
async function runTest() {
13+
const input = new ArrayStream();
14+
const output = new ArrayStream();
15+
16+
const replServer = repl.start({
17+
prompt: '',
18+
input,
19+
output: output,
20+
allowBlockingCompletions: true,
21+
terminal: true
22+
});
23+
24+
replServer._domain.on('error', (e) => {
25+
assert.fail(`Error in REPL domain: ${e}`);
26+
});
27+
28+
await new Promise((resolve, reject) => {
29+
replServer.eval(`
30+
const getNameText = () => "name";
31+
const foo = { get name() { throw new Error(); } };
32+
`, replServer.context, '', (err) => {
33+
if (err) {
34+
reject(err);
35+
} else {
36+
resolve();
37+
}
38+
});
39+
});
40+
41+
['foo.name.', 'foo["name"].', 'foo[getNameText()].'].forEach((test) => {
42+
replServer.complete(
43+
test,
44+
common.mustCall((error, data) => {
45+
assert.strictEqual(error, null);
46+
assert.strictEqual(data.length, 2);
47+
assert.strictEqual(data[1], test);
48+
})
49+
);
50+
});
51+
}

0 commit comments

Comments
 (0)