You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
console.table / console.dir hold operand 0 unrooted across operand 1's lowering (lower_call/console_promise.rs) #7649
Found while auditing lower_call/ for #7615 slice 5 (#7648). Filed rather than fixed: both belong with console_promise.rs's own Layer 1 migration, which needs a combinator that does not exist yet (see below), and fixing them ahead of it would leave the module half-migrated — the state that let #7461's expr/url_main.rs hide from #7461 to #7617.
The two windows
Both are crates/perry-codegen/src/lower_call/console_promise.rs, both #7280 taxonomy (c) — operand-to-operand.
console.table(data, properties) (~line 300):
let v = lower_expr(ctx,&args[0])?;if args.len() == 2{let props = lower_expr(ctx,&args[1])?;// arbitrary user code
ctx.block().call_void("js_console_table_with_properties",&[(DOUBLE,&v),(DOUBLE,&props)],// `v` is a bare register);}
let v = lower_expr(ctx,&args[0])?;let opts = if args.len() >= 2{lower_expr(ctx,&args[1])? }else{ undefined };
ctx.block().call_void("js_console_dir_with_options",&[(DOUBLE,&v),(DOUBLE,&opts)]);
console.table(buildRows(), buildCols()) / console.dir(makeObj(), makeOpts()) hold operand 0 across a real user call with back-edge polls. An evacuating minor relocates it and the formatter reads from-space.
Each is a one-line fix with crate::rooting::with_operands_rooted, and costs nothing when the second operand provably cannot collect (OperandProtection::Reuse), which is the console.dir(x) / console.table(rows) majority.
A separate, non-GC bug in the same console.dir arm
console.dir lowers arguments 2..n after the call that produces its output:
ctx.block().call_void("js_console_dir_with_options",&[...]);for a in args.iter().skip(2){let _ = lower_expr(ctx, a)?;// side effects sequenced AFTER the print}
Node evaluates every argument before invoking console.dir, so console.dir(x, y, sideEffect()) interleaves output differently. Not verified against the oracle yet — noting it here so whoever fixes the rooting window does not restructure the arm without noticing it.
Why not now
console_promise.rs cannot be migrated end to end with today's API. lower_dynamic_closure_call re-reads its operand group at two points — receiver and callee below the arguments, then the arguments again below the allocating js_closure_unbox_callee_checked_rebind — and every with_operands_rooted* form has exactly one re-read point. The same gap blocks new.rs (three re-read points under a scope marker spanning ~20 return paths) and is why mod.rs::lower_rest_call_args_rooted still names the raw API: the variadic shape needs per-element re-reads between allocating pushes.
So the useful ordering is: add the multi-stage / variadic combinator to crate::rooting, then migrate console_promise.rs, and close these two as part of it.
Activity
refactor(codegen): Layer 1 rooting migration slice 6 — the multi-poin…
refactor(codegen): Layer 1 rooting migration slice 6 — the multi-poin…