Commit Graph
4679 Commits
Author SHA1 Message Date
Chris Tate 3c7f47e6aa Lower function values with extra optional parameters into narrower slots (#2433) 2026-10-27 14:31:51 -05:00
Chris Tate cc6ee85fd7 Run JavaScript web streams on a native ReadableStream (#2411) 2026-10-27 14:20:02 -05:00
Chris Tate a75cff7ab4 Use an exact integer loop limit as the counted loop's endpoint
A counted for loop converts its limit to an i64 endpoint. A limit that is
not a literal went through the double every time: converted to double,
clamped to 2^53, rounded with llvm.floor or llvm.ceil and converted back.
At the default target CPU the rounding is a libm call, which a loop such as
`for (let i = values.length - 1; i > at; i--)` paid on every entry even
when `at` was an int32 local. A limit that is an exact integer (an int32
local, an integer view, or a proven range) is its own floor and ceiling and
within ±2^53, so it is now the endpoint directly; other limits keep the
double path.
2026-10-27 13:49:48 -05:00
Chris Tate 349feb5e47 Capture stack traces with Error.captureStackTrace as Node does (#1809) 2026-10-27 13:49:06 -05:00
Chris Tate 1feed2a3ec Answer cross-layout record casts with views of the original record (#2419) 2026-10-27 13:47:06 -05:00
Chris Tate 2f636fa93d Keep class instances that stay in their frame in frame storage (#2426) 2026-10-27 13:46:18 -05:00
Malte UblandClaude Opus 5.5 76d08ef73e Lower function values with extra optional parameters into narrower slots
A function value with more parameters than the slot it flows into is
assignable in TypeScript exactly when the extra trailing parameters are
optional, and a call through the slot passes no value for them, so the
callee sees undefined as it does in JavaScript. funcCoerceAdapter refused
every such pair, so a callback parameter forwarded into a narrower slot
fell through to requireExactShape and failed with SC2003 ("union
conversion has no unambiguous layout mapping") even though the checker
accepted the program.

The adapter now supplies each omitted extra parameter with omittedArgFor
(the undefined arm of the parameter's ABI union, or the checked-dynamic
undefined). A pair whose extra parameter cannot hold undefined stays
refused, and an extra required parameter remains a checker error.

Only pairs that previously returned null reach the new code, so programs
that compiled before are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 11:45:13 -07:00
Chris Tate 4bd4c43efd Check Uint8Array chunks in clones of JavaScript ReadableStream bodies
Response.clone and Request.clone tee the body. The tee branches of a body
read from a JavaScript ReadableStream did not keep that body's chunk check,
so consuming the original or the clone took string chunks as text. The
check is now a flag on the body's stream that its tee branches inherit, and
consuming either branch rejects with Node's TypeError "Received
non-Uint8Array chunk".
2026-10-27 13:28:33 -05:00
Chris Tate a897079be1 Capture onto the target evaluated before a constructorOpt that reassigns it
When Error.captureStackTrace evaluates a JavaScript constructorOpt (a getter,
a call, or this.constructor with a constructor getter), V8 has already
evaluated the target. The lowering kept the target in a local only when its
expression was not a plain read, so a constructorOpt that assigned the target
variable made the capture install the stack on the new value. The target is
now always kept in a local before the constructorOpt runs.
2026-10-27 13:00:48 -05:00
Chris Tate 9bdb36ed46 Report published values as frozen, sealed and not extensible
Under Node.js, publish() deep-freezes everything it reaches, so
Object.isFrozen and Object.isSealed return true and Object.isExtensible
returns false for a published object, array, Map or Set. Native builds
already refused writes into published values, but answered these queries
as for an unpublished value: publication marks an object immortal and sets
no frozen bit.

- record.isFrozen, array.isFrozen and the extensibility test of Maps and
  Sets also read an immortal rc word as published, the test of the
  published write guards, for the types the program may publish
  (PublishedTypes). Other types emit the code they did.
- scr_dyn_is_frozen, scr_dyn_is_sealed and scr_dyn_is_extensible answer
  for a published object, array, Map or Set, or a checked view of one,
  before materializing the view.
2026-10-27 12:57:41 -05:00
Chris Tate 82cf32d1cd Convert number | undefined timer delays with ToNumber (#2427) 2026-10-27 12:42:02 -05:00
Chris Tate 3b330b850a Release a frame object's nullable class field as the class pointer it holds
A nullable class field (Tag | null) stores the class pointer itself, whatever form the union takes elsewhere. When the same union was also a Set element or a Map key, it was a boxed union outside class fields, and the frame's cleanup of a frame class released the field's raw pointer with scr_union_release. classReferenceFields now gives each field its storage type (NullableRefFields.storageType), as the heap teardown does, so the scope exit calls the class's release.
2026-10-27 12:21:27 -05:00
Chris Tate 5431a8c142 Keep class instances that stay in their frame in frame storage
A class instance could live in its function's frame only when its class
had scalar fields only and CallLifetimes proved the binding was only read.
Per-call state objects whose fields own arrays, maps and other objects,
and whose methods and helpers store into them, stayed on the heap.

frame-escapes.ts proves which locals never leave their frame: every use
keeps the value in the frame (region-ir.ts keepsInFrame), a const binding
of it in the same function shares its fate, and a closure that captures it
stays in the frame and keeps the captured binding there. Parameter and
capture facts are solved optimistically over the call graph with a
worklist; async functions, generators and module code get none.

A frame class is a standalone class whose constructor does not let this
escape. Its instance now lives in frame storage when it is bound to such a
local, when it is passed in place to a parameter that stays in the
callee's frame, or when closures that stay in the frame capture its
binding. The frame owns the reference fields: they start empty, union and
dyn fields start at undefined, and the binding's scope (or the call's
statement frame) releases them on every exit. A cycle-capable class keeps
its collector header in front of the object, never a candidate.
2026-10-27 11:56:10 -05:00
Chris Tate a3ae97c097 Specialize calls that omit a constant default and drop unreached originals (#2423) 2026-10-27 11:37:19 -05:00
Chris Tate 096bf9a364 Map a failing type exactly from its canonical form where localized failure would degrade it (#2338) 2026-10-27 11:32:18 -05:00
Malte UblandClaude Opus 5.5 cdd121405b Convert number | undefined timer delays with ToNumber
setTimeout and setInterval passed their delay to the F64 timer slot
unconverted, so a delay typed number | undefined failed with SC2003. Such
a delay is an optional `ms?: number` passed on, or a number[] element
read, whose storage keeps the out-of-range undefined. Node converts the
delay with ToNumber, so undefined is NaN, the omitted delay. A union
delay now goes through lowerOptionalNumber, as Math arguments already
do. Other unions still reach the lib boundary's refusal, and number
delays are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 09:11:51 -07:00
Chris Tate fa65a3b385 Specialize calls that omit a constant default and drop unreached originals 2026-10-27 10:49:22 -05:00
Chris Tate 8de39fdd57 Borrow typed-array pages and literal-ending getter returns (#2421) 2026-10-27 10:48:04 -05:00
Chris Tate b11bc54a2b Expect the collapsed canonical forms as dynamic slots in the coverage tests
The tools field (Tool[]) and the all global ((number | Callbacks)[]) hold unions that canonical mapping maps to the checked dynamic, so the canonical fallback now degrades them: they are dynamic slots again, with SC6104 warnings and attributed operations, as these tests expected before the fallback classified them as exact.
2026-10-27 10:46:54 -05:00
Chris Tate 3f81117a2d Record each reachable-emit unit's lowering as plain data, checked by a serial mode (#2410) 2026-10-27 10:07:09 -05:00
Chris Tate aed3e84ff5 Borrow typed-array pages and literal-ending getter returns 2026-10-27 10:04:40 -05:00
Chris Tate f12a2b2a23 Take JavaScript ReadableStreams as fetch bodies and in instanceof by value
A JavaScript ReadableStream given to Response, Request or fetch as a body
was converted to the string "[object ReadableStream]". The fetch unit now
reads such a stream through a reader of its own. A locked or disturbed
stream gives Node's TypeError, and the chunks must be Uint8Array
when the body is consumed ("Received non-Uint8Array chunk").

instanceof against a web stream class held in a variable or passed as an
argument walked no prototype for handles and answered false. It now starts
at the class prototype of the handle. The fetch unit's own streams, readers
and controllers, such as Response.body, are instances of the JavaScript
classes, so `response.body instanceof ReadableStream` is true again.

These paths, and the fetch reader functions' hand-off to web stream
readers, are in SCR_HANDLE_CLASSES variants of scr_json.c and scr_fetch.c,
which only programs with JavaScript web streams link; other programs that
use fetch build to the same size as on main.
2026-10-27 10:00:00 -05:00
Chris Tate 8f3a8e3e63 Merge main 2026-10-27 09:58:30 -05:00
Malte UblandClaude Opus 5.5 0927bfd295 Expect exact canonical mappings in the dynamic-slot coverage tests
With main's canonical-mapping gap fixes (#2340), stage N2 maps the
Tool[] field and the (Callbacks | number)[] global exactly, so they are no
longer dynamic slots. The hot-slot fixture's Hook gets an intersection arm,
which has no canonical form, so it stays a dynamic piece of Plugin[].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 07:42:33 -07:00
Chris Tate d4be651952 Prove module-level number state int32 through every write (#2416) 2026-10-27 09:37:35 -05:00
Malte Ubl 195ed79f00 Merge remote-tracking branch 'origin/main' into perf/static-objects
# Conflicts:
#	tests/harness/npm.test.ts
2026-10-27 07:21:50 -07:00
Chris Tate ddea35322b Check with the shared tsc-ts server by default on Linux and macOS (#2417) 2026-10-27 09:20:35 -05:00
Chris Tate 80bfc5fada Version for loops on the positions they advance (#2415) 2026-10-27 09:16:42 -05:00
Chris Tate aed7a20709 Let units without a record change the helpers they name in place
The serial mode's wave-end check threw when a unit changed a held function in place, even when the unit had no record and its change was its own lowering's: two functions that define computed-key properties on one class both rebuild the class's descriptor guard. Each unit's in-place changes are now checked against the names its IR and new entries quote: a unit with a record that changed such an entry has no record, and a unit without one keeps its changes. The wave-end check throws only for a change to an entry no unit names.
2026-10-27 09:03:39 -05:00
Malte UblandClaude Opus 5.5 6a30f02a69 Answer cross-layout record casts with views of the original record
A checked conversion of a capsule of record A into record type B, when A
has all of B's fields with the same types in any order, now answers a view:
a B object whose two trailing words hold A and A's offsets for B's fields.
Every field address of a B value tests the first word (recordViewFieldPtr,
the record clone and the JSON walker), so reads and writes through either
name reach the same storage. The view's record-source entry owns A's
capsule: boxing the view answers that capsule, so identity holds, B's
reflective surfaces and spreads see the whole object, and the entry dies
with the view, which releases A's capsule. The capsule finds its views
again through borrowed cast-list entries.

Only the target types of such conversions carry the words and the test.
They are planned from the IR before emission (record-views.ts): record
types a checked conversion can produce for which another record type in
the module has all their fields. Programs without checked conversions are
unchanged. This replaces the prefix pointer pass, whose narrower release
could not free the source's extra fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:35:26 -07:00
Chris Tate 3b374f3201 Check with the shared tsc-ts server by default on Linux and macOS
Builds on Linux and macOS now connect to a shared `tsc-ts --api --listen`
server by default, which keeps the files it parsed and bound, so a later
build in the same working directory does not parse the TypeScript libs and
scriptc's declaration files again. SCRIPTC_CHECKER_SERVER=0 turns it off.
When CI is set to a value other than 0 or false, builds use their own tsc-ts
process unless SCRIPTC_CHECKER_SERVER=1.

- The server exits after 600 seconds without a build (was 300).
- Its name also digests the compiler executable's identity, so each scriptc
  installation and build connects only to servers it started.
- The server process runs in the root directory, so it does not keep the
  first build's directory in use; its --cwd argument is unchanged.
- When the connection to the server fails after it answered, through a
  failed transfer or an ended stream, the native CLI runs the command again
  in a new process with SCRIPTC_CHECKER_SERVER=0 and exits with its status.
- SCRIPTC_CHECKER_SERVER no longer changes the frontend cache key: the
  server's answers are the build's own checker's.

The CLI docs describe the background process, its socket directory, when it
exits and how to stop it.
2026-10-27 08:21:48 -05:00
Chris Tate d9eb38a54d Split the slowest sanitized suites by case and raise the sanitized bootstrap's stack (#2414) 2026-10-27 08:21:31 -05:00
Malte UblandClaude Opus 5.5 db2c41910c Suppress the root certificates' leak under a stripped libnode
The libnode-packs workflow's packs are stripped, so the certificate
loading threads' Node frames have no names and the node::crypto
suppressions do not match. Each certificate is parsed through libnode's
OpenSSL PEM reader, which the runtime never links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:01:13 -07:00
Chris Tate c54c4e1d2a Prove module-level number state int32 through every write
- Module globals of type number join the whole-program int32 fixpoint as
  `global:` candidates, starting non-negative. A candidate needs its module
  initializer to assign it at the top level (its declaration), and no
  write by `++`/`--`, for-of or catch; every assignment must evaluate to
  an int32 under the current facts (an integer literal counts in any
  function), and a write that may be negative keeps only the int32 fact.
- Reads take the proven range through IntegerSlotFacts.locals, so a
  generator state reduced by a remainder (`seed = (seed * a + c) % m`)
  takes unsigned integer remainders without the -0 select.
2026-10-27 07:57:22 -05:00
Chris Tate adbf74802f Free a worker's group-arena pool when the worker exits (#2413) 2026-10-27 07:54:31 -05:00
Chris Tate fb58a34ca1 Version for loops on the positions they advance
- Loop-entry versioning covers innermost for loops as well as while loops,
  and a typed array index that the assumed entry ranges prove qualifies a
  loop as a remainder does. In `for (let j = 0; j < line; j++)
  out[o++] = f(src[i++])` the test checks `line`, `o` and `i` once per
  entry, and the fast copy bounds both positions by the loop's passes.
- Proven integer loops step their shadowed bindings in expression position
  too (`out[o++] = v`), so those positions run as i64 counters in the
  fast copy and the typed array accesses use them without a round trip.
- An increment of a binding the enclosing for loop bounds takes the loop
  range where a surrounding statement has discarded its fact.
2026-10-27 07:44:34 -05:00
Malte UblandClaude Opus 5.5 597b759e4f Merge the restacked base into ni/i11-cross-heap-gc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:37 -07:00
Malte UblandClaude Opus 5.5 37f752ab89 Merge the restacked base into ni/i10-js-island-requires
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:35 -07:00
Malte UblandClaude Opus 5.5 27fd36402b Merge the restacked base into ni/i9-recorded-deferral
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:33 -07:00
Malte UblandClaude Opus 5.5 a11b82dd8e Merge the restacked base into ni/i8-exit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:32 -07:00
Malte UblandClaude Opus 5.5 b97b161c08 Merge the restacked base into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:30 -07:00
Malte UblandClaude Opus 5.5 76a60ff53b Merge the restacked base into ni/i6-subclasses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:29 -07:00
Malte UblandClaude Opus 5.5 3a8aee738a Merge the restacked base into ni/i5-shared-modules
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:22 -07:00
Malte UblandClaude Opus 5.5 603fec49b9 Merge the restacked base into ni/i4-deferred
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:03 -07:00
Malte UblandClaude Opus 5.5 562b56a54c Merge the restacked base into ni/i3-exports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:02 -07:00
Malte Ubl caee8dfef2 Merge the restacked base into ni/i2-handles
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/compiler/src/executable/executable-cache.ts
#	packages/compiler/src/index.ts
2026-10-27 05:41:01 -07:00
Malte UblandClaude Opus 5.5 02a322e712 Merge ni/13-libnode-packs (with ni/1-libnode-loader)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:40:40 -07:00
Malte Ubl f9419b5d47 Merge remote-tracking branch 'origin/ni/1-libnode-loader' into HEAD
# Conflicts:
#	packages/compiler/src/index.ts
2026-10-27 05:40:18 -07:00
Malte UblandClaude Opus 5.5 4d5127944e Merge main into the zero-cost triage branch
Main moved the per-program report lines into zero-cost-compare.mjs and
added sharding and a baseline cache. The runtime-only and
nondeterministic verdicts now print through programLines, count as
compared in merged shard reports, and the runtime pack diff is skipped
when only recording a baseline cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:38:08 -07:00
Malte UblandClaude Opus 5.5 03ca571663 Merge main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:36:35 -07:00