Commit Graph
3145 Commits
Author SHA1 Message Date
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 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 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
Chris Tate dd44148528 Merge main 2026-10-17 22:52:07 -05:00
Chris Tate ed817c017d Name the rejected value in the Object prototype TypeError
Object.setPrototypeOf and Object.create threw "Object prototype may only be
an Object or null" without the value Node appends. The message now ends
with the value's string form, as in Node: "...: 7", "...: undefined",
"...: Symbol(tag)".

A prototype or receiver that is a valid object of a kind these functions
do not handle yet (a function or Map prototype for Object.create, a Map
prototype or receiver for Object.setPrototypeOf) threw the same TypeError.
It now throws an Error that says the operation is not supported for that
value.
2026-10-17 22:50:45 -05:00
Chris Tate 2f93c46d2a Merge main 2026-10-17 22:49:18 -05:00
Chris Tate 32061e8ba3 Add Float16Array
Float16Array is a native typed-array kind with IEEE 754 binary16 storage.
Stores round the number to nearest-even in one step from the double, as
Node does, including overflow to Infinity, subnormals, -0 and NaN; reads
widen exactly. The runtime converts with integer operations, so no target
needs half-precision hardware or compiler-rt support.

Construction, element reads and writes, ArrayBuffer views, fill, set,
subarray, slice, both sort orders, constructor values and checked-dynamic
storage work as for the other typed arrays. Math.f16round,
DataView.prototype.getFloat16 and setFloat16, and util.types.isFloat16Array
compile.
2026-10-17 22:45:32 -05:00
Chris Tate 43d9b02e8e Merge main 2026-10-17 22:45:12 -05:00
Chris Tate eeffa294e5 Merge main 2026-10-17 22:42:10 -05:00
Chris Tate 82c699b437 Add DisposableStack
new DisposableStack() compiles to a native stack handle. use, adopt, defer,
move, dispose, disposed and [Symbol.dispose] are members of its dynamic
view, and the stack works with `using`. Disposal runs the resources in
reverse order, calls use() resources' dispose methods with the resource as
`this`, and raises a SuppressedError chain when more than one disposal
throws; a single failure is rethrown as thrown. Calls on a disposed stack
throw Node's ReferenceError, and invalid receivers, resources and
callbacks throw Node's TypeErrors.

use() of a class instance registers a function that calls the class's
[Symbol.dispose] method, and returns the instance at its static type; use()
of a union that includes class instances is refused. The stack is
collected with its callbacks when a callback captures the stack.
x instanceof DisposableStack compiles. Handles can expose symbol-keyed
members through a new symbol_get hook.
2026-10-17 22:40:13 -05:00
Chris Tate effe6ed5c2 Merge main 2026-10-17 22:39:14 -05:00
Chris Tate bb04c2112e Merge main 2026-10-17 22:32:25 -05:00
Chris Tate e0998f0172 Prove generic classes acyclic like other classes
Each instantiation of a generic class names the generic class as its base,
and the generic class has no constructor function. The cycle analysis
treated a base without a constructor function as one whose constructor let
`this` escape, so every store in an instantiation's constructor counted as
a write after construction. A generic class could not be declared Acyclic,
and its constructor-only references were never treated as immutable.

A program class without a constructor function runs no code at
construction, so it now lets nothing escape. Runtime classes keep the
conservative answer.
2026-10-17 22:25:55 -05:00
Chris Tate f4daa00d5c Merge main 2026-10-17 21:59:33 -05:00
Malte UblandClaude Opus 5.5 43fb612438 Compile CommonJS requires placed after code that can reach them, and JSON requires
A top-level require binding that earlier code could reach was refused
(SC1013). The reachability scan now follows Node's evaluation order:
hoisted functions and classes run only when reachable code names them, a
class statement runs only its heritage, computed keys, decorators and
static initializers, timer and promise callbacks run after the module
body, and publishing a value through module.exports, exports.n,
Object.defineProperty(exports, ...) or F.prototype.m runs nothing until
reachable code reads it back. This admits the export-first layouts of
semver, glob@7, fs.realpath, on-finished, readable-stream and
tsc-emitted CommonJS.

A const or let binding that code can still reach before its require now
compiles with an initialization flag set right after the require and a
module.tdzCheck before the outermost expression of every reference that
can run earlier, so an early read throws Node's ReferenceError before
any argument evaluates. A reference whose lowering leaves no expression
to check, a var binding (Node reads undefined there) and createRequire
bindings keep SC1013.

require() of a JSON document now answers one parsed object per document,
created in the requiring module's %init prelude unless another module
already created it, so repeated requires return the same object and
mutations are visible to every requirer. Require bindings of JSON
documents are ordinary storage, so destructuring (nested patterns,
defaults, missing keys) copies at the require as in Node.

Nested destructuring of a builtin require binds the members of the
sub-module the key names (const { types: { isPromise } } =
require('util')).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 19:58:21 -07:00
Chris Tate 84b74c0269 Keep declared methods of function-local classes after a computed-key write
observedPrototypeMethod skipped classPrototypeData for function-local
classes, which also marks such a class as needing prototype data, so its
instances got no prototype behind their property bag. A function-local
class value's prototype holds only methods the program replaced there, so
reads of a marked method name on its instances now check for the
property, own or inherited, and otherwise use the declared method, as for
generic class instances. The prototype data is built again, so a method
replaced on the class's prototype stays visible.
2026-10-17 21:50:39 -05:00
Malte UblandClaude Opus 5.5 1e5079a23a Format the array length loop changes and record their order baselines
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 19:44:08 -07:00
Malte UblandClaude Opus 5.5 cf872eaba5 Convert only int32 loop counters from their low half
A uint32 counter converted through `uitofp i32` folds an inlined callee's
index conversion, but the zero extension it adds on x86-64 slowed loops
that only convert the counter for a cold fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 19:43:56 -07:00