Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate 3999311319 Give the discard test's trim run an array of its own
check_discard ran trim_allocate again for its returned-regions case, which
writes the blocks into trim_blocks, the array check_trim had freed. Where
glibc maps an 8 MB block by itself (its adaptive mmap threshold, which the
allocator's heap policy leaves alone unless transparent huge pages are in
madvise mode), the free unmaps it and the worker thread's first write
faults. The case now allocates the array again and frees it.

The check that a worker takes the free blocks set aside compared the
highest block it carved with that of the worker before it, which took its
blocks from free lists below the slice's frontier, and it passed when a
worker never took them. Each run now counts the blocks it allocates past
the highest block of the class so far: at most the bump room the slice had
left, against all of them when a worker does not take the free blocks. The
residency bound after a discard allows for pre-fault requests still in
flight.
2026-10-18 01:30:10 -05:00
Chris Tate 8a2fbeec35 Give querystring.parse results a null prototype
querystring.parse and querystring.decode build their result with a null
prototype, as Node does: util.inspect prints the [Object: null prototype]
base, Object.getPrototypeOf answers null, ToPrimitive throws a TypeError,
and Object.prototype methods are not reachable through the result.
2026-10-18 01:26:35 -05:00
Chris Tate 6cdff93888 Merge main 2026-10-18 01:24:45 -05:00
Chris Tate 53a8b0af56 Window long source lines in diagnostic code frames (#1260) 2026-10-18 01:23:48 -05:00
Chris Tate 5d56b57a80 Read .groups on matchAll rows in JavaScript callbacks
An inline JavaScript callback receives a matchAll row as a dynamic
array. The groups projection now reads its captures through dynamic
index reads, and throws Node's TypeError for null and undefined rows.
Rows kept by filter, slice, toReversed and toSorted, or picked by find,
findLast and at, trace to the drain's regex; a reassigned callback
parameter does not. { groups } patterns serve dynamic rows and pattern
parameters. The groups record has a null prototype.

maybeNarrow takes the receiver's declared shape from its non-nullable
type, so a JavaScript read of a nonparticipating group keeps its
undefined arm instead of narrowing to string.
2026-10-18 01:22:45 -05:00
Chris Tate 9750d3994a Merge main 2026-10-18 01:21:52 -05:00
Chris Tate 9498d93c52 Merge main 2026-10-18 01:18:53 -05:00
Chris Tate aa9c6d2be9 Answer in for class methods and accessors on instances with runtime prototypes
`"method" in instance` on a class instance held as `any` answered false
for the class's methods, getters and setters when the program also uses
instanceof or Object.getPrototypeOf on the class. Those uses attach the
class's prototype data to the instance's snapshot, and that object holds
only the methods the program reads through the prototype; the literal-key
`in` then searched it instead of the class.

When a literal key names a method or accessor of a boxed class with
prototype data, the class dynamic-dispatch pass now evaluates `in`
through the computed-key form, which answers class members from the class
itself.
2026-10-18 01:17:39 -05:00
Chris Tate 98cbdb9a4f Merge main 2026-10-18 01:16:36 -05:00
Chris Tate d60978acb2 Match Node 24 in util.format, util.types and util.inspect (#1229) 2026-10-18 01:16:04 -05:00
Chris Tate c533a6bb7d Merge main 2026-10-18 01:06:58 -05:00
Chris Tate 0309ff63cb Window long source lines in diagnostic code frames
renderDiagnostics printed every frame line whole and rebuilt the
file's line index for each diagnostic. On minified sources with
thousands of diagnostics the joined report exceeded V8's string length
and `scriptc coverage` crashed with RangeError: Invalid string length.
Frame lines longer than 400 characters now show a 400-character window
around the diagnostic's column, with "…" where text is cut, and the line
index is built once per file. Every diagnostic is still rendered.

Closes #201
Co-authored-by: Max B <32987678+techfreaque@users.noreply.github.com>
2026-10-18 01:00:47 -05:00
Chris Tate 54ab14d604 Keep declared methods of generic class instances after a computed-key write (#1215) 2026-10-18 01:00:16 -05:00
Chris Tate dc1cb7c1d9 Merge main 2026-10-18 00:53:58 -05:00
Chris Tate b06e06f871 Merge main 2026-10-18 00:53:19 -05:00
Chris Tate 8077a9bfff Add DisposableStack (#1248) 2026-10-18 00:50:48 -05:00
Chris Tate 3f84d6475d Merge main 2026-10-18 00:50:24 -05:00
Chris Tate b15226075e Settle async generator completions on Node's microtask turns (#1251) 2026-10-18 00:48:44 -05:00
Chris Tate add07dba3d Return a worker's memory when it exits without sharing any of it
On Linux, a worker thread that exits without having made any object it
allocated reachable from another thread gives back all of its memory: the
pages it carved, including ProgramLifetime instances and other objects it
never freed, and the system blocks it still holds.

A worker carves past each slice's frontier while the arena's free lists and
returned regions are set aside, takes from those free lists only once a
slice has no bump room left, and records the system blocks it allocates.
Messages and intern tables that share published graphs by pointer, and the
process-wide key arrays of typed records, mark the thread as sharing; so
does a remote free of one of its blocks. When a worker that did not share
parks, the trim helper returns its pages, frees its system blocks, and
restores the arena to its state at the claim, with the free blocks the
worker took free again. A worker that shared keeps its memory as before.

SCR_SA_DISCARD=0 keeps every worker's memory; SCR_SA_DISCARD=poison makes
the discarded pages inaccessible instead, for tests.
2026-10-18 00:46:59 -05:00
Chris Tate 58e6396a1d Merge main 2026-10-18 00:42:37 -05:00
Chris Tate 7804e62041 Merge main 2026-10-18 00:39:44 -05:00
Chris Tate d6bb761ede Merge main 2026-10-18 00:37:03 -05:00
Chris Tate 421e98f5a9 Merge main 2026-10-18 00:31:04 -05:00
Chris Tate b64ef0a8bf Read fields of a class instance asserted to a wider union (#1254) 2026-10-18 00:29:05 -05:00
Chris Tate 6774fe5f08 Merge main 2026-10-18 00:28:33 -05:00
Chris Tate 86f5bc34bc Merge main 2026-10-18 00:26:05 -05:00
Chris Tate 5eb65cf64e Merge main 2026-10-18 00:24:03 -05:00
Chris Tate 27f06f41db Carry Hash and Hmac values through unknown and any slots
A Hash or Hmac that flows into an unknown or any slot, or into untyped
JavaScript code, is now a native handle (Hash or Hmac) instead of a
compile error. The handle keeps the value's identity, so a cast back to
Hash or Hmac answers the same value. update(), digest() and Hash's
copy() dispatch through it, called directly or read as values: update
is one function for Hash and Hmac and digest is one per class, as in
Node 24.22, and update() answers the receiver. The stream and event
members throw a coded SC2020 error there, as do the methods called with
another receiver; other names read as undefined.

The to-dyn conversion boxes a Hash or Hmac through
scr_crypto_hash_to_dyn, which installs the handle ops on first use, so
programs that never box one link none of the handle code.

A native handle read through `as any` now dispatches its method calls
through the handle. Calling copy() on an Hmac throws Node's TypeError
("mac.copy is not a function") after the arguments are evaluated,
instead of the method-call fence.

Corpus 5768 and 5769 cover the TypeScript and JavaScript forms against
Node 24.22, and errors.test.ts covers the SC2020 cases. corpus-costs.json
records their plain and sanitized costs.
2026-10-18 00:22:29 -05:00
Chris Tate bce093c9fd Keep JavaScript lets open when a later object literal adds properties (#1252) 2026-10-18 00:19:23 -05:00
Chris Tate 0f50a7be57 Lower mapped types over a generic function's type parameters
Inside a monomorphized generic body the checker keeps mapped types over
the function's own type parameters symbolic. The instance binds those
parameters, so { [K in keyof S]: T } now maps each property of the bound
type through the template evaluated at that key, covering indexed
templates (S[K], S[K]["_output"]) and conditional ones with infer
(S[K] extends Schema<infer T> ? T : never). Conditional types over bound
parameters evaluate the same way elsewhere in the body.

Closes #886
Co-authored-by: Malek Mohamed Noureldean Elkssas <87043730+malekelkssas@users.noreply.github.com>
2026-10-18 00:16:55 -05:00
Chris Tate 3dcb8a1a29 Lower Date.prototype.toJSON with Node's null for invalid dates
d.toJSON() now answers the toISOString() string for a valid date and
null for an invalid one, as a string | null value although the lib
types say string. Unannotated bindings, object and array literal
entries initialized by it keep the null, reads of such bindings stay
checked, and JSON.stringify serializes the null instead of narrowing
it to string.

Closes #935
Co-authored-by: phall <25888173+phall1@users.noreply.github.com>
2026-10-18 00:12:05 -05:00
Chris Tate 6f9acfd5f8 Read fields of a class instance asserted to a wider union
A class instance that a type assertion widens to a union, such as
(concrete as A | B).value, still lowers as that class's instance. The
shared-property lowering now reads the field or calls the getter on the
known class instead of refusing the receiver for not being a tagged
union.

Closes #49
Co-authored-by: Patrick Wozniak <4481570+patlux@users.noreply.github.com>
2026-10-18 00:08:20 -05:00
Chris Tate ca23c11d10 Add the cstring class to library profiles (#1253) 2026-10-18 00:04:01 -05:00
Chris Tate 298e67443d Match Node 24 in URLSearchParams, querystring, string_decoder and os (#1228) 2026-10-18 00:01:40 -05:00
Chris Tate 87abacc400 Merge main 2026-10-18 00:01:05 -05:00
Chris Tate b6cfe07320 Merge main 2026-10-17 23:57:47 -05:00
Chris Tate 044b7ae42c Regenerate the surface manifest 2026-10-17 23:42:51 -05:00
Chris Tate 370bf5962d Add the cstring class to library profiles
Library exports and callbacks passed text only as a pointer and length.
profile_format 2 adds the cstring class, a NUL-terminated `const char *`
(an i32 pointer in Wasm reactors), for export parameters, export results
and callback parameters. Parameters are copied in and decoded as UTF-8,
with U+FFFD for malformed bytes; a NULL pointer is the SC4012
host-contract trap. Results live in the result arena like string
results. A result or callback argument containing U+0000 traps instead
of being truncated.

Closes #615
Co-authored-by: ailuoku6 <23209876+ailuoku6@users.noreply.github.com>
2026-10-17 23:40:06 -05:00
Chris Tate db18d45dd4 Merge main 2026-10-17 23:39:11 -05:00
Chris Tate b072969f3b Name the rejected value in the Object prototype TypeError (#1250) 2026-10-17 23:37:12 -05:00
Chris Tate 62bf38524d Merge main 2026-10-17 23:34:58 -05:00
Chris Tate 924e485b13 Merge main 2026-10-17 23:34:22 -05:00
Chris Tate ccd65e22c2 Merge main 2026-10-17 23:31:28 -05:00
Chris Tate 1fe191f407 Merge main 2026-10-17 23:27:17 -05:00
Chris Tate 23ce40cace Keep JavaScript lets open when a later object literal adds properties
An untyped JavaScript `let` initialized with an object literal took the
initializer's record shape. A later assignment of an object literal with
more properties (`state = { _tag: "PullLeft", right }`, as in Effect's
Stream.zip) stored the object, but reading the added property failed with
SC1090. Such a binding now uses checked-dynamic storage, as it already does
when the program adds a property through the binding, so the added
properties read back and missing ones read as undefined, as in Node.

Closes #937
Co-authored-by: phall <25888173+phall1@users.noreply.github.com>
2026-10-17 23:25:06 -05:00
Chris Tate a3a4c72a71 Merge main 2026-10-17 23:24:41 -05:00
Chris Tate 97f2875f3f Merge main 2026-10-17 23:21:38 -05:00
Chris Tate aa19f948c5 Settle async generator completions on Node's microtask turns
An async generator that fell off its end, with a return type other than
void, settled its last next() one microtask turn after Node, so for await
loops over it also ended a turn late. The appended implicit return now
marks the completion as settled, like a bare `return;`.

return() at a suspended yield ran the generator's finally blocks before
awaiting the return value. The request now awaits the value first and
resumes the body on the next turn, and the completion that follows settles
without a further turn, also when a finally block yields or awaits first.
An explicit `return x` reached during that unwind still takes its own turn
to await x.

Array.fromAsync accepts typed async generator sources: each next() result
is awaited and its value used as is, a throwing or rejecting mapping
function closes the generator, and a rejected next() rejects the result on
the turn Node rejects it.
2026-10-17 23:19:15 -05:00
Chris Tate 982316d9e6 Merge main 2026-10-17 23:18:09 -05:00
Chris Tate f5c87fb191 Merge main 2026-10-17 23:15:19 -05:00