Commit Graph
2626 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 40c56b96ce Merge origin/main into opt/npm-small-fixes
Keep both the entry module's DEP0040 link warning and #1303's CommonJS
top-level this object in the module init header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:58:53 -07:00
Malte UblandClaude Opus 5.5 26b703a821 Keep React Flight's development-only stack parsing out of production builds
React Flight's production server never runs its V8 structured-stack parser
(ReactFlightStackConfigV8): every use is behind __DEV__. Builds compiled from
its sources keep that code behind `throw new Error('... should never be
called in production mode')` stubs, so scriptc lowered it, failed on
Error.prepareStackTrace, and a dead `.stack` read turned on stack-trace mode
(a frame push in every function) for the whole program.

- Statements after return/throw/break/continue in a function body are not
  lowered. Declarations keep their hoisting through the existing forward
  machinery (later function declarations, `var` reads undefined, `let`/
  `const` TDZ); class, enum, module, import/export and generic function
  declarations still lower. Functions referenced only from such code are
  unreached and dead-stripped.
- Stack-trace mode requires a stack read reachable through live code
  (observesStacks).
- Synchronous functions in executables push and pop their source frame
  inline instead of calling scr_stack_enter/leave.
- Functions that only perform operations which never create or throw an
  Error, and only call such functions, keep no frame (captureFreeFunctions):
  no capture can include them.

Error.prepareStackTrace and CallSite stay refused: only development Flight
runs them. Good code is byte-identical (zero-cost gate: bench and
self-hosted compiler IR, LLVM and executables).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:44:31 -07:00
Chris Tate 526415a91c Give matchAll rows and exec results their index, input and groups
Match results record their metadata lazily. The results of one exec or
matchAll call share a source (the subject and the regex), and each
result keeps its start index. index, input and a null-prototype groups
object are built when a result is converted to a dynamic value; .index
and .input on a match slice read the source directly.

matchAll rows are allocated inline, like exec results. A drain whose
rows are read only through their elements, .length, the .groups
projection and the companion index uses matchAllCaptures or
matchAllCapturesInto, whose rows carry no metadata.
2026-10-18 14:43:40 -05:00
Chris Tate 42591b1eed Read zlib options held in typed objects (#1316) 2026-10-18 14:24:54 -05:00
Malte UblandClaude Opus 5.5 700f59c1b2 Serve Temporal's absence, v8, stream/web, dns/promises, EvalError and graceful-fs
T10 and T12 of the zero-cost compatibility plan, plus the maintainer
decisions:

- Programs that name Temporal type-check against TS 7's temporal lib
  (added only for them) and lower to Node 24's answers: typeof is
  "undefined", globalThis.Temporal is undefined, other uses throw
  ReferenceError at the root read.
- node:v8 is importable; getHeapStatistics answers allocator-derived
  figures in Node's shape; other members fence with the by-design reason.
  vm's refusal says why.
- node:stream/web exports the web stream globals themselves.
- node:dns/promises and dns.promises: lookup and lookupService as
  promises, in the link-gated scr_dns_promises.c unit.
- eval and the Function constructor throw Node's EvalError under
  --disallow-code-generation-from-strings; the corpus harness runs the
  Node oracle with that flag for `// @disallow-code-generation`.
- graceful-fs is served as node:fs.

Every new lowering replaces a refusal, and the new runtime code links only
when used, so other programs compile to identical bytes (zero-cost gate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:11:05 -07:00
Chris Tate 240f576805 Add execution tiers to the IR and a report-only tier analysis (#1315) 2026-10-18 14:08:12 -05:00
Malte UblandClaude Opus 5.5 5746dc30c5 Expect the compile to refuse stream-shims and node24-file-url-conversion again
With the local-class refusal reported again, both npm fixtures fail to compile with SC1090 as they did before #1303; restore their build expectations. The deleted corpus program 6102 also leaves the measured cost table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:05:40 -07:00
Malte UblandClaude Opus 5.5 f649959609 Report refused local classes from speculative type queries again
The previous change dropped the diagnostics of a function-local class refused during a speculative whole-program type query, expecting the class's own declaration to report them. In JavaScript that declaration only defers them to a runtime fence, so two npm programs that the compile refused (stream-shims and node24-file-url-conversion, both SC1090) began to compile and diverged from Node at run time. The speculative query now leaves the refusal reported, as it was wherever such an analysis already caught the poison, and only stops the poison from escaping the compiler.

The JavaScript crash repro becomes a diagnostics fixture (local-class-type-query-js.js); the corpus program 6102 never crashed the base compiler and is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:05:25 -07:00
Malte Ubl a273999cb0 Merge branch 'opt/cjs-exports-value' into opt/cjs-exports-model 2026-10-18 12:05:24 -07:00
Malte UblandClaude Opus 5.5 8531a45509 Record baselines for the capture-buffer fence fixtures
The diagnostics snapshot for speculative-function-refusal.ts and the ts7 order-parity baselines for it and 6110-cjs-speculative-wrapper-fences.cjs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:04:28 -07:00
Chris Tate d9f0005ecb Represent unions of records as the record pointer itself (#1225) 2026-10-18 14:04:16 -05:00
Chris Tate a5548597e5 Read zlib options held in typed objects
A zlib options object that reaches the runtime as a typed reference (a
cast object literal such as `{ level: 0 } as zlib.ZlibOptions`, which the
checked-dynamic options slot holds by reference) was not a plain
checked-dynamic object, so the options reader found no keys: every option
was ignored and out-of-range values were not reported. The sync, callback
and promisified one-shots and the stream constructors compressed with the
defaults instead.

scr_zlib_options_parse and scr_zlib_stream_new now read a typed reference
through its materialized view, as the blob and fetch option readers do, so
the options apply and invalid ones throw Node's ERR_OUT_OF_RANGE.
2026-10-18 14:02:03 -05:00
Chris Tate 329ea4387e Convert a Date to local time once for its local getters and follow TZ changes (#1313) 2026-10-18 13:57:42 -05:00
Malte UblandClaude Opus 5.5 2ae1f72d0b Read self-capturing closures through selfRef instead of their box
`const visit = (node) => { ... visit(child) ... }` lowered to a closure
over the box of `visit`, and the box then held the closure: a two-object
reference cycle that only the cycle collector could free, along with
everything the closure captured. Nested function declarations already
read their own name through selfRef; arrows and function expressions
stored into a binding now do too, when the IR proves the binding holds
the closure itself whenever the closure can run.

resolveSelfCaptures runs with the other backend IR passes, before
copyStableCaptures. A capture qualifies when every creation site stores
the new closure, unadapted and with the binding's own type, into a boxed
local of the creating function (its only declaration's initializer, or
the only assignment after an empty declaration in the same statement
list), nothing else writes the binding in any body that shares its box,
and the closure body is synchronous, not a generator, and does not hand
the binding to a nested closure. The capture then leaves the body and
every creation site; its reads become selfRef. A creating binding that
no other closure captures loses its box, except dead-zone bindings.

Self-hosted compiler (native, compiling its CLI to IR): the cycle
collector frees 14.5M objects instead of 22.1M; closures that held a box
containing themselves drop from 3.5M to 54k. Of the runtime suite and the
cg-* cycle workloads, only published-graphs compiles differently
(-1.3%, 31 interleaved runs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:50:59 -07:00
Malte UblandClaude Opus 5.5 19c742834e Add an experimental young-in nursery for compiled classes and records
SCRIPTC_YOUNG_NURSERY=1 at compile time makes an executable allocate
traced, program-owned class instances and records young and link the
collector unit built with SCR_YIN (a runtime pack variant of
scr_cycle.c; libraries never get it). The emitted constructor lists the
object in a per-thread nursery and marks its header: generation
SCR_CYC_YOUNG and its nursery index under the buffered bit, so every
existing release path treats it as a buffered candidate (no call while it
stays alive; scr_cyc_on_dead takes it out of the nursery at its death).

A nursery collection (whenever 4096 young objects are live, at collection
points, before full sweeps and limited passes) counts each young object's
references from other young objects, marks from the objects whose count is
below their reference count (referenced from outside the nursery: stacks,
globals, older objects, runtime structures, FFI, fibers, coroutine
frames), frees what stays unmarked, and gives up the dead set's outgoing
edges after the teardowns. The counts are recomputed at every collection
instead of maintained by a store barrier, so soundness rests only on the
invariants trial deletion already needs. Survivors join trial deletion's
nursery and outside roots become candidates; trial deletion walks young
objects it reaches as nursery-level objects. Per-type admission statistics
send types that do not die in young cycles back to trial deletion for a
while. Publication takes young objects out of the nursery. The RC audit
build checks nursery membership and counts at every collection.

With the flag off, the compiler emits and links exactly what it did
before (249 programs and the self-hosted compiler compared
byte-identical). Measured with the flag on: cg-rings -38%, cg-pairs -27%,
the runtime suite geomean -0.3% (published-graphs +3.4%), the self-hosted
compiler neutral; shapes whose cycles go through arrays or closures
(phase 2) pay without gaining.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:48:50 -07:00
Malte UblandClaude Opus 5.5 b0ce48f168 Fence refused statements inside capture buffers instead of dropping them
A refused statement in a JavaScript function body was dropped without a diagnostic or runtime fence when the body lowered inside a capture buffer whose owner kept the result. tryLowerExpression lowers the receiver of `(function () { ... }).call(this)` or `.apply(...)` and a computed class base that way: the refused statement (for example `console.log(root === module.exports)`) was missing from the kept function, its diagnostic was handed back to the enclosing statement, and that statement, having lowered cleanly, filed it as an informational runtime-fence entry. The program printed nothing for that statement.

lowerStmts now emits the statement's runtime fence inside a buffer as well (Lowerer.capturedRuntimeFence, the same message deferToRuntimeFence would give), leaving the diagnostics with the buffer's owner. The three JavaScript class initializer sites that skipped their fence under a buffer (static field, instance field, static initializer block) do the same. An owner that discards the candidate discards the fence with it, so every program that compiled before keeps byte-identical IR unless it contained such a dropped statement: all 3006 corpus programs produce the same IR as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:47:04 -07:00
Malte UblandClaude Opus 5.5 8597a364f6 Merge origin/main into opt/npm-small-fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:45:47 -07:00
Chris Tate e5c45f8253 Merge main 2026-10-18 13:41:18 -05:00
Chris Tate f80eb5fc39 Give every arm of record unions that share arms a collector header
A shape union's arms all carry a collector header when one of them does.
The emitter applied that rule in one pass over the unions in module order,
so a header that a later union gave a shared arm did not reach the other
arms of an earlier union. With First = A | B and Second = B | C, where only
C is cyclic, B got a header through Second, First's trace helper then
called A's trace function, and A had none: the module failed LLVM
verification with "use of undefined value '@sc_rtrace_r0'".

The rule now repeats until no union adds a header. Corpus
5919-record-unions-shared-arms chains Third = D | A, First = A | B and
Second = B | C, so the requirement passes from Second through First to
Third.
2026-10-18 13:39:12 -05:00
Chris Tate fe7b1cd199 Convert a Date to local time once for its local getters and follow TZ changes
Each local-time getter (getFullYear, getMonth, getDate, getHours, ...,
getTimezoneOffset) called localtime_r and walked the calendar for its
own field. The thread now keeps its last local conversion, keyed by the
instant and a timezone generation, so reading several local fields of
one Date converts it once. The UTC walk and the second computation use
integer division instead of floor.

Writing or deleting process.env.TZ calls tzset and starts a new
generation, so later local-time reads use the new zone, as in Node.
Before, the zone the process started with stayed in effect.
2026-10-18 13:33:41 -05:00
Chris Tate c1dca25415 Merge main 2026-10-18 13:24:16 -05:00
Chris Tate 761252e173 Expect the run-time refusals of node24-file-url-conversion and stream-shims
Both fixtures declare a function-local class whose base has no static
lowering (`class FileURL extends URL {}`, `class Counter extends
Readable`). The SC1090 for it used to come from the whole-program type
queries, which collected the class outside any statement, so the build
failed. Those queries now keep a refused local class out of their
results and drop its diagnostics; the class declaration reports the
refusal when it lowers, and in a JavaScript source a refused statement
becomes a runtime fence. Both programs therefore compile and throw the
fence when they reach a construct without a lowering, like the other
fixtures listed with a run refusal.
2026-10-18 13:20:51 -05:00
Chris Tate 504473d0cb Merge main 2026-10-18 13:20:09 -05:00
Chris Tate ead270ca24 Merge main 2026-10-18 13:18:08 -05:00
Chris Tate 16da839c62 Merge main 2026-10-18 13:16:06 -05:00
Chris Tate 09b0dffebc Walk dead cycles once in the cycle collector (#1286) 2026-10-18 13:09:16 -05:00
Chris Tate 507b32b027 Merge main 2026-10-18 12:54:01 -05:00
Chris Tate 2a9a68e0d2 Merge main 2026-10-18 12:52:04 -05:00
Chris Tate 700c8f7e5e Merge main 2026-10-18 12:47:53 -05:00
Chris Tate 7cfb6b16d2 Report unhandled rejections between timers and between immediates
Node runs runNextTicks after each timer and each immediate callback, which
reports the unhandled rejections the callback left before the next one runs.
The event loop now leaves the timer phase when a callback leaves the
rejection ledger non-empty, the check phase waits for that report, and the
check phase reports the ledger after each immediate's checkpoint.

When several timers expired in the same turn, a later timer ran before an
earlier timer's rejection was reported. On macOS the 2 ms and 3 ms timers in
5809-process-exit-and-rejections.cjs expire together, so its uncaught
'thrown string' was printed before the TypeError rejection.

5996-rejections-between-macrotasks.cjs makes five timers expire in the same
turn and checks the order across three immediates.
2026-10-18 12:47:01 -05:00
Chris Tate e9a90e000a Merge main 2026-10-18 12:45:51 -05:00
Chris Tate 4b8b5e3b81 Revert "Read fields as undefined before their assignment" (#1310) 2026-10-18 12:38:47 -05:00
Chris Tate debbd359d7 Merge main 2026-10-18 12:30:17 -05:00
Chris Tate 29f6d8e47f Merge main 2026-10-18 12:28:02 -05:00
Chris Tate f59049112f Compile chalk 5 through the dynamic object model (#1309) 2026-10-18 12:25:45 -05:00
Chris Tate cb3d7dbe7c Merge main 2026-10-18 12:24:21 -05:00
Chris Tate 2a3e95ddc7 Merge main 2026-10-18 12:23:49 -05:00
Chris Tate 4a2c33b739 Merge main 2026-10-18 12:22:43 -05:00
Chris Tate da1af7437f Revert "Read fields as undefined before their assignment (#1292)"
This reverts commit ff90245b27.
2026-10-18 12:20:36 -05:00
Chris Tate cc28e0f6c9 Merge main 2026-10-18 12:20:01 -05:00
Chris Tate 71e34fa445 Merge main 2026-10-18 12:16:33 -05:00
Chris Tate 640abd5294 Merge main 2026-10-18 12:15:43 -05:00
Chris Tate 4e7f104457 Merge main 2026-10-18 12:13:40 -05:00
Chris Tate 9ef9c038da Merge main 2026-10-18 12:01:19 -05:00
Chris Tate 3c638d5537 Add the zero-cost gate (#1304) 2026-10-18 11:57:52 -05:00
Chris Tate 65d849e6b8 Merge main 2026-10-18 11:56:36 -05:00
Chris Tate 29c8ad237c Throw when nm cannot list an executable's symbols in the zero-cost gate
The gate read an empty symbol list when nm could not start, was killed by
a signal, or exited with a non-zero status, so the dynamic-tier symbol
check and the executable comparison passed without any symbols. nmOutput
now throws with the binary, the reason and nm's stderr.
2026-10-18 11:36:13 -05:00
Malte UblandClaude Opus 5.5 cd376c14f1 Refresh the corpus costs from main's newest green Linux shards
Rerun scripts/ci-corpus-costs.mjs over the 120 green Linux corpus shard
logs (plain and san, 20 shards each) of main CI runs 39142848734,
39142619508 and 39142079655 (307387c8e, 18c50b55d, b85dd44cd). Their
Linux lanes were all green; only macOS jobs were still queued.

This measures the programs added since the previous refresh, including
5770-5773, 5960-5962, 5973, 6041-6043, the CommonJS require programs
6140-6145 and 5023/5024. Coverage of current main's corpus goes from
2992/3012 (0.9934) to 3011/3012 (0.9997); the one unmeasured program,
scratch-string-slices.ts, landed after those runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:33:01 -07:00
Malte UblandClaude Opus 5.5 23981f0c95 Merge origin/main into opt/shard-costs-refresh
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:32:57 -07:00
Chris Tate 4bba6c1e0b Keep this PR's refreshed corpus costs after the main merge 2026-10-18 11:30:08 -05:00