Commit Graph
2836 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 b8dd7d2182 Throw MODULE_NOT_FOUND with its code from statement-level requires
A statement-level require() of a package nothing installed threw a plain
Error; the expression form already carried Node's MODULE_NOT_FOUND code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:36 -07:00
Chris Tate 26946d6b22 Revert "Copy repeated childless IR nodes with a spread" (#1404) 2026-10-19 08:57:17 -05:00
Chris Tate 0abf9a6395 Revert "Copy repeated childless IR nodes with a spread"
This reverts commit c836f93998. The
bootstrap seed build cannot compile the spread copies (SC1090).
2026-10-19 08:34:08 -05:00
Chris Tate 7f907d23e3 Read getTestContext's arguments through their nodes (#1402) 2026-10-19 08:18:09 -05:00
Chris Tate edd3874925 Run iterator helpers and destructuring on generators passed through untyped values (#1389) 2026-10-19 07:57:24 -05:00
Chris Tate c6e16d365b Read getTestContext's arguments through their nodes
lowerGetTestContext (#1393) read expr.arguments as an array. Since #1394
an AST list's elements are in nodes, so the compiler did not type-check.
2026-10-19 07:56:11 -05:00
Chris Tate 9e628720ae Test the common IR node kinds first in child traversals (#1391) 2026-10-19 07:50:45 -05:00
Chris Tate c80f9e62a1 Record new corpus program costs in per-program files (#1361) 2026-10-19 07:48:44 -05:00
Chris Tate 56850331b6 Copy repeated childless IR nodes with a spread (#1392) 2026-10-19 07:43:33 -05:00
Chris Tate 8e0af5fc59 Add getTestContext to node:test (#1393) 2026-10-19 07:37:01 -05:00
Chris Tate 098425ec78 Add an experimental young-in nursery for compiled classes and records (#1319) 2026-10-19 07:32:01 -05:00
Chris Tate 05f6c9c6ad Read AST lists through their nodes (#1394) 2026-10-19 07:30:01 -05:00
Chris Tate 3c749a2895 Read AST lists through their nodes
The frontend's node lists become node lists, { pos, end, nodes }, the
shape of tsc-ts's NodeList, so that the frontend can later read tsc-ts's
own nodes. AstNode's list getters return an AstNodeList, ts.NodeArray<T>
is that class, and the frontend reads a list's elements through nodes.
The lists already carried their positions on the wire.

The class is the type, not a structural interface, because native code
represents a class and a record differently.

A decoded file keeps each list's nodes array, as before, and each read
of a list field returns a new AstNodeList over it, so the file holds no
more objects than before. nodes keeps its identity; a list object is not
the same object on each read.

The change is mechanical: a type-aware rewrite of each list field read,
plus hand edits where lists are read through casts, generic accessors
or test mocks. The generated getters and types follow from
generate-ts7-ast-schema.mjs. AstFile and Ts7Session drop their unused
listMetadata hook.
2026-10-19 06:33:33 -05:00
Chris Tate e82686eab7 Add getTestContext to node:test
node:test changes, matching Node 24.22:

- getTestContext() returns the context of the test, suite or hook whose
  code is running: undefined at module top level, the root's context in
  module-level hooks, the suite's in a suite body and in the suite's
  hooks, and the test's in its body. In the afterEach and t.after hooks
  of a subtest cancelled by its parent's timeout, it returns each hook's
  owner.
- The context rides the AsyncLocalStorage snapshot under a reserved id,
  so it follows a test across its awaits, into the async functions,
  promise callbacks, timers and other callbacks it schedules, and into
  concurrent subtests, which each see their own.
- The root's fullName is "<root>", as its name is.
- Optional chains over a test context, such as getTestContext()?.name,
  lower.

AsyncLocalStorage enter and restore move into the always-linked async
core so the test runner can use them. The snapshot holds the node's
index in a table of live nodes, so a snapshot that outlives its node
answers undefined.

The get-test-context and get-test-context-cancelled fixtures compare
the contexts seen at each point with Node, plain and with SCRIPTC_SAN=1.
2026-10-19 06:29:28 -05:00
Chris Tate c836f93998 Copy repeated childless IR nodes with a spread
unshareNodes gives every occurrence of a repeated node its own object.
Nodes with children are copied by mapExprChildren and mapStmtChildren,
which spread the node and keep its type and location objects. Childless
nodes were copied by reparseExpr and reparseStmt, which stringify the
node and parse the text, copying the type and location objects as well;
on the three.js nodes fixture that took about 6 s.

Childless nodes are now spread too. Analyses key their facts by node
identity, which each copy still has of its own. The type and location
objects are shared as they already are in every copied node with
children and in every function without a repeated node. The emitted IR
is unchanged for every corpus program.

unshareNodes takes about 2 s on the three.js nodes fixture instead of
8 s, and compiling it to LLVM text takes 171 s instead of 178 s (median
of five interleaved runs).
2026-10-19 06:18:17 -05:00
Chris Tate 60084fcf48 Test the common IR node kinds first in child traversals
everyExprChild, everyStmtChild, mapExprChildren and mapStmtChildren
switch over the node kind: 96 expression and 26 statement string cases,
which the engine tests one after another in source order. Every analysis
and rewrite walks the IR through them, and some of the most common kinds
were near the end: in the three.js nodes module, dynFrom, libCall and
dynCheck make up 27% of expression nodes and were the 64th, 94th and
96th cases.

The cases for the most common kinds now come first, in the order of how
often they occur in the three.js nodes and Effect fixtures. All other
cases keep their order, and no case changes. In those fixtures, seven
expression kinds make up 89% of expression nodes and four statement
kinds make up 88% of statements.

A full child walk of the three.js nodes module takes about 108 ns per
node instead of 154 ns. Compiling it to LLVM text takes 161 s instead
of 180 s (median of five interleaved runs), and the Effect schema
fixture 64 s instead of 69 s. The emitted IR is unchanged.
2026-10-19 06:14:15 -05:00
Chris Tate 6a8732f57a Run iterator helpers and destructuring on generators passed through untyped values
A compiled generator that reached an untyped value, as in
`function f(it) { return it.map(g) } f(gen())`, refused its iterator
helper calls with SC1071 at run time: the runtime's helpers could not
step a generator's capsule. Array destructuring of untyped values only
handled arrays, strings and bytes, so `const [a, b] = it` threw "is not
iterable" for generators, iterators, Maps and Sets.

Programs that box a synchronous generator and call a helper method or
destructure an untyped value now register, first thing in %main, the
checked getters that read a generator capsule's next and return methods
(scr_dyn_set_generator_members). With them:

- helper calls on a generator capsule run the runtime's helpers, which
  step and close the generator through its own next() and return();
- reads of a helper name answer the runtime's helper function, so
  `typeof it.map` is "function" and `it.reduce.call(it, f, init)` works;
- destructuring of any other iterable steps it through the iterator
  protocol (scr_dyn_iter_take, and rest elements through
  scr_dyn_iter_pack), closing an iterator that has values left.

A return() through an untyped value with no argument, or with undefined,
now completes with undefined as the typed .return() does, instead of
converting undefined to the generator's return type. This also fixes
for-of loops over untyped generators with a typed return value that exit
early.
2026-10-19 05:52:22 -05:00
Chris Tate ac21611925 Use EventEmitter, the stream classes and the error classes as values (#1387) 2026-10-19 05:09:04 -05:00
Chris Tate 309fa897ec Use EventEmitter, the stream classes and the error classes as values
`function make(C: typeof EventEmitter) { return new C(); }`,
`const kinds: ErrorConstructor[] = [TypeError, RangeError]` and
`(typeof Readable)[]` stopped at SC2020 (the type has no mapping) or
SC1090 (builtin classes as values). These classes are now class values:
the name in a TypeScript file (or a const alias of it) is the class
object, `typeof EventEmitter`, `typeof Readable` and ErrorConstructor-
style types map to class values, and parameters, arrays, Map values and
object fields hold them.

Construction through a value calls %C.construct, which builds the
instance with the runtime constructor the direct `new` uses (emitter.new,
a stream's newDyn without options, error.newOptions for Error and its
six standard subclasses). instanceof, typeof, .name and === answer as in
Node. A Duplex value widens into a `typeof Readable` slot, and stream
values widen into `typeof EventEmitter` slots, as the constructor ABIs
match; a union of such class values (`const kinds = [Readable,
Writable]`) is a value of their nearest common ancestor, and `new K()`
through it answers the checker's union of instances. `instanceof`
through a value that may be Writable keeps the Duplex rule at run time.

EventEmitter and stream values constructed with options, runtime class
values stored as untyped values or printed, statics read through a value,
and DOMException, AggregateError, AssertionError and SuppressedError
values report SC1090. The existing static lowerings
(`EventEmitter.setMaxListeners`, `Error.stackTraceLimit`, the events
module) are unchanged. JavaScript files keep the identity token.
2026-10-19 04:48:10 -05:00
Chris Tate 4cb690db0c Name the destination of every ownership transfer in the LLVM emitter (#1384) 2026-10-19 04:32:54 -05:00
Chris Tate 20a989e405 Format Dates with Node's local time zone names (#1355) 2026-10-19 04:12:27 -05:00
Chris Tate ef394d0de9 Name the destination of every ownership transfer in the LLVM emitter
moveTemp, retainValue and releaseValue are replaced by forms that state
where a reference goes or comes from: toHeap and toFrame, retainHeap and
retainFrame, releaseHeap and releaseFrame. A heap reference is one that
heap memory holds: a field, an array or map element, a capture box, a
union box, a module global, a promise or generator slot, the exception
cell or another runtime structure. A frame reference is one that a
local, a parameter, a return value, a stack object or a temporary holds.
Sites whose destination depends on the binding (a local or a module
global) or on the function (a coroutine's return fulfills its promise)
choose at emission time.

adoptHeap marks the runtime results whose +1 the runtime made from heap
memory and that become frame values: capture box reads, generator in and
out slots, and await results. The statement-frame release is renamed
releaseTemps.

Both forms of each operation emit what the old call emitted, so the
generated IR is unchanged.
2026-10-19 04:09:13 -05:00
Chris Tate ee5008c1e5 Inspect class instances as Node does (#1382) 2026-10-19 04:03:05 -05:00
Chris Tate 5e9815759c Commit default-region memory as ProgramLifetime instances use it (#1381) 2026-10-19 03:55:18 -05:00
Chris Tate d79bcf9d79 Accept test and describe calls without a name, and settle skip and todo as Node does (#1371) 2026-10-19 03:49:07 -05:00
Chris Tate b9a54a65df Destroy an idle pooled http socket that receives data (#1380) 2026-10-19 03:43:51 -05:00
Chris Tate d823931318 Give matchAll rows and exec results their index, input and groups (#1323) 2026-10-19 03:41:07 -05:00
Chris Tate 0523806107 Inspect class instances with added properties as Node does
console.log and util.inspect of a class instance list the properties
added to it after construction, after its declared fields. JavaScript
classes print their own properties in creation order: a field first
assigned in a method or under a condition appears only once it exists.
Such instances render through their checked-dynamic view when the class
has a property bag or is a JavaScript class; other classes keep the
generated inspect helper.

The runtime inspector detects cycles through the class instances it
reaches through views, so a cycle prints [Circular *N] instead of
expanding to the depth limit. A circular target's <ref *N> prefix counts
toward the 80-column line width, as in Node.

Capsules of a recursive class type carry the class's view operations,
so a nested instance prints with its class name.
2026-10-19 03:37:40 -05:00
Chris Tate d8d48e1637 Commit default-region memory as ProgramLifetime instances use it
A thread's default region asked for huge pages from its first chunk and
gave every class a 16 KiB segment, so in a program that only references
LifetimeRegion each thread committed whole 2 MiB pages and per-class
segment space for a few instances of each class. Default regions now use
small pages, and a class's segments in a default region start at 1 KiB
and double up to 16 KiB. Freeable regions keep their page and segment
policy.
2026-10-19 03:31:48 -05:00
Chris Tate f75b7d8aab Use a computed test name as given when the function's name is unknown
A test name computed at run time took the function's name when it was
empty, which needs that name at compile time. For a function the
program received as a parameter, such as a helper that registers
`test(\`case ${label}\`, body)` for its callers, the call was refused,
where it compiled before. The name is now used as given in that case,
and the limitations page notes that an empty one stays empty, where
Node uses the function's name.

The test-name-forms fixture registers tests through such a helper and
compares them with Node, plain and with SCRIPTC_SAN=1. Without this
change it fails to compile.
2026-10-19 03:22:05 -05:00
Chris Tate 8802765a1c Destroy an idle pooled http socket that receives data
Node 24's http.Agent installs a read guard on each socket it puts in
freeSockets: bytes that arrive while the socket is idle destroy it, so
they are never read as the response to the next request that reuses the
socket. scriptc discarded the bytes and kept the socket in the pool, so
the next request reused it (reusedSocket true, one connection) where
Node dials a new one.

The client connection's data hook now destroys the socket when it is in
an agent's free list. The socket leaves freeSockets and
totalSocketCount, and the next request through the agent opens a new
connection, as in Node.

Corpus 6200 covers an idle socket that receives a stray response.
2026-10-19 03:19:31 -05:00
Chris Tate 8e9e2136cf Merge main 2026-10-19 03:18:36 -05:00
Chris Tate 4eb3dd8def Merge main 2026-10-19 03:15:05 -05:00
Chris Tate c2b0db5778 Print iterators and iterator helper objects as Node does (#1377) 2026-10-19 03:04:50 -05:00
Chris Tate 192ea32474 Read generator tags through a pointer the generator runtime installs
scr_json.c and scr_inspect.c called scr_gen_tag, which lives in
scr_async.c. Libraries and the runtime's unit tests link those units
without scr_async.c, so their links failed on the undefined symbol.

Both now read the tag through scr_gen_tag_reader, defined in scr_json.c
and set at startup by scr_async.c. A generator capsule only exists when
the generator runtime is linked, so the pointer is always set when read.
2026-10-19 02:40:22 -05:00
Chris Tate 6834cf2886 Wait before returning parked workers' memory (#1378) 2026-10-19 01:38:52 -05:00
Chris Tate e63d0e51c9 Wait before returning parked workers' memory, and discard one waiting arena to claim one
When the last running worker parks, the trim helper now waits
SCR_SA_DISCARD_DELAY_MS (100 by default) before it returns the parked
workers' pages, and stops if a worker starts meanwhile. A program that
exits within the delay, such as a one-shot type check whose checkers finish
just before it reports and exits, no longer returns pages during its last
milliseconds. A thread that finds every arena claimed discards one waiting
arena and claims it, instead of running every waiting discard.
2026-10-19 01:14:20 -05:00
Chris Tate 3f51d2f5b1 Look up runtime features in sorted sets instead of comparison chains (#1368) 2026-10-19 01:11:34 -05:00
Chris Tate 496cf577c7 Print iterators and iterator helper objects as Node does
console.log and util.inspect of a generator object, an iterator helper,
or an array or string iterator were refused, and printing a Map or Set
iterator threw "util.inspect of a dynamic Array Iterator is not
supported yet".

Compiled generators now carry the tag their object prints with:
"Generator" or "AsyncGenerator" by default, "Iterator Helper" for the
helpers, "Array Iterator" and "String Iterator" for the iterators the
compiler builds as generators. The spawn wrapper sets it through the new
scr_gen_set_tag; an adapter over another generator copies that
generator's tag (scr_gen_copy_tag), and generators that stand in for an
object of another class, such as a Set iterator adapted into an
iterator-typed slot, keep refusing to print. They print as Node's
Object [<tag>] {} at any depth, and Object.prototype.toString answers
[object <tag>].

The runtime's native iterators print as Node does: Object [Array
Iterator] {} and Object [String Iterator] {}, and for Map and Set
iterators the entries the next steps would produce, without advancing,
under [Set Iterator], [Set Entries], [Map Iterator] or [Map Entries],
as [Object [Set Iterator]] past the depth limit and {  } when none are
left. Object.prototype.toString answers [object Set Iterator] and the
other tags.
2026-10-19 01:10:39 -05:00
Chris Tate 96d3a0f5e4 Carry AsyncLocalStorage stores into timer, immediate, tick and microtask callbacks (#1365) 2026-10-19 01:01:02 -05:00
Chris Tate a7e30318a3 Sort ShapeHost's members and the lowerer's mode options by name (#1370) 2026-10-19 00:59:31 -05:00
Chris Tate 1b27822d64 Merge main 2026-10-19 00:47:00 -05:00
Chris Tate d6952e4fe2 Walk Map and Set iterators inside iterator helper objects (#1367) 2026-10-19 00:43:25 -05:00
Chris Tate 60535fd09c Accept test and describe calls without a name, and settle skip and todo as Node does
node:test changes, matching Node 24.22:

- test, it, describe, suite, their skip/todo/only twins and t.test take
  Node's (name?, options?, fn?) forms: test(fn), test(options, fn),
  test(options) and test() compile, as does describe(fn) and
  describe(options, fn). The fallback declarations add the overloads.
- A missing or empty name is the function's name, or "<anonymous>", as
  Node's Test constructor completes it. The name is the function
  expression's or declaration's own name, or the binding name of a
  const arrow or anonymous function; a name that is empty at run time
  takes the same default.
- With both skip and todo, the test is skipped, and the directive message
  is the skip string, else the todo string. The skip and todo method
  twins override the option of the same name, so test.skip("x",
  { skip: "msg" }) reports a bare SKIP and it.skip("x", { todo: "msg" })
  reports "msg". Before, the last option set the mode.

The limitations page notes that a thrown object or array that is not an
Error reports [object Object] in the failing section.

The test-name-forms fixture compares the names, directives and counts
with Node, plain and with SCRIPTC_SAN=1.
2026-10-19 00:21:03 -05:00
Chris Tate b1395256d2 Initialize the asyncContext runtime feature in declaration order
The runtime feature scanner's result is serialized as JSON, and the
self-hosted scanner lays its record out in the interface's field order.
asyncContext was initialized before dynAsync, while the interface declares
it after, so the self-hosted scanner's JSON put the key elsewhere than the
TypeScript one. The initializer now follows the interface order.
2026-10-19 00:17:31 -05:00
Chris Tate 86e8a09d86 Sort ShapeHost's members and the lowerer's mode options by name
Changes that add a backend host hook or a lowering option appended it at
the end of ShapeHost, LowererMode and the Lowerer constructor's option
assignments, so concurrent additions edited the same lines. ShapeHost's
and LowererMode's members are now sorted by name with their comments, and
the constructor assigns the plain mode options in name order, followed by
the values derived from them in their previous order. Type-only and
reordering changes: the emitted LLVM is unchanged.
2026-10-19 00:14:59 -05:00
Chris Tate 9e9d467c69 Look up runtime features in sorted sets instead of comparison chains
scanRuntimeFeatures matched libCall names with long `fn === "a" ||
fn === "b"` chains. Adding a case at the end edited the previous last line
to add `||`, so changes that added cases to the same chain always
conflicted. Each chain is now a sorted module-level set (typed as IrLibFn,
so a misspelled name fails to compile), with prefix lists where a chain
also matched by prefix. The intrinsic method chains become sets too.

The features are unchanged: for every libCall in LIB_FN_SIGS and each
string, array and typed-array method the scan tests, the old and new
moduleRuntimeFeatures agree.
2026-10-19 00:08:12 -05:00
Chris Tate 05f2dd5859 Walk Map and Set iterators inside iterator helper objects
A helper object over a Map or Set iterator that is stored and consumed
later (const it = set.values().map(f); for (const x of it) ...) stepped
the runtime's checked iterator through an adapter generator, boxing each
value and resuming two generators per element: 5.1x Node's time on a
100k-element Set.

A stage on m.keys(), m.values(), m.entries() (or a Set's), or on
Iterator.from(set) or Iterator.from(map), now walks the collection inside
the helper's own generator. The walk steps the runtime's native iterator
by entry index through the new scr_dyn_collection_iterator_index and
reads each key or value with the typed intrinsics. The native iterator
keeps the walk live and ends the collection's iteration when it is
exhausted or released, so an abandoned or returned helper leaves the
collection free to compact. Other helper sources over a Map or Set, and
Iterator.from over a Set or Map, use a typed generator over the
collection built the same way.
2026-10-19 00:03:25 -05:00
Chris Tate d4e614a51c Count each statement once in coverage however often it lowers (#1366) 2026-10-18 23:38:30 -05:00
Chris Tate 6b14ce8833 List bench/thread-slots among the zero-cost gate's dynamic workloads (#1364) 2026-10-18 23:36:46 -05:00