fix(codegen,runtime): #5437 — require-derived captured local drops to undefined (no decl-site snapshot) - #5568
Conversation
… drop to undefined when no decl-site snapshot exists
The W6 fix made a bare/member `new C(captures...)` fill the synthesized
__perry_cap_* params from the class's decl-site capture snapshot
(js_class_capture_value), discarding the new-site appended cap arg —
the snapshot being authoritative because the bundle's multi-level
capture chain can materialize a mis-boxed value into the appended arg.
But the snapshot only exists for classes that reach the
RegisterClassCaptures decl-site. An inline anonymous class
(`new class { m(){ return capturedLocal } }`) capturing a local whose
initializer derives from a require() result (`const h = require(s).x` /
`const {x:h} = require(s)`) has NO registered snapshot, so the snapshot
read returned undefined and dropped the (correct) appended cap value.
This was the next Next.js wall after W6: the render threw
`TypeError: Cannot read properties of undefined (reading 'getSpan')`
from the OTel-backed tracer's getActiveScopeSpan() — `trace` is
destructured from the require'd @opentelemetry/api module and captured
by the tracer class, so it read undefined inside the method.
Fix: js_class_capture_value_or(cid, slot, fallback) returns the snapshot
slot when a snapshot is registered for cid, else the appended cap arg
(fallback). Keeps W6 (snapshot wins when present) while restoring the
appended value for the snapshot-less case. Codegen passes the appended
cap arg as the fallback at the bare/member new site.
Regression test issue_5437_require_derived_capture covers
`require().prop` and destructured `{prop}=require()` captured by class
methods (member, function-scoped, and whole-require / plain-object
controls).
|
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 ignored due to path filters (1)
📒 Files selected for processing (7)
📝 WalkthroughWalkthroughFixes a bug where Changesrequire-derived inline-class capture fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 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
Follow-up to the #5437 W6
CaptureFillfix (v0.5.1202). Fixes the next Next.js wall (trace.getSpanon undefined) and a real over-aggressiveness in that fix.Root
The W6 fix made
inline_constructor_param_values_with_class(lower_call/new.rs) ALWAYS read a cap param from the decl-site snapshotjs_class_capture_value(cid, slot)when the class is inctx.class_ids. But a snapshot only exists for classes reaching aRegisterClassCapturesdecl-site. An inline anonymous class capturing arequire()-derived local (const h = require(s).x) gets acidwith no registered snapshot →js_class_capture_valuereturnsTAG_UNDEFINED→ captured value readsundefinedin methods. Decisive probe:CAPVAL cid=5 idx=0 snapshot_exists=false result_bits=0x7ffc...0001. In the bundle this was the OTel tracer capturing arequire("@opentelemetry/api")-derivedtrace.Minimal repro (
w6-repro/otel/)FAILS = class-capture of a require()-derived local; PASSES = capturing the whole require result / a plain-obj property / capture by an arrow closure.
Fix (codegen + runtime, 3 files +test)
New FFI
js_class_capture_value_or(cid, slot, fallback)(object/class_constructors.rs, +#[used]keepalive, declared inruntime_decls/strings.rs): returns the snapshot slot when registered (W6: authoritative), elsefallback.CaptureFillarm passes the new-site appended cap arg as fallback. Keeps W6 (snapshot wins when present); restores the appended value for the snapshot-less inline-class case.Validation (rigorous 4-point gate)
curl(no --retry), server stays alive.reading 'getSpan'throws 1→0;undefined is not a constructor= 0 (W6 not regressed; W6 regression test passes).IncrementalCache nh.handleGetreading.geton undefined — next target). HTTP still 500 (node oracle 200/10784).cargo testperry-hir/codegen/runtime green (thenative_proof_regressionsparallel failures are the documented env-lock flake → 51/51 with--test-threads=1); both Next.js standalone: deferred-require binding captured by-value (as unresolved thunk) by a class constructor → 'undefined is not a constructor' #5437 regression tests pass.Refs #5437.
Summary by CodeRabbit
Bug Fixes
require()calls within class methods could incorrectly resolve toundefined.Tests