vm: lexical declaration no longer throws SyntaxError over a global function declaration in vm context (regression in v26.6.0)
維護者通常 1 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 新手友好度
- 55/100
- Issue 類型
- 缺陷
- 描述清晰度
- 基本清楚
- 活躍度
- 冷清
- 技術堆疊
- javascript
- 領域
- backend
研究方向
從 vm context 實作以及報告中提到的 test262 language/global-code/script-decl-lex-var.js 案例開始;在 v26.6.0 上執行提供的重現,並與 v26.5.1 比較。追蹤全域宣告處理以及 restricted-global/interceptor 查找。完成標準是:function 後接 let、const 或 class 的衝突會引發 SyntaxError,同時現有的 var 和反向順序案例仍然正確。
由索引模型根據 Issue 內容生成。
描述
Version
v26.6.0
Platform
Darwin 24.6.0 Darwin Kernel Version 24.6.0: xnu-11417.140.69.705.2~1/RELEASE_ARM64_T6041 arm64 (macOS, Apple Silicon)
Subsystem
No response
What steps will reproduce the bug?
A let declaration that collides with a name introduced by a function declaration in a previous script in the same vm context is no longer rejected with a SyntaxError:
const vm = require('vm');
const context = vm.createContext({});
// A function declaration in global (script) code appends its name to the
// global environment record's [[VarNames]] (CreateGlobalFunctionBinding).
vm.runInContext('function foo() { return 42; }', context);
// Per ECMA-262 GlobalDeclarationInstantiation, HasVarDeclaration('foo') is
// then true, so this lexical declaration must throw.
try {
vm.runInContext('let foo;', context);
console.log('NO ERROR (let foo; was accepted)');
} catch (e) {
console.log('THREW:', e.constructor.name, '-', e.message);
}
// Fallout: the accepted `let` shadows the still-present function.
console.log('typeof foo ->', vm.runInContext('typeof foo', context));
console.log('this.foo() ->', vm.runInContext('this.foo()', context));
How often does it reproduce? Is there a required condition?
100% reproducible.
What is the expected behavior? Why is that the expected behavior?
let foo; should throw a SyntaxError. CreateGlobalFunctionBinding appends the name to the global environment record's [[VarNames]], so on the next script HasVarDeclaration('foo') is true and GlobalDeclarationInstantiation must throw.
v26.5.1 behaves correctly:
THREW: SyntaxError - Identifier 'foo' has already been declared
typeof foo -> function
this.foo() -> 42
What do you see instead?
On v26.6.0 the declaration is silently accepted, and the new lexical binding shadows the function — calling it by name now fails while it is still reachable as a property of the global object:
NO ERROR (let foo; was accepted)
typeof foo -> undefined
this.foo() -> 42
(foo() at that point throws TypeError: foo is not a function.)
Additional information
Bisects to v26.6.0; v26.5.1 and v26.3.0 are fine. Both ship the same V8 (14.6.202.34), so this looks like it comes from #64202 (vm: enable interception on global restricted properties, 7be42b64e1) — the fix for #63715, which landed in v26.6.0. I confirmed #63715's own repro is indeed fixed on v26.6.0, so this appears to be the opposite direction of the same interceptor path.
const and class are affected identically. The reverse order (function foo(){} after let foo;) and all var collisions still behave correctly:
| previous script | next script | v26.5.1 | v26.6.0 |
|---|---|---|---|
function foo(){} |
let foo; |
SyntaxError | accepted |
function foo(){} |
const foo = 1; |
SyntaxError | accepted |
function foo(){} |
class foo {} |
SyntaxError | accepted |
var foo; |
let foo; |
SyntaxError | SyntaxError |
let foo; |
function foo(){} |
SyntaxError | SyntaxError |
A possibly useful detail for localizing it: the two declaration forms land in different places in a contextified global. After var v; the property exists on the context global as non-configurable and is absent from the sandbox object; after function f(){} the property is on the sandbox object and reads back as configurable: true. Since the restricted-global lookup now consults the interceptor, it sees that configurable sandbox property and reports no collision.
Relatedly, inserting a failed delete this.foo between the two scripts makes v26.6.0 throw again:
vm.runInContext('function foo(){}', context);
vm.runInContext('delete this.foo', context); // returns false
vm.runInContext('let foo;', context); // SyntaxError again
Found via the test262 language/global-code/script-decl-lex-var.js case, which fails on v26.6.0 and passes on v26.5.1.
- 主要語言
- JavaScript
- 星號
- 122k
- 分支
- 37.4k
- 平均合併
- 4 天 12 小時
- 30 天內合併 PR
- 296
環境準備
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
nodejs/node 的其他 Issue
-
flaky-test test v26.x
難度 2/5 1-3 小時 新手友好度 68/100
維護者通常 1 天內回覆
-
doc
難度 1/5 1 小時以內 新手友好度 90/100
維護者通常 1 天內回覆
-
doc
難度 2/5 1-3 小時 新手友好度 65/100
維護者通常 1 天內回覆
-
build
難度 1/5 1 小時以內 新手友好度 88/100
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 84/100
nodejs/node#65994 · 2 則留言 · 2 個 reaction ·
維護者通常 1 天內回覆
相似的 Issue
-
factory-active factory-automatic harness/codex task-bug-reproduction-success task-identify-harness-labels-done task-identify-issue-type-done
難度 2/5 1-3 小時 新手友好度 85/100
維護者通常 1 天內回覆
-
ux
難度 1/5 1 小時以內 新手友好度 90/100
rr-djk/rr-djuikoo.com#53 ·
維護者通常 1 天內回覆
-
new spec review
難度 2/5 1-3 小時 新手友好度 72/100
w3c/browser-specs#2666 · 1 則留言 ·
維護者通常 3 天內回覆
-
難度 2/5 1-3 小時 新手友好度 76/100
thim81/openapi-format#238 ·
-
難度 2/5 1-3 小時 新手友好度 82/100
decentespresso/dye2#13 ·