ReviewOS

zig-utils/zig-js

VM: reject non-simple parameter chunks from direct native calls

#737
Closed chris opened this 9 days ago ·
9 days ago

Parents: #465, #498 Related: #728, #729, #735, #736

Problem

tryRunNativeDirectCall constructs a bounded native slot array by copying argument i into slot i. That identity contract is valid only for simple formal lists. Named rest chunks from #735 already require VM activation entry to allocate/bind a rest Array, and #736 will give destructuring formals hidden raw-input slots plus a bytecode BindingInitialization prologue. The direct native path does not currently reject Chunk.has_non_simple_parameters, so a hot eligible artifact could bypass those entry semantics.

Scope

  • Fail closed before native direct-call selection for every chunk with non-simple parameter metadata.
  • Preserve normal VM execution and native tiering inside the function body after exact activation entry; only the activation-eliding direct leaf path is refused.
  • Add a deterministic hot-call witness proving named rest results remain exact and the direct-native hit counter does not advance.
  • Keep the guard metadata-only, O(1), and shared-chunk/no-GIL safe.

No-workaround rules

  • Do not disable JIT globally, suppress tiering, rewrite the function, guess slot identity, or claim non-simple native entry support.
  • No stacks, queues, jobs, Promise, microtask, Worker lifecycle, or Map/Set iterator changes.

Acceptance

  • A focused normal/TSan test heats a named-rest numeric leaf and proves exact results with zero direct-native hits.
  • Existing simple-formal direct-native coverage still records hits.
  • Full relevant units and exact Test262 accounting retain zero regressions.
  • Conventional commit lands directly on main and links #465, #498, #735, and #736.
9 days ago

Implemented on main in e71ce6cb.

The direct native path now checks immutable Chunk.has_non_simple_parameters before artifact selection. The focused witness first proves a ready simple artifact executes and increments the direct-native hit counter, then applies non-simple entry metadata to that same ready artifact and proves it is refused with no hit. This makes the test independent of the real rest body's current incidental baseline eligibility. The real named-rest function remains exact through normal VM activation before and after the refusal.

Evidence:

  • focused guard/rest test: Debug pass, 0 failures/leaks;
  • focused guard/rest test: TSan pass, 0 failures/leaks;
  • existing simple-formal numeric call-loop quickening test: pass and direct-native hits preserved;
  • exact relevant Test262: 2,319 → 2,319, 0 failures, 0 gains, 0 regressions; all nine parent/candidate transcripts byte-identical.

#736 can now extend non-simple metadata without risking activation-eliding native entry.

9 days ago

Follow-up hardening landed with #736 in e2ba5ac3: the same immutable non-simple guard now rejects activation-eliding quick numeric leaf and recurrence kernels as well as direct native entry. Ordinary in-frame JIT tiering remains available after the BindingInitialization prologue.

9 days ago

The #737 activation-elision guard now protects another non-simple surface: #740 landed feat(compiler): lower invocation defaults (#740) with bare this, new.target, and own arguments defaults on the ordered VM entry path.

Sign in to comment on this issue.