fix(engine): map ShadowRealm.evaluate declaration early errors to SyntaxError#817
Conversation
…taxError ShadowRealm.prototype.evaluate treats its source as a Script, so Script-level static early errors must surface as caller-realm SyntaxErrors (proposal-shadowrealm PerformShadowRealmEval §3.1.3 step 2). #814 added the ValidateEvalEarlyErrors pre-evaluation pass for reference/assignment early errors but left EvalDeclarationInstantiation conflicts as a tracked follow-up, where they surfaced as the step-15 caller-realm TypeError instead. Add CheckEvalScriptLexicalEarlyError (ECMA-262 §16.1.1) covering the two top-level declaration rules — a duplicate lexically-declared name, and a lexically-declared name that is also var-declared — and run it on the same seam before evaluation so both map to a caller-realm SyntaxError: new ShadowRealm().evaluate("let y; var y;") // SyntaxError (was TypeError) new ShadowRealm().evaluate("var y; let y;") // SyntaxError (was TypeError) new ShadowRealm().evaluate("let x; let x;") // SyntaxError (was TypeError) var is processed only in ShadowRealm child realms, so plain GocciaScript (which excludes var) is unaffected, and distinct lexical/var names still bind normally. built-ins/ShadowRealm test262 stays 64/64 and the full JS suite stays green in both interpreter and bytecode modes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughEval declaration instantiation now raises ChangesEval redeclaration handling
Sequence Diagram(s)Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
Suite TimingTest Runner (interpreted: 10,948 passed; bytecode: 10,948 passed)
MemoryGC rows aggregate the main thread plus all worker thread-local GCs. Test runner worker shutdown frees thread-local heaps in bulk; that shutdown reclamation is not counted as GC collections or collected objects.
Benchmarks (interpreted: 430; bytecode: 430)
MemoryGC rows aggregate the main thread plus all worker thread-local GCs. Benchmark runner performs explicit between-file collections, so collection and collected-object counts can be much higher than the test runner.
Measured on ubuntu-latest x64. |
Benchmark Results430 benchmarks Interpreted: 🟢 65 improved · 🔴 20 regressed · 345 unchanged · avg +2.5% arraybuffer.js — Interp: 🟢 4, 10 unch. · avg +3.1% · Bytecode: 🔴 1, 13 unch. · avg -2.4%
arrays.js — Interp: 🟢 4, 🔴 1, 14 unch. · avg +3.8% · Bytecode: 🟢 3, 16 unch. · avg +0.7%
async-await.js — Interp: 6 unch. · avg +1.3% · Bytecode: 🔴 1, 5 unch. · avg -7.5%
async-generators.js — Interp: 2 unch. · avg +2.0% · Bytecode: 🔴 2 · avg -29.0%
atomics.js — Interp: 🟢 3, 3 unch. · avg +3.1% · Bytecode: 6 unch. · avg +0.3%
base64.js — Interp: 🟢 1, 9 unch. · avg +2.2% · Bytecode: 🔴 2, 8 unch. · avg +0.1%
classes.js — Interp: 🔴 1, 30 unch. · avg +0.4% · Bytecode: 🟢 7, 🔴 1, 23 unch. · avg +1.4%
closures.js — Interp: 11 unch. · avg +2.6% · Bytecode: 🟢 2, 🔴 1, 8 unch. · avg +0.8%
collections.js — Interp: 🟢 1, 11 unch. · avg -0.5% · Bytecode: 🟢 2, 10 unch. · avg +3.3%
csv.js — Interp: 🟢 3, 10 unch. · avg +3.8% · Bytecode: 🟢 4, 🔴 3, 6 unch. · avg +0.5%
destructuring.js — Interp: 🟢 2, 20 unch. · avg +0.1% · Bytecode: 🟢 1, 21 unch. · avg +2.2%
fibonacci.js — Interp: 🟢 1, 7 unch. · avg +1.9% · Bytecode: 8 unch. · avg +0.6%
float16array.js — Interp: 🟢 7, 🔴 1, 24 unch. · avg +3.2% · Bytecode: 🟢 1, 🔴 6, 25 unch. · avg -1.7%
for-of.js — Interp: 🔴 1, 6 unch. · avg -0.2% · Bytecode: 7 unch. · avg -0.4%
generators.js — Interp: 🔴 1, 3 unch. · avg -5.4% · Bytecode: 🟢 1, 3 unch. · avg -2.4%
intl.js — Interp: 🔴 1, 5 unch. · avg -1.5% · Bytecode: 🟢 1, 5 unch. · avg +3.2%
iterators.js — Interp: 🟢 4, 🔴 1, 37 unch. · avg +2.8% · Bytecode: 🟢 5, 🔴 5, 32 unch. · avg +0.1%
json.js — Interp: 🟢 1, 🔴 1, 18 unch. · avg -0.1% · Bytecode: 🔴 7, 13 unch. · avg -3.6%
jsx.jsx — Interp: 🟢 3, 18 unch. · avg +1.7% · Bytecode: 🟢 5, 16 unch. · avg +3.7%
modules.js — Interp: 🟢 2, 7 unch. · avg +5.1% · Bytecode: 🟢 1, 8 unch. · avg +0.5%
numbers.js — Interp: 🔴 1, 10 unch. · avg +1.8% · Bytecode: 🟢 1, 🔴 1, 9 unch. · avg -1.5%
objects.js — Interp: 7 unch. · avg -1.6% · Bytecode: 🔴 3, 4 unch. · avg -4.4%
promises.js — Interp: 🔴 1, 11 unch. · avg -3.1% · Bytecode: 12 unch. · avg +1.5%
property-access.js — Interp: 🟢 1, 4 unch. · avg +1.7% · Bytecode: 🔴 1, 4 unch. · avg +1.4%
regexp.js — Interp: 🟢 2, 9 unch. · avg +1.1% · Bytecode: 🟢 1, 10 unch. · avg +0.5%
strings.js — Interp: 🔴 1, 18 unch. · avg +0.1% · Bytecode: 🔴 2, 17 unch. · avg -1.8%
temporal.js — Interp: 🟢 2, 4 unch. · avg +4.7% · Bytecode: 6 unch. · avg -0.9%
tsv.js — Interp: 9 unch. · avg +0.6% · Bytecode: 🟢 1, 🔴 2, 6 unch. · avg -1.8%
typed-arrays.js — Interp: 🟢 11, 🔴 4, 7 unch. · avg +12.8% · Bytecode: 🟢 2, 🔴 5, 15 unch. · avg -13.1%
uint8array-encoding.js — Interp: 🟢 6, 🔴 3, 9 unch. · avg -4.1% · Bytecode: 🟢 5, 🔴 2, 11 unch. · avg +3.5%
weak-collections.js — Interp: 🟢 7, 🔴 2, 6 unch. · avg +19.2% · Bytecode: 🔴 3, 12 unch. · avg -2.4%
Deterministic profile diffDeterministic profile diff: no significant changes. Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context. |
test262 Conformance
Areas closest to 100%
Per-test deltas (+1 / -0)Newly passing (1):
Steady-state failures are non-blocking; regressions vs the cached main baseline (lower total pass count, or any PASS → non-PASS transition) fail the conformance gate. Measured on ubuntu-latest x64, bytecode mode. Areas grouped by the first two test262 path components; minimum 25 attempted tests, areas already at 100% excluded. Δ vs main compares against the most recent cached |
…-2917b0 # Conflicts: # source/units/Goccia.Builtins.GlobalShadowRealm.pas
…to claude/agitated-elion-2917b0
Summary
ShadowRealm.prototype.evaluatetreats its source as a Script, so a top-level name that is both lexically- and var-declared (let y; var y;/var y; let y;) is a static Script early error (ECMA-262 §16.1.1) and must surface as a caller-realmSyntaxError— not theTypeErrorreserved for runtime abrupt completions (proposal-shadowrealm PerformShadowRealmEval §3.1.3 step 2 vs step 15). It was reportingTypeError. This is the declaration-conflict follow-up fix(engine): map ShadowRealm eval instantiation errors to SyntaxError #816 flagged as out of scope.PrepareEvalProgram) classifies an early error by catching a PascalTGocciaSyntaxError, mapping it to a caller-realmSyntaxError; anything else (incl.TGocciaThrowValue) becomes aTypeError. The lexical/var early-error checks inEvalDeclarationInstantiationraised viaThrowSyntaxError, which (throughRaiseNativeError) raises a throwableTGocciaThrowValue— so phase-1 classified them as runtime throws →TypeError. Duplicate-lexical (Scope.pas) and illegal control-flow (RejectEvalControlFlow, fix(engine): map ShadowRealm eval instantiation errors to SyntaxError #816) alreadyraise TGocciaSyntaxErrordirectly, which is why those were alreadySyntaxError.EvalDeclarationInstantiation's declaration early-error checksraise TGocciaSyntaxError(via a smallRaiseAlreadyDeclaredhelper), matchingRejectEvalControlFlowandScope.pas. fix(engine): map ShadowRealm eval instantiation errors to SyntaxError #816's phase-1 catch then routes them to a caller-realmSyntaxErrorautomatically — no ShadowRealm-boundary or parallel-check code needed.CanDeclareGlobal*TypeErrors are left untouched (they are genuine runtime errors).evalis unaffected: a PascalTGocciaSyntaxErrorfromEvalDeclarationInstantiationis still converted to a catchable JSSyntaxError(interpreterPascalExceptionToErrorObject; bytecode VMTGocciaSyntaxErrorunwind arm) — verified in both modes.evalwhose body has the conflict is a runtime abrupt completion inside the outer eval, so ShadowRealm still wraps it as aTypeError(step 15) — verified.varis processed only in ShadowRealm child realms; plain GocciaScript (which excludesvar) is unaffected, and distinct lexical/var names still bind normally.Testing
Details:
built-ins/ShadowRealm/**test262: 64/64, both interpreter and bytecode modes../build/GocciaTestRunner testsand--mode=bytecode).tests/built-ins/ShadowRealm/evaluate.js: the lexical/var collision (both orderings) and a distinct-name guard (duplicate-lexical is already covered by fix(engine): map ShadowRealm eval instantiation errors to SyntaxError #816's test)../format.pas --check: clean.