Commit Graph
2075 Commits
Author SHA1 Message Date
Chris Tate 87565abc16 Merge main 2026-10-26 20:57:59 -05:00
Chris Tate 1245b18469 Search ASCII strings from a start index without index conversion
String.prototype.indexOf took its byte-search fast path on a proven-ASCII
receiver only when the start index was not positive. A positive start went
through the general path: ToIntegerOrInfinity, the surrogate-needle check,
and UTF-16 to byte conversions in both directions, although byte offsets
are UTF-16 indexes in an ASCII string. The fast path now covers every start
index: a positive start truncates and clamps to the length, where only the
empty needle is found, and the search runs from that byte offset.
2026-10-26 18:22:22 -05:00
Chris Tate 7e225c5d87 Merge main 2026-10-26 17:33:06 -05:00
Chris Tate 3c83fcb89c Allocate object groups that die together in arenas (#2348) 2026-10-26 16:54:31 -05:00
Chris Tate c5e8a876ec Store ordinary objects as a layout plus value slots (#2353) 2026-10-26 16:18:38 -05:00
Chris Tate 133551d76e Build node:zlib on the zlib Node bundles
The runtime vendored zlib 1.3.1 and archived it at -Os, and host C-lane
builds linked the system libz, so deflate, raw deflate and gzip output
differed from Node's at every level. The vendored copy is now Node
v24.22.0's deps/zlib (Chromium's zlib, 1.3.2.1-motley-285e94b), built at
-O3 with the units and defines Node's zlib.gyp selects per target: the
SSSE3 Adler-32 and SSE2 inflate and slide-hash code on x86-64, the NEON
and ARMv8 CRC-32 code on arm64, and the portable code on wasm32. The SIMD
units check the CPU at run time and give the same output, so compressed
bytes match Node's on every target.

zlibInputs (runtime-pack-common/scripts/zlib-inputs.mjs for the runtime
packs, vendor-inputs.ts for Zig cross builds and the sanitized runtime)
holds the per-target table, and a unit test keeps the two identical.
Every C-lane driver now links the vendored objects instead of -lz.
Chromium's BSD license ships beside zlib's in the runtime packs.

Corpus 8405 compares compressed bytes with Node across levels,
strategies, memLevel, windowBits, dictionaries and flushing streams;
8406 covers chunked inflation with small input pieces and output chunks.
2026-10-26 15:12:51 -05:00
Chris Tate f12b2b041d Cheaper trial deletion in the cycle collector 2026-10-26 14:34:49 -05:00
Chris Tate 9c7d552fdc Link the arena runtime into the runtime C tests 2026-10-26 13:26:29 -05:00
Chris Tate 68e2556fda Merge main 2026-10-26 12:24:22 -05:00
Chris Tate 18755e0599 Allocate object groups that die together in arenas 2026-10-26 11:41:21 -05:00
Chris Tate 6dbdf39120 Track a stream's 'data' listeners instead of looking them up per chunk (#2344) 2026-10-26 11:38:04 -05:00
Chris Tate de6cdf1156 Add the shape overlay: typed JavaScript from analysis-proven facts (opt-in) (#2176) 2026-10-26 11:33:47 -05:00
Malte UblandClaude Opus 5.5 dc1e8acc1d Store ordinary objects as a layout plus value slots
A checked object (SCR_DYN_OBJ) now starts in layout mode: v.obj.layout names
its key sequence, a node of the thread's interned transition tree, and
v.obj.slots holds one 8-byte value per key, where every property used to take
a 48-byte entry. All properties of a layout-mode object are writable,
enumerable, configurable data properties. An accessor, a non-default
attribute, a delete, more than 64 keys, spent layout budgets, publication to
other threads, or any code that reads entries directly moves the object to
dictionary mode (today's entries) for good, so unported paths stay correct.

Layouts replace the dynamic tier's lazily computed hidden classes (ScrJsShape)
and keep their budgets. Inline caches key on the layout pointer and read the
slot directly; JSON.parse builds objects along layouts; stringify, key
enumeration, release, trace and the runtime read cache read slots in place.
The emitted index-signature checks read members through runtime accessors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:18:53 -07:00
Chris Tate e7f3ab29c2 Track a stream's 'data' listeners instead of looking them up per chunk
Each chunk a readable side delivered looked up its 'data' listeners by
name twice: once to decide whether a flowing stream emits directly, and
again in emit('data'), which scans the stream's event names even when a
piped stream has no 'data' listener at all. The stream now keeps a
data_listeners flag, set from the emitter registry's empty/nonempty
transitions through scr_emitter_observe, and reads it on those paths;
emit('data') is skipped when the stream has no 'data' listener.
2026-10-26 10:59:32 -05:00
Chris Tate 09a73ab6ed Fix Array.prototype calls on native arrays, typeof on ambient names, and net connections to Unix sockets (#2326) 2026-10-26 10:56:58 -05:00
Chris Tate d5dc896576 Merge main 2026-10-26 10:53:46 -05:00
Chris Tate 928c30e908 Bind thisArg in the array callback methods of dynamic arrays
forEach, map, filter, some, every, find, findIndex, findLast,
findLastIndex and flatMap on a dynamic array, and through
Array.prototype.<method>.call on a native array, called the callback
without its thisArg, so `this` in the callback was not the second
argument. These methods now bind `this` to the thisArg, or undefined
when there is none, once per call, around all of the method's callbacks.
2026-10-26 10:19:57 -05:00
Chris Tate 6a8650be5f Throw the async generator depth RangeError through scr_throw_error_msg 2026-10-26 08:22:47 -05:00
Chris Tate 238cb0e7cd Free sanitized fibers' fake stacks 2026-10-26 07:56:46 -05:00
Malte UblandClaude Opus 5.5 7ac0542aa9 Read ordinary objects' properties through accessors outside the object core
Runtime units outside the object core (scr_json.c, scr_js_object.c) read and
adjusted SCR_DYN_OBJ entries in place: v.obj.len, v.obj.entries[i] and the
48-byte ScrDynEntry fields. The single object model's generic objects
(stage S3) change that storage, so these units now go through own-property
accessors in scr_dyn.h:

- scr_dyn_obj_count, scr_dyn_obj_key_at, scr_dyn_obj_value_at and
  scr_dyn_obj_prop_at (a ScrDynProp view: key, value, getter, setter and
  attributes) walk the properties in creation order;
- scr_dyn_obj_index_of finds one by key;
- scr_dyn_obj_set_attrs_at restricts a member a builtin object just added;
- scr_dyn_own_symbol_prop and scr_dyn_copy_prop_at wrap the symbol-entry
  and copy primitives.

The accessors are inline over today's entries, so behavior is unchanged.
21 units move (inspect, assert, util compare, console, http, http2, fetch,
tls, fs, os, lib, array, message, publish, region, symbol, worker events,
handle properties, builtin values, declaration adoption). A package test
keeps entry storage reads inside the core.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:39:34 -07:00
Chris Tate fe244085d4 Run native async generators as stackless coroutines
An async generator whose body suspends only at its own awaits and yields
now compiles to an LLVM switched coroutine, as async functions of that
kind already do, instead of running on a fiber with its own stack. Its
steps run on the stack that resumes it (the loop's after an await), its
awaits suspend through scr_coro_await_prepare and scr_coro_hop_prepare,
and its yields store OUT through scr_coro_yield_* before suspending.

- scr_async_gen_new_stackless creates the generator. The stackless hop
  settles an async generator's state as the fiber hop does, and a step
  that would start below the stack guard throws a RangeError, which
  rejects its request.
- Calling an async generator's next, return or throw no longer counts as
  parking the caller: they queue a request and run the body to its next
  suspension, which returns to the caller. Bodies that consume async
  generators can now be stackless too.
- An untyped await (async.awaitDyn) has a native coroutine form,
  extracted by scr_coro_await_dyn_value, so async bodies that await
  untyped values can be stackless.
- An async generator's return through a finally region takes its await
  turn through the coroutine when the body is one.

Debug builds, libraries, worker programs and Windows executables keep
fibers, as for stackless async functions.
2026-10-26 07:28:50 -05:00
Chris Tate a59df23c71 Merge main 2026-10-26 07:21:12 -05:00
Chris Tate b221a13369 Reuse the completion callback a stream passes to its option callbacks (#2323) 2026-10-26 07:00:49 -05:00
Chris Tate de17158254 Merge main 2026-10-26 06:51:26 -05:00
Chris Tate d98fce9e27 Merge main 2026-10-26 06:45:43 -05:00
Chris Tate 7ae99f63bc Give stream option callbacks Node's callback identities
Node passes a stream's one onwrite function to every _write and _writev
call, and a new function to every _transform call. A write or writev
option callback now receives the same closure on every call
(scr_stream_write_closure), kept or not, so a Set, WeakSet or WeakMap
finds it again. A reused transform, flush, final, construct or destroy
callback is first forgotten by weak collections, as at its free, so a
WeakSet or WeakMap that recorded the previous call's callback does not
find it on the next call.
2026-10-26 05:52:57 -05:00
Chris Tate b8aaa5985a Merge main 2026-10-26 05:23:00 -05:00
Chris Tate 4553941d84 Merge main 2026-10-26 04:25:15 -05:00
Chris Tate c66ce24a73 Fix Array.prototype calls on native arrays, typeof on ambient names, and net connections to Unix sockets
Fixes #2116
Co-authored-by: 4ndual <44326858+4ndual@users.noreply.github.com>
Fixes #2134
Co-authored-by: dolphin278 <1130473+dolphin278@users.noreply.github.com>
Fixes #2142
2026-10-26 03:57:37 -05:00
Chris Tate 2ad699c98a Compile classes that extend Array (#2059) 2026-10-26 03:57:16 -05:00
Chris Tate 0ba138b57e Merge main 2026-10-26 03:25:54 -05:00
Chris Tate acf19e2652 Merge main 2026-10-26 02:53:26 -05:00
Chris Tate ca3217fe99 Reuse the completion callback a stream passes to its option callbacks
Each call of a typed or untyped write, writev, transform, flush, final,
construct or destroy option callback allocated a closure and a box holding
the stream for its `callback` argument. The emitted thunks now take it
from scr_stream_done_closure, which keeps the last one on the stream state and
hands it out again while the stream holds its only reference and it has
no own properties. A callback the user kept (stored, or passed to
setImmediate or a promise) is not reused: the next call gets a new
closure, and the kept one keeps its own stream reference.

The cached closure holds the stream, so the stream drops it when it
emits 'close' and traces it, which lets the cycle collector reclaim a
stream dropped before it closes.

RUNTIME_ABI_VERSION goes from 46 to 47 for scr_stream_done_closure.
2026-10-26 02:48:03 -05:00
Chris Tate 055d1f9682 Fix eight compiler and type bug reports
Co-authored-by: dolphin278 <1130473+dolphin278@users.noreply.github.com>
Fixes #2139
Fixes #2131
Fixes #2132
Fixes #2133
Fixes #2140
Fixes #2141
Fixes #2159
2026-10-26 01:50:59 -05:00
Chris Tate 2d24d6ad80 Merge main 2026-10-26 00:41:23 -05:00
Chris Tate feb4501013 Keep a candidate's generation when a limited cycle pass reaches it
A limited pass (scr_cyc_collect_set, which freeing a LifetimeRegion runs
over the objects the region owned) takes only the candidates in its set.
Its walk can still reach other candidates, and one that is referenced from
outside is blackened by scan and promoted to the next generation while it
keeps its slot in its old generation's buffer. scr_cyc_on_dead finds a
candidate's buffer by its generation, so when that object died it removed
another buffer's entry and left its own slot pointing at the freed block,
which the next pass then read.

Such a candidate is no longer promoted: it keeps the generation of the
buffer it is in until a pass takes it.
2026-10-26 00:24:05 -05:00
Chris Tate 21bce9e4fa Merge main 2026-10-26 00:21:24 -05:00
Chris Tate fc88e5c474 Return the freed heap pages to the system after the frontend is disposed (#2306) 2026-10-25 23:24:57 -05:00
Chris Tate 307f9e0009 Keep one heap-trim test block per four pages of any size
The test kept one live 48-byte block every 1,000 allocations, which on a
system with 64 KiB pages leaves a live block on every page, so no page held
only free blocks and the resident-memory check failed. It now keeps one
block per four pages of blocks, from the system's page size.
2026-10-25 22:47:07 -05:00
Chris Tate b01a86f557 Keep one identity through checked casts of dynamic values (#2296) 2026-10-25 22:41:00 -05:00
Chris Tate 5b672087c6 Return the freed heap pages to the system after the frontend is disposed
The native compiler's frontend-dispose sweep frees about 450 MB on a cold
tsc-ts build, but the size-class allocator keeps the freed blocks on their
classes' free lists, which only later blocks of the same size reuse. Code
generation allocates other sizes, so its pages stacked on top of the freed
heap and set the build's peak resident memory.

- scr_heap_trim (scr_alloc.c) returns the pages of the calling thread's
  size-class slices that hold only free blocks (MADV_DONTNEED) and the
  system allocator's free pages (malloc_trim). In the single-threaded
  allocator the blocks of a returned page leave the free list and the
  class carves past its frontier instead; the remaining free blocks are
  relinked in address order. Worker runtimes return the calling thread's
  arena pages as a parking thread does.
- scr_collect_cycles_phase, the full sweep after a phase's data died,
  calls it; the native compiler calls that sweep when it disposes the
  frontend.
- test_heap_trim.c checks that the pages go back, that the blocks still
  allocated keep their contents, and that new blocks overlap none of
  them, with and without worker arenas (another thread allocating and
  freeing remotely throughout).

Cold tsc-ts build, 12 interleaved rounds: peak RSS 1407 -> 1247 MB
(-160 MB, -11.4%, every round); wall 87.1 -> 85.9 s (median).
2026-10-25 22:36:11 -05:00
Chris Tate d48e060076 Merge main 2026-10-25 22:34:40 -05:00
Chris Tate 7741f24cc8 Reserve the rc word's high half for layout ids (#2299) 2026-10-25 22:10:20 -05:00
Chris Tate 89db5d47f7 Merge main 2026-10-25 22:02:35 -05:00
Chris Tate 00b58e5c7a Map UTF-16 indexes of long non-ASCII strings in constant time, and quick-check normalize (#2294) 2026-10-25 21:59:27 -05:00
Chris Tate 018dbb766c Compute Math functions as V8 does and link libm only when a program needs it (#2287) 2026-10-25 21:26:20 -05:00
Chris Tate 80ae660be6 Merge main 2026-10-25 21:22:05 -05:00
Chris Tate 59e78f298a Name the package declaration and its patch when a declared-type check fails (#2268) 2026-10-25 21:20:34 -05:00
Chris Tate 0b483e8650 Map UTF-16 indexes of long non-ASCII strings in constant time and quick-check normalize
- Index walks (slice, substring, substr, startsWith/endsWith with a
  position, lastIndexOf results) on a non-ASCII receiver of 64 KiB or
  more: once they would have paid for one pass over it, the receiver's
  index entry keeps every unit's byte offset (a 32-bit base per 64 units
  and one byte per unit), and indexes map with two loads. Before that, a
  walk within 64 units of the hot cursor starts from it, without the index
  search. The index entry stays 128 bytes on 64-bit targets.
- lastIndexOf on non-ASCII receivers tests the needle's first byte before
  comparing, as the ASCII path does.
- normalize: a quick check (UAX #15) returns the receiver when every code
  point is inert for the form: unchanged when normalized alone, combining
  class 0, and, in the ranges checked, never the second element of a
  composition. Other text normalizes segment by segment, each starting at
  a starter, so runs of inert code points copy as they are and a lone
  precomposed letter's result is kept per form. The per-code-point facts
  come from unicode_normalize, 256 code points at a time.
2026-10-25 21:04:29 -05:00
Chris Tate 8981b5c0a2 Resolve computed createRequire specifiers at run time (#2079) 2026-10-25 20:29:51 -05:00