fix(hir,runtime): Annex B B.3.3 hoisting precedence + legacy method aliases (#5346)#5394
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughThe PR implements two Annex B compliance fixes: in the HIR lowerer, hoisted ChangesAnnex B compliance: hoisted var seeding and prototype method aliasing
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
…liases (#5346) Three self-contained, non-eval clusters from the test262 `annexB` tail: 1. B.3.3 hoisting precedence (`global-code/*existing-fn-no-init`, 8 cases). When a sloppy block-nested `function f(){}` and a same-named *top-level* `function f(){}` coexist at program scope, F is already in declaredFunctionNames, so B.3.3 must NOT create a fresh `undefined` legacy var — the function declaration owns the entry binding. A non-reassigned top-level `function f` is otherwise called straight through `lookup_func` and never bound to the var slot, so the legacy var shadowed it as `undefined` and `f()` threw `TypeError: value is not a function` at entry. Seed the legacy-var slot with `FuncRef(func_id)` (and mark it function-valued) when a top-level function of that name exists; the block-level declaration still overwrites it (`existing-fn-update` stays green). `lower/lower_module_fn.rs`. 2. `String.prototype.trimLeft`/`trimRight` (8 cases). These Annex B legacy names must be the SAME function objects as `trimStart`/`trimEnd` (`trimLeft === trimStart`, `.name === "trimStart"`, real own data properties), not independent thunks. Alias the installed property value. 3. `Date.prototype.toGMTString` (1 case). Same legacy-alias requirement: `toGMTString === toUTCString`. Was installed as a second independent thunk. New `install_proto_method_alias` helper installs an alias as the same function object with the standard `{ writable, !enumerable, configurable }` descriptor. test262 `annexB` (node v26 differential, `--all-features`): 776/1026 (75.6%) -> 793/1026 (77.3%); +17 pass, zero regressions. Remaining non-eval tail is the regex categorical gap (Rust `regex` crate), `String.prototype.substr` edge cases, `escape`/`unescape` not-a-constructor (needs the literal-`new` codegen path to honor [[Construct]]), and B.3.4 var-in-catch — separate follow-ups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2354f8e to
cdca560
Compare
Summary
Three self-contained, non-eval clusters from the test262
annexBtail (#5346). Builds on the already-merged #5356 (B.3.3 skip cases). All changes fully flip tests green with zero regressions.annexB parity: 776/1026 (75.6%) → 793/1026 (77.3%), +17 pass.
1. B.3.3 hoisting precedence —
global-code/*existing-fn-no-init(8)When a sloppy block-nested
function f(){}and a same-named top-levelfunction f(){}coexist at program scope,Fis already in declaredFunctionNames, so B.3.3 must not create a freshundefinedlegacy var — the function declaration owns the entry binding.A non-reassigned top-level
function fis otherwise called straight throughlookup_funcand never bound to the var slot, so the legacy var shadowed it asundefinedandf()threwTypeError: value is not a functionat entry:Fix: seed the legacy-var slot with
FuncRef(func_id)(and mark it function-valued) when a same-named top-level function exists. The block-level declaration still overwrites it, soexisting-fn-updatestays green.lower/lower_module_fn.rs.2.
String.prototype.trimLeft/trimRight(8)These Annex B legacy names must be the same function objects as
trimStart/trimEnd—trimLeft === trimStart,.name === "trimStart", real own data properties — not independent thunks (they already worked as method calls, butString.prototype.trimLeftwasundefined). Alias the installed property value.3.
Date.prototype.toGMTString(1)Same legacy-alias requirement:
toGMTString === toUTCString. It was installed as a second independent thunk, failing the reference-equality check.A new
install_proto_method_aliashelper installs an alias as the same function object with the standard{ writable: true, enumerable: false, configurable: true }method descriptor.Validation
node v26exactly (trim aliases +.name,toGMTString === toUTCString,f()= outer at entry / inner after the block).Remaining annexB tail (separate follow-ups, deliberately out of scope)
regexcrate gap noted in CLAUDE.md.String.prototype.substredge cases (~6): length/start coercion + error ordering.escape/unescapenot-a-constructor (2): the per-closure non-constructable flag already makesisConstructorcorrect via the dynamic path, but the literalnew escape()codegen special-case bypassesjs_new_function_construct— needs a codegen change.catch-redeclared-*, 4): catch-param vs hoisted-var assignment resolution.eval(~184): AOT limitation, per the issue.🤖 Generated with Claude Code
Summary by CodeRabbit
Date.prototype.toGMTStringnow aliasestoUTCString).String.prototype.trimLeftandString.prototype.trimRightreference the same functions astrimStartandtrimEnd, respectively.functiondeclarations to maintain correct entry-time semantics.