Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate 0a9cfa5efd Merge main 2026-10-24 05:25:59 -05:00
Chris Tate 7278c3c467 Read a union's discriminant once in checked casts (#2073) 2026-10-24 04:51:36 -05:00
Chris Tate c652e418f6 Read the discriminant again after an arm's check fails
An arm's field check can run a getter that changes the discriminant. The
checked cast now reads the discriminant again after each failed arm;
when its case changed, the tests continue arm by arm after the failed
arm, so the cast picks the arm the arm-by-arm tests pick.
2026-10-24 04:10:52 -05:00
Chris Tate 4581dd2f12 Keep a Date boxed when a toJSON argument calls its setter
A Date local kept as its time value was read as toJSON's receiver
before the arguments ran, so a setter in an argument
(d.toJSON(d.setTime(0))) did not change the result. Such a local now
keeps its Date object, and toJSON reads it after its arguments, as in
Node.
2026-10-24 04:05:54 -05:00
Chris Tate 1559fb1d22 Merge main 2026-10-24 04:03:19 -05:00
Chris Tate 64f3bb8fbe Give the capped Linux test workers the heap they had before the cap (#2084) 2026-10-24 03:49:00 -05:00
Chris Tate 949c715f7b Keep comparator sorts and untyped-source copies out of units loops
A units copy reads the receiver's UTF-16 units through the pointer its
guard took, which stays valid only while nothing indexes another
non-ASCII string: that evicts the receiver's index entry and frees the
units. The classifier admitted every typed-array and Buffer method, so a
loop that sorted a typed array with a comparator function value, or
copied an untyped source into one (`ta.set(source)`, whose element reads
can run getters), got a units copy, and a comparator or getter that
indexed other strings made the loop read freed memory.

The units copy now admits only the typed-array and Buffer methods that
call no program code and read no checked-dynamic value or string. Corpus
7307 sorts with such a comparator and copies from such a source inside
scans of a warmed non-ASCII text; the sanitized build of the previous
commit reports a heap-use-after-free on it.
2026-10-24 03:27:00 -05:00
Chris Tate af6d2eb696 Merge main 2026-10-24 03:23:46 -05:00
Chris Tate 03e677a9c4 Inherit the base constructor after a prototype's constructor is deleted
A constructor read on a class instance through an untyped value answered
the class itself whenever the class's prototype object did not hold an own
`constructor`. After `delete Object.getPrototypeOf(new Middle()).constructor`,
Node reads the property the base class's prototype holds, and past the last
class's prototype Object.prototype's.

The read now checks the instance's own property, then walks the class's
prototypes: each answers its own `constructor`; a prototype without one
answers its class unless reflection had populated it, in which case the
program deleted the property and the read continues to the base class's
prototype, and from the last one to Object.prototype. Reflection sets a
per-class flag when the prototype initializes, so the answer does not
depend on whether the reflection was compiled before the read.

Corpus 8260 gains the deletion case.
2026-10-24 03:14:24 -05:00
Chris Tate 0f90e07603 Process non-ASCII URL hosts with UTS #46 as Node does (#2088) 2026-10-24 02:48:24 -05:00
Chris Tate 39216602ab Merge main 2026-10-24 02:39:41 -05:00
Chris Tate 66726267fc Share untyped module bindings that combine with strings and numbers (#2087) 2026-10-24 02:35:45 -05:00
Chris Tate 60c8551ad3 Merge main 2026-10-24 02:26:31 -05:00
Chris Tate f13ae4495d Merge main 2026-10-24 02:22:49 -05:00
Chris Tate 09e50bee50 Merge main 2026-10-24 01:58:41 -05:00
Chris Tate dd4ae4e508 Merge main 2026-10-24 01:57:53 -05:00
Chris Tate 0e0bedca01 Forward pointer-union wraps of call arguments to the callee's parameter proof (#2048) 2026-10-24 01:56:42 -05:00
Chris Tate bf2f1c45d7 Merge main 2026-10-24 01:52:57 -05:00
Chris Tate 1ddec6df7c Freeze the symbol-keyed fields of class instances
A frozen class instance kept its symbol-keyed fields writable through
typed references, and Object.isFrozen, isSealed and isExtensible reported
it unfrozen, because the field write checks and the instance's checked
view covered its string-keyed fields only. This held for Object.freeze
through an untyped value and for a typed Object.freeze alike.

- Writes to a symbol-keyed field of a hierarchy with an integrity word are
  checked like other field writes, and throw Node's TypeError naming the
  symbol ("Cannot assign to read only property 'Symbol(tag)' ...").
- The checked view applies the instance's integrity state to its
  symbol-keyed properties too, and the commit of an untyped freeze, seal
  or preventExtensions counts them.
- The commit removes a symbol-keyed field's entry from the property bag
  whatever its attributes (scr_dyn_obj_delete_symbol_field), as it does
  for string-keyed fields, so a sealed view no longer leaves the field's
  old value in the bag.

Corpus 7407 covers freeze and seal of symbol-keyed fields through unknown
and through typed calls, on a class and a subclass, and matches Node.
2026-10-24 01:52:11 -05:00
Chris Tate 937db54656 Raise the capped Linux test scope to 15 GB of RAM
The sanitized self-hosting native-frontend build needs about 15 GB by
itself: its clang compile of the whole frontend under ASan peaks at
10.5 GB beside a 3.8 GB compile in Node. In the 14 GB scope, with the
shard's other work running beside it, the kernel killed that clang
intermittently, so SC5004 reported clang's warnings and no error (SAN
16/20 and 19/20). The scope now caps RAM at 15 GB, still below the
runner's 16 GB, and may still swap past it.
2026-10-24 01:42:23 -05:00
Chris Tate 9496e42968 Keep a pointer-wrapped argument on the heap when its callee keeps the narrowed payload
A union parameter's proof covered the union, not its payload: narrowing it
and storing, returning or pushing the result, a narrowed nullish read, and a
store into a nullable-pointer field all counted as projections. That holds
for a boxed union, whose payload is a separate object. A record passed
through a nullable-pointer union is that payload, so forwarding it to such a
callee let a record in its caller's frame escape the frame.

A pointer-wrapped argument now needs a second fact from the callee's union
parameter: its payload stays in place too. The payload is in place when it
is only tested, narrowed for a field read (directly or through the checked
narrowing a field read of a `T | undefined` parameter compiles to), or passed
on to a parameter that is proved in turn. The facts that other arguments use
are unchanged.

Corpus 7663 has callees that push (directly and through a non-null
assertion), default, store in a global record field or a class field, or
pass on the narrowed record, and one that only reads it.
2026-10-24 01:27:44 -05:00
Chris Tate d99fc851e6 Type untyped JavaScript Maps, Sets and arrays from their uses
In a JavaScript file, `const m = new Map()`, `new Set()` and `[]` type as
Map<any, any>, Set<any> and any[], so every use lowered to the
checked-dynamic collection operations: boxed keys and values, generic
method dispatch, and dynamic arithmetic on what they returned.

js-collection-types.ts proves types for such bindings from their uses.
A `const` qualifies when every reference is a direct use of the
collection:
- for a Map: get/has/delete/clear, size, and set as a statement;
- for a Set: has/delete/clear, size, and add as a statement;
- for an array: push/pop/shift/at/includes/indexOf/join and length.

The value then never leaves the binding. Its key, value or element type
is the one primitive (string, number or boolean) of every expression the
program writes. That is found by a fixed point that types `m.get(k) ?? d`,
arithmetic, comparisons and constants defined from them, and it requires
lookups of the same type. Any other reference (passing, returning,
storing, exporting, iterating, inspecting, an optional call), a direct
`eval` in the file, or any doubt keeps the binding untyped.

A proven binding answers the lowerer's typeOf with a carrier type
declared in scriptc-overrides.d.ts, which only the lowering program
includes. `new Map()` and `new Set()` adopt it, so the binding lowers as
the typed Map<K, V>, Set<K> or T[], as a JSDoc @type would make it.

7985 covers the typed cases and every refusal.
2026-10-24 01:22:42 -05:00
Chris Tate 1e90263517 Support Symbol.toStringTag class fields and getters (#2037) 2026-10-24 01:21:32 -05:00
Chris Tate 20339d9c27 Process non-ASCII URL hosts with UTS #46 as Node does 2026-10-24 01:13:00 -05:00
Chris Tate 70c2b8246d Share untyped module bindings that combine with strings and numbers
A JavaScript module binding whose initializer joins an untyped value with
a string, number or boolean through `||`, `&&`, `??` or `?:`
(`export const APP_NAME = pkg.appConfig?.name || "app"`) stayed a local of
the module's initializer, so reads from other modules and functions
failed with "a binding form with no lowering". Such initializers now
register the binding in checked-dynamic module storage, like the untyped
initializers they combine.

pi-coding-agent's config.js declares APP_NAME, APP_TITLE and isBundledNode
this way.

Refs #1777.
2026-10-24 01:03:18 -05:00
Chris Tate 373ebd9706 Initialize import cycles that re-export a member with export \* (#2081) 2026-10-24 00:55:34 -05:00
Chris Tate f713cd631c Run the int32 slot analysis once per module the emitter emits
Release emission selects int32 arrays by analyzing each module
selectInt32Arrays marks (provenInt32Arrays) until every candidate it
marks is kept, then constructed LlEmitter on the returned module, whose
constructor built a node index, found the initializer bindings and the
dynamic field stores, and ran analyzeInt32Slots on that same module
again. On tsc-ts the constructor's call alone took about 3 s.

Each round now analyzes its module with the inputs the constructor
gives the analysis (one node index, the initializer bindings and the
dynamic field stores read through it), and the emitter of the module
selectInt32Arrays returns takes over the last round's analysis and
inputs instead of computing them again. The rounds also read the node
index instead of walking the bodies. The emitted LLVM is unchanged.
2026-10-24 00:52:48 -05:00
Chris Tate 98e3ff52b3 Reuse linked executables on Linux (#2077) 2026-10-24 00:50:58 -05:00
Chris Tate 2764288376 Dispatch untyped calls on plain arrays without the method ladder
An untyped call of an array method went through the whole dispatch ladder
on every call: the receiver-kind tests for handles, proxies, typed refs,
objects, functions and primitives, then the array section's prologue
(the hole scan and the frozen, sealed and extensibility tables) before
the method itself.

A dense array without holes, named properties or a prototype of its
own, whose length and elements are writable, now runs push, pop, shift,
at, includes and indexOf directly from the call site's cached method id.
An argument that could run user code (a non-number index, a fromIndex)
still takes the ladder, as does every other receiver. A statement-position
push calls an entry that does not box the new length.

Calls on arrays that the direct path refuses now also follow Node:
- an array's own method, or one on its custom prototype chain
  (Object.setPrototypeOf), runs instead of the builtin;
- push on a sealed or non-extensible array throws "Cannot add property
  N, object is not extensible", and pop or shift on a sealed array
  "Cannot delete property 'N' of [object Array]".
2026-10-24 00:43:56 -05:00
Chris Tate 68a9dea3fa Give the capped Linux test workers the heap they had before the cap 2026-10-24 00:42:45 -05:00
Chris Tate 6715609695 Place each source file's functions in the same partitions
Partitions were filled by a hash of each function's name, so most calls
crossed partitions and the thin link imported their small callees into
nearly every partition. The emitter now writes a "; scriptc-group" line
before the functions of each source file, and the text partitioner places
a group's functions together: a group larger than the mean partition, or
than 1 MiB, is divided into a power of two of chunks by name, and chunks
go, largest first, to the lighter of two partitions chosen by hash. The
1 MiB bound keeps the code a partition optimizes together, and the
inlining within it, from growing when a program has fewer partitions.
Modules with fewer than 32 partitions, and functions without a group,
keep their name-hash placement: with few partitions per core, the balance
of the largest partitions decides the build time more than the imports.
The placement still depends only on the module text and the partition
count.
2026-10-24 00:34:25 -05:00
Chris Tate 7ed72cca80 Merge main 2026-10-24 00:34:08 -05:00
Chris Tate bfb360a659 Add a runner for Node's test/parallel files
tests/node-parallel/run.mjs builds files from a Node.js checkout's
test/parallel with scriptc, runs each executable in that directory, and
compares its exit status with Node's, writing a pass, fail or refused
verdict and the first error line per file (results.json, results.md).
--common minimal (the default) runs the files against a minimal
test/common (common/index.cjs, common/wpt.cjs) whose helpers keep Node's
semantics, so a verdict measures the module under test; --common node
uses Node's own test/common.
2026-10-24 00:30:36 -05:00
Chris Tate 7bf50dd9cc zlib: Brotli compression and decompression over the vendored brotli 1.2.0 (#2053) 2026-10-24 00:30:21 -05:00
Chris Tate fd768cd914 Initialize import cycles that re-export a member with export *
An `export * from` declaration binds each re-exported name to the binding
of the module that declares it, like a named re-export, so it has no
initialization state of its own. Import cycles whose modules re-export
another member of the cycle this way now compile with the exact cycle
initialization: re-exported names read the same values, and throw the
same temporal dead zone errors, as in Node. `export * as ns` of a cycle
member keeps the SC1016 refusal.

pi-coding-agent's sdk.js (sdk.js → agent-session-runtime.js →
agent-session-services.js → sdk.js) has this shape.

Refs #1777.
2026-10-24 00:24:12 -05:00
Chris Tate ec4929fc1c Merge main 2026-10-24 00:12:03 -05:00
Chris Tate 970a53c1ba Store (T | undefined)[] elements as T so widened arrays stay one array
A number[], string[] or boolean[] seen as (T | undefined)[] through a
cast, a binding or a parameter is the same array in Node: undefined
written through either name is visible through both. The type mapper
now stores (T | undefined)[] of a number, string or boolean T as T
elements, which hold a present undefined by their slot state, so the
wider name shares the storage instead of converting to a separate
union array.

Writers of these arrays store undefined as a present undefined:
element writes, push, unshift and splice, literals, Array.from (with a
length or a mapper), Array.of, generator drains, flatMap, rest packs,
copies from Map and Set values and Object.values, and checked
conversions from dynamic values. Promise.all over Promise<T |
undefined>[] fills the result inside the combinator's settlement, so it
settles on Node's turn. The element-state analysis counts callbacks and
iterables whose checker type admits undefined.

Readers keep their optional reads: index reads through a union of
arrays, searches with a direct needle, and asserted reads, which throw
a TypeError for undefined. Comparators and callbacks that receive
present elements take the element type, without an adapter.
2026-10-24 00:05:48 -05:00
Chris Tate de5557ce5a Answer Error.prototype for Object.getPrototypeOf of an Error (#2058) 2026-10-24 00:04:29 -05:00
Chris Tate 07bab259ee Read typed-array elements through non-null assertions as undefined where Node does (#2067) 2026-10-23 23:53:54 -05:00
Chris Tate 1e2f9b2dfc Resolve computed createRequire specifiers at run time
In JavaScript dependencies, a createRequire require call with a computed
specifier, and require.resolve called with a computed request or read as
a value, now compile. The new runtime unit scr_require.c resolves the
specifier with Node 24's CommonJS algorithm (builtins, relative and
absolute paths with Node's extension search, package "main", "exports"
and self-reference, node_modules and NODE_PATH lookup) when the call runs:

- require.resolve returns the id or real path Node returns, and throws
  Node's MODULE_NOT_FOUND error when nothing resolves.
- require throws the same MODULE_NOT_FOUND error when nothing resolves,
  so packages that probe for optional addons, such as pi-tui, take their
  absent branch as in Node. When the specifier resolves to a native addon,
  a builtin or another file, Node would load that module: the program
  prints an SC2020 error naming it and exits with status 1, without
  running the catch clause around the call.

Coverage lists these calls as deferred to runtime. TypeScript sources keep
the compile-time refusal.

Refs #1777.
2026-10-23 23:52:50 -05:00
Chris Tate cbb87e8812 Address review comments 2026-10-23 23:49:40 -05:00
Chris Tate e3fb35cc68 Cap the Linux test step's memory and move memory-heavy side files apart (#2030) 2026-10-23 23:48:45 -05:00
Chris Tate 3b951ef4e2 Let objects inherit from JavaScript class prototypes 2026-10-23 23:43:30 -05:00
Chris Tate fe3fd0de44 Parse URL hosts and paths as the URL Standard does, and inspect URLs as Node does (#2071) 2026-10-23 23:38:03 -05:00
Chris Tate 82ebbd4df7 Fix the species trace, deep release, iteration bound and optional length findings 2026-10-23 23:31:58 -05:00
Chris Tate cdef55778a Answer Error.prototype only while an error keeps its built prototype
Object.getPrototypeOf of an Error answers Error.prototype when the error's view was built from its fields and its prototype is still the object the view was built with. After Object.setPrototypeOf(error, {}), it answers the object that was set.
2026-10-23 23:31:30 -05:00
Chris Tate 0309a21106 Fix the tsgo and tsc-ts IR identity failure 2026-10-23 23:29:39 -05:00
Chris Tate 543cb5c3c2 Divide by Math.pow(2, n) as a multiply by its reciprocal (#2076) 2026-10-23 23:29:14 -05:00
Chris Tate 6722a01898 Track asserted typed-array reads that can be undefined in element state 2026-10-23 23:28:09 -05:00
Chris Tate f060cad505 Inspect dynamic classes, functions and %o arguments as Node does (#1848) 2026-10-23 23:23:51 -05:00