codegen: derived-ctor this-TDZ on standalone constructor-symbol path (toward #5345)#5362
Conversation
…symbol path
A derived class constructor that never calls `super()` leaves `this`
uninitialized, so the implicit `return this` (GetThisBinding) must throw a
ReferenceError per ECMAScript. The inline `new` path in `lower_new` already
detects the static no-super case and emits
`js_throw_reference_error_this_before_super`, but the DEFAULT construction
path routes `new C(...)` through the shared `<class>_constructor` symbol
(`force_ctor_call`) — which lacked the check. So
`class A extends Array { constructor() {} }; new A()` constructed silently
instead of throwing.
Mirror the inline path's guard in `compile_method`'s standalone
constructor-symbol emission, reusing the exact same predicate combination
(`ctor_body_calls_super` / `ctor_body_closure_calls_super` /
`ctor_body_uses_this` / `ctor_body_has_value_return`) so closure-captured
`super()` without a direct `this` use still suppresses the throw, and a
value-bearing `return` still takes the return-override path. The four
predicates were `pub(super)` in `lower_call::new_helpers`; promote them to
`pub(crate)` and re-export so `codegen/method.rs` can share them.
test262 language/{statements,expressions}/class: pass 5046 -> 5070 (+24),
0 regressions, parity 94.0% -> 94.4%. Fixes the
subclass/builtin-objects/*/super-must-be-called cluster (Array, ArrayBuffer,
Boolean, DataView, Date, Error, Function, Map, NativeError/*, Number,
Promise, RegExp, Set, String, WeakMap, WeakSet),
Object/constructor-return-undefined-throws, and
class-definition-null-proto-this. Toward #5345.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
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 (3)
📝 WalkthroughWalkthroughFour constructor-body predicate helpers ( ChangesDerived Constructor TDZ Enforcement
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 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 |
What
A derived class constructor that never calls
super()leavesthisuninitialized, so the implicitreturn this(specGetThisBinding) must throw a ReferenceError. Perry's inlinenewpath (lower_new) already enforces this, but the default construction path routesnew C(...)through the shared<class>_constructorsymbol (force_ctor_call), which lacked the check. As a result:This mirrors the inline path's guard in
compile_method's standalone constructor-symbol emission, reusing the exact same predicate combination already battle-tested on the inline path:ctor_body_calls_super— a directsuper()suppresses the throwctor_body_closure_calls_super+ctor_body_uses_this— a closure-capturedsuper()suppresses, unless the body also dereferencesthisdirectlyctor_body_has_value_return— a value-bearingreturntakes the return-override path insteadThe four predicates were
pub(super)inlower_call::new_helpers; they're promoted topub(crate)and re-exported socodegen/method.rscan share them (no logic duplication).Results
Full
test262 language/{statements,expressions}/classsweep (8426 cases, differential vsnode --experimental-strip-types):+24 cases fixed, 0 regressions. Confirmed by diffing the failing-test sets before/after (not just counts), and by
perry-codegenunit tests (16 passed).Fixes:
subclass/builtin-objects/*/super-must-be-called(Array, ArrayBuffer, Boolean, DataView, Date, Error, Function, Map, NativeError/{Eval,Range,Reference,Syntax,Type,URI}Error, Number, Promise, RegExp, Set, String, WeakMap, WeakSet)subclass/builtin-objects/Object/constructor-return-undefined-throwssubclass/class-definition-null-proto-thisA spot-check of valid super-calling shapes (conditional super, super in
try, super after statements, no-own-ctor forwarding, multi-level subclass, return-override) constructs correctly and matches Node — no false-positive throws.This is a contained slice of the larger #5345 class tail; it does not close the tracker.
🤖 Generated with Claude Code
Summary by CodeRabbit
super()and emitting appropriate reference errors to prevent undefined behavior during runtime.