Commit Graph
6750 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 3041df387d Spread untyped iterables into Math.min and Math.max
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:59:56 -07:00
Malte UblandClaude Opus 5.5 3fff00e5f7 Fold navigator member reads under optional chains
zod 4 probes `navigator?.userAgent?.includes("Cloudflare")`. Node's
navigator global is never nullish and has no value form here, so the
optional step is the plain member read: `navigator?.userAgent` folds to
the same `Node.js/<major>` literal as `navigator.userAgent`, instead of
lowering the navigator object as a value and refusing at run time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:59:34 -07:00
Malte UblandClaude Opus 5.5 7f7e11a1a7 Read and write Error.stackTraceLimit through the Error constructor value
zod 4 saves and restores the limit through an alias,
`const E = "captureStackTrace" in Error ? Error : null`. The interned
Error constructor value now answers `stackTraceLimit` from the same limit
the static member and stack capture use, and numeric writes set it, so
`E.stackTraceLimit` reads 10 instead of undefined and `E.stackTraceLimit = 0`
reaches `Error.stackTraceLimit`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:59:34 -07:00
Chris Tate f3bb5d2cb1 Borrow the array of an inline Atomics operation from its local
emitInlineAtomicsOp read its typed array with emitExpr, so every Atomics
call on a local array retained the array before the atomic instruction and
released it after, two out-of-line calls around one instruction. The
array now goes through emitBorrowedInput: an unchanged, unboxed local owns
it across the later operands and the operation, and the instruction and
scr_atomics_op only read it. Fields, globals, captures and bindings an
operand can reassign keep their own reference.
2026-10-22 20:57:07 -05:00
Chris Tate e501b046d5 Keep the native compiler's LLVM text as the emitter's parts (#1919) 2026-10-22 20:56:27 -05:00
Chris Tate 1bd65afe61 Keep the charAt unit reads' origins on the emitter (#1921) 2026-10-22 20:56:03 -05:00
Malte UblandClaude Opus 5.5 17bef8c03b Type user code from npm package declarations at a checked, identity-preserving boundary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:54:55 -07:00
Chris Tate b025254f79 Keep the charAt unit reads' origins on the emitter
The charCodeAt reads that stand for rewritten charAt reads (one-unit
tests and switches) recorded the charAt each stands for in a
module-level WeakMap. Compiler sources keep such caches on their
owners: the map now belongs to the emitter (charAtUnitReads), and
charAtUnitRead takes it.
2026-10-22 20:53:28 -05:00
Chris Tate 5ed872b843 Index arrays with exact i64 products of int32 values (#1857) 2026-10-22 20:53:08 -05:00
Chris Tate 7b79d73a55 Store local number arrays of proven int32s in 4-byte slots
A local number[] that only compiled element operations reach, and whose
every stored element the int32 slot analysis proves an int32 (never -0),
is now stored as SCR_ELEM_I32: 4-byte int32 slots, with state bytes once
it holds a hole or undefined, as for every array. Element reads give
consumers an exact int32, so indexing with an element skips the double
round trip, and the slots take half the memory.

The runtime's dense slot accessors convert between the int32 slot and the
double, so its double-valued entry points serve int32 arrays unchanged;
growth, copies, fill and length changes use the 4-byte width.

ir/int32-arrays.ts proposes the candidates and inlines their element-reader
helper calls; the int32 slot analysis keeps the proven ones, and
selectInt32Arrays rewrites the module for exactly those, so a program
without an int32 array emits the same LLVM IR as before.
2026-10-22 20:52:38 -05:00
Chris Tate 5e4303e62d Keep the native compiler's LLVM text as the emitter's parts
The LLVM emitter builds a module as parts and joined them into one
string: on a large program such as tsc-ts the parts and the 150 MB joined
text were live together at the end of emission. The native compiler only
writes, escapes and hashes the text, so it now takes the emitter's parts
(emitLlvmModuleParts) and never joins them. Consecutive small parts are
joined into batches of about 1 MiB as they are written, escaped or hashed
(joinedParts), so the many small parts cost few calls. A frontend cache
hit reads the text back as one part. The three emission entry points
share one module pipeline (moduleEmitter).
2026-10-22 20:52:22 -05:00
Chris Tate dcd86a17da Merge main 2026-10-22 20:52:05 -05:00
Chris Tate fc05167a29 Merge main 2026-10-22 20:50:35 -05:00
Malte UblandClaude Opus 5.5 d646fa70d0 Collect garbage cycles across the scriptc and V8 heaps (NI-8)
Node islands stack, part 11 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:56 -07:00
Malte UblandClaude Opus 5.5 00e88c2dfd Islands: CommonJS island requires, builtin-class handles, JavaScript widened bindings
Node islands stack, part 10 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:56 -07:00
Malte UblandClaude Opus 5.5 ec88372eba Defer island modules whose recorded load is deterministic (NI-7b/7d/7e/7f)
Node islands stack, part 9 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:55 -07:00
Malte UblandClaude Opus 5.5 55a69232b7 Exit cleanly with Node loaded and keep nested entries on Node's stack (NI-7c, NI-3b)
Node islands stack, part 8 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:55 -07:00
Malte UblandClaude Opus 5.5 0f41b7bc67 Island handle operations, native subclasses of island classes, arrays and collections by reference (NI-5b/5c/5d)
Node islands stack, part 6 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:54 -07:00
Malte UblandClaude Opus 5.5 0dcf0e95d3 Route more packages and values through Node islands (NI-4c–4g)
Node islands stack, part 7 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:54 -07:00
Malte UblandClaude Opus 5.5 5fa44d9243 Share modules between a program and its Node islands through loader hooks (NI-6)
Node islands stack, part 5 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:53 -07:00
Malte UblandClaude Opus 5.5 2f8e8b2b92 Defer evaluating Node island modules until the program uses them (NI-7)
Node islands stack, part 4 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:53 -07:00
Malte UblandClaude Opus 5.5 a1b6fda11c Run npm packages in Node islands: object handles and compiler routing (NI-4)
Node islands stack, part 2 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:52 -07:00
Malte UblandClaude Opus 5.5 106eccd4fd Pass native closures, promises and objects into Node islands by reference (NI-5)
Node islands stack, part 3 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:52 -07:00
Malte UblandClaude Opus 5.5 993486e4a4 Bound V8's stack scan by Node's own stack in Node islands
Node islands stack, part 1 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:49:51 -07:00
Chris Tate 5ad562da7e Hold any later value in JavaScript var bindings (#1916) 2026-10-22 20:46:16 -05:00
Chris Tate 3991a49094 Hold any later value in JavaScript var bindings
A JavaScript `var` took the checker's type of its initializer, which no
later write is checked against in a JavaScript file. js-yaml 4.1.1 failed
on every timestamp with "a 'string' value is not representable in the
target union": its `var fraction = 0` holds the fraction's digits as a
string while it pads them (`fraction = match[7].slice(0, 3)`). Every YAML
error also threw the "assignment to non-variables" fence instead of the
YAMLException, because its error mark is a `var` object literal that
gains a member (`mark.snippet = snippet(mark)`).

- A `var` whose writes the declared type cannot hold is checked-dynamic,
  by the rules `let` already uses (jsBindingHasOpenWrites), and so is a
  `var` whose object literal gains members or carries methods
  (jsDeclarationExpandsObject, shared with `let` and `const`).
- `+=` of a string onto a number or boolean binding (`width += "px"`)
  counts as such a write, for `var` and `let`.
- A JavaScript function that returns such a binding returns a
  checked-dynamic value (an async one a promise of one) instead of the
  initializer's type. A string binding that is only appended to stays a
  string.
2026-10-22 20:40:56 -05:00
Chris Tate 961aad6ac9 Keep the identity of records and class instances converted to dictionaries
A record or class instance converted to a string-keyed dictionary type
(`Record<string, V>`, `{ [key: string]: V }`) was copied field by field
into a native dictionary record, directly, through a dictionary parameter,
or through a `{}`, `object` or `unknown` binding or parameter. Writes
through the dictionary missed the original object, and the dictionary was
never `===` to it, where under Node it is the same object.

ClassProtocols now marks a dictionary value type when a dictionary of that
value type receives an existing object (a binding, member, element or
`this` whose type is a program class instance, a record with declared
properties, or a `{}`, `object` or `unknown` binding or parameter that
holds one), and the program writes through, compares or passes to a
whole-object observer a dictionary of that value type. usesCheckedIdentity
answers true for those dictionaries, so they lower to checked-dynamic
storage, which holds the original object. Dictionaries of `unknown` or
`any` values are checked-dynamic already and are not marked.

The dictionary case of SC6001 is removed, since these conversions now keep
the object.
2026-10-22 20:37:10 -05:00
Chris Tate fa843548a5 Keep Dates as time values where no use observes their identity 2026-10-22 20:36:52 -05:00
Chris Tate 8c67f04062 Throw Node's TypeError for class constructors called through call/apply (#1892) 2026-10-22 20:36:01 -05:00
Chris Tate 5966f23bd3 Keep the class-value call fence when a subclass declares a static call
`C.call(...)` through a class value typed as `Base` may run a subclass's own
static `call`, which Node calls instead of the class constructor. The
constructor-call lowering now applies only when no class the receiver can
hold declares a static `call` or `apply`: the class and its bases (any
static member of that name, including accessors, fields without
initializers and computed names), and, for a receiver that may hold a
subclass, every subclass. Otherwise the call keeps its existing
diagnostic.
2026-10-22 20:33:33 -05:00
Chris Tate 8d5b3377aa Represent Dates as objects with identity
A Date is a refcounted object holding its time value, shared with the
checked-dynamic Date handle, so it keeps its identity through unknown.
Dates compile in arrays (includes, indexOf, lastIndexOf, join, default
sort by toString() text), in Date | undefined and Date | null slots, as
Map and Set keys and values, in index signatures, and in records, tuples,
class fields, closures, promises and generators. === compares identity,
relational operators compare time values, new Date(date) makes a new
Date, and JSON.stringify writes toJSON().

Closes #225
Co-authored-by: Max B <max@tfq.at>
2026-10-22 20:33:09 -05:00
Chris Tate 210dee9eb0 Read the global object without rebuilding its name table (#1895) 2026-10-22 20:26:51 -05:00
Chris Tate a9ee38611e Register the global object's names again after a library re-init 2026-10-22 20:20:56 -05:00
Chris Tate b306a4e11e Leave fresh unknown[] storage out of the view-hole fact
Merged with current main, the hole fact for stores through dynamic views
fired on the emitter's per-feature state (`states[slot] = create(emitter)`
in emitter-state.ts writes past the end of `emitter.moduleStates`, an
`unknown[]`). The viewed element types then widened reads of the IR
function arrays, and the object spreads of `IrFunction | undefined` in
unshare.ts, callback-specialization.ts and constant-globals.ts were
refused when the LLVM emitter bootstraps.

A receiver that only holds the array its declaration creates (a const
binding or a readonly property whose initializer is an array literal,
`new Array` or `Array.of`/`Array.from`, possibly through further such
bindings) is never a view of a typed array, so stores through it add no
hole fact.
2026-10-22 20:18:02 -05:00
Chris Tate 6bbabd5fc9 Convert dynamic values into nested unions in one pass
Converting a dynamic value into a union (`JSON.parse(text) as T`, a cast
from `any`) tries the arms in order and builds the first arm whose literal
discriminator and whole structure match. The builder of that arm converts
its members, and a member typed as another union matched its arm's
structure again. Every value was matched once per union around it, so
converting a tree of depth d cost about d times as much as a flat one.

The arm a union matched now builds as a trusted conversion
(scr_dyn_trust_enter). Inside it, a nested union picks an arm without
matching the arm's structure, which the enclosing match already checked,
when the value's arm is known: a plain object's arm by its literal, when
the arms' literals are disjoint and no arm without a literal takes plain
objects, or the only arm that takes plain objects (a record) or arrays (an
array), as in an optional or nullable member. Other unions, and values that
are not plain objects or arrays, match as before. Getters and proxy traps
run outside the trusted conversion. Failed conversions throw the same
TypeError with the same path.
2026-10-22 20:15:02 -05:00
Chris Tate 8807c24526 Compile zod 3 natively (#1835) 2026-10-22 20:13:04 -05:00
Malte Ubl f593e17e1d Merge remote-tracking branch 'origin/main' into feat/decl-boundary 2026-10-22 18:12:28 -07:00
Chris Tate 0c6a35996d Fix the native compiler build on main (#1913) 2026-10-22 20:10:49 -05:00
Malte UblandClaude Opus 5.5 982572c173 Dispatch splice on arrays of unknown elements
Arrays the checker types with unknown elements are stored checked-dynamic,
and their methods dispatch on the runtime array. splice was missing from
that dispatch table, so unknown[] receivers refused it, including generic
bodies instantiated at unknown (pool.splice(i, 1) in a sample<T> helper
gets such an instantiation once a program has a dynamic tier). The
runtime's dynInvoke already implements splice; the table now names it,
and arrays of unknown elements take the dispatch from the array-method
lowering.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:10:08 -07:00
Malte UblandClaude Opus 5.5 122c98cf4f Read the global object without rebuilding its name table
Every evaluation of `globalThis` as a value built an array literal of
every name the global type declares (hundreds of strings) and registered
each one in the known-name table again. A server that reads a singleton
through `globalThis` per request spent about a tenth of its time there.

Emit the name table as one immortal static array per program (the
tagged-template site array, keyed by its contents), and return the
global object at once when the runtime sees a table it has already
registered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:10:05 -07:00
Chris Tate ae5c7d7780 Map interfaces that extend an any-typed base (zod z.infer) to checked-dynamic values (#1912) 2026-10-22 20:08:05 -05:00
Chris Tate 33b22c5bac Fix the native compiler build on main 2026-10-22 20:07:43 -05:00
Chris Tate 1660531c7b Support FinalizationRegistry with bookkeeping and no cleanup callbacks (#1854) 2026-10-22 20:03:31 -05:00
Chris Tate 62a0426193 Keep Linux poller timers in a heap behind one timerfd (#1896) 2026-10-22 20:02:08 -05:00
Chris Tate 43917080c9 Reach a class's methods from 'this' in its static initializers
The cycle walk resolved only identifiers, so a static field initializer or static block that called a method through 'this' did not reach the method, and its reads of a cycle binding declared later lost their temporal-dead-zone checks. 'this' in static code now reaches the class's stored unit, as naming the class does.
2026-10-22 20:01:47 -05:00
Chris Tate 636ffa21be Merge main 2026-10-22 19:59:51 -05:00
Malte UblandClaude Opus 5.5 d0334f93f5 Record the preflight/order baseline for the zod3-infer npm case
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:57:59 -07:00
Chris Tate 0d016b7554 util: runtime format strings, inspect.custom and colors, untyped promisify 2026-10-22 19:57:46 -05:00
Chris Tate b90a01fb80 Update the diagnostics fixtures now that Iterable values compile (#1910) 2026-10-22 19:57:19 -05:00
Chris Tate 56149fde04 Count each epoll test deadline from its own arm call
The poller test recorded every initial deadline as the same start time
plus the delay, while the poller reads the clock at each arm call; when
arming the 2,000 timers took over 1 ms, the recorded order could differ
from the delivered one by more than the assertion's slack. Each deadline
now counts from the time read just before its scrp_timer_arm call, as
the re-arm loop's do, and a 3 ms pause halfway through arming keeps the
test exercising that case.
2026-10-22 19:56:53 -05:00