Commit Graph
6750 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 20f15f89fd Let implicit JS instances that fall through return undefined
A JavaScript function specialized per call site takes its declared
return type from JSDoc. bcryptjs's `hash` and `compare` document
`@returns {!Promise}` but return nothing when a callback is passed, so
their instances ended with the "the checker proved every path returns"
trap and every callback-style call aborted with SC9002. Non-generic
JavaScript functions already widen such a return with an undefined arm;
implicit instances now do the same.

The widened `Promise<unknown> | undefined` result is then awaited by
TypeScript callers, which was refused: an await of a promise-of-dynamic
or unit union now goes through the dynamic await, which adopts the
promise or hops once for the unit, as Node does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:48:01 -07:00
Malte UblandClaude Opus 5.5 f8fa60afec Link the promise adapter when a boxed class instance holds a promise
Boxing a class instance as a dynamic value (Object.keys, spread,
inspection) boxes its fields, and a promise field goes through
scr_dyn_new_promise_adapting in scr_async_dyn.c. The link-unit scan
followed promises through records, arrays, unions and functions but not
class instances, so `Object.keys(job)` over a class with an optional
`Promise` field failed to link.

The scan now follows a class instance's fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:48:01 -07:00
Malte UblandClaude Opus 5.5 b6637c882d Check record, array and string fields that construction can read early
A field without an initializer that the constructor assigns only after
handing `this` to other code is undefined in Node until that assignment.
Class-typed fields in that position already take the undefined-armed
deferred-init slot whose reads check it. Record, array and string fields
kept a plain pointer slot, so a method the constructor calls first could
read null and crash with a segfault (`this.internal.n` read from a
method before `this.internal = ...`).

Those field kinds now take the same checked slot when a method or
accessor the constructor calls through `this` (transitively, over the
class and its bases) reads them. Fields only exposed in other ways keep
their plain slot, so constructors whose early calls never touch the
field compile as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:48:00 -07:00
Chris Tate ec79cf2789 Run iterator helpers on untyped iterators
In JavaScript, a receiver without an inferred type, such as an untyped
parameter's xs.values(), is the runtime's checked iterator. Its only
method was next(), so xs.values().filter(f), .map(f) and the other
iterator helpers threw "TypeError: ... is not a function" where Node
runs them, and [].values().reduce(f) threw the same instead of Node's
"Reduce of a done iterator with no initial value".

The native array, string, Map and Set iterators now answer the
Iterator.prototype methods. map, filter, take, drop and flatMap make
helper objects (SCR_DYNH_ITERATOR_HELPER) that pull lazily and support
next(), return(), for...of and spreads, print as
"Object [Iterator Helper] {}" and tag as "[object Iterator Helper]".
toArray, forEach, reduce, some, every and find drive the iterator.

The helpers close the iterator they were made from where Node does:
when a callback throws, when take's budget is spent, on return(), and
before the error for an invalid argument. flatMap closes its inner
iterator first. An error from a close that runs because of another
error is dropped. Invalid arguments throw Node's messages, such as
"number 5 is not a function" and "x must be positive".
2026-10-18 19:42:27 -05:00
Malte UblandClaude Opus 5.5 273c20c136 Bump the IR version in main's new stack-observations test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:37:53 -07:00
Malte Ubl 91c2a608b6 Merge remote-tracking branch 'origin/main' into opt/tier-s3-jsval 2026-10-18 17:37:42 -07:00
Malte Ubl 63d04d3a70 Merge remote-tracking branch 'origin/main' into opt/silent-bugs
# Conflicts:
#	tests/harness/errors.test.ts
2026-10-18 17:32:39 -07:00
Malte UblandClaude Opus 5.5 4e72477c55 Answer NaN for ** with a base of ±1 and a non-finite exponent
`1 ** x` with x = Infinity printed 1 where Node prints NaN: the operator
lowered to libm's pow, which answers 1 for pow(±1, ±Infinity) and
pow(1, NaN) (and LLVM folds pow(1.0, x) to 1.0). Such operands now keep
the direct pow call behind an inline guard that selects NaN when the
base is ±1 and the exponent is not finite. A finite literal exponent
(x ** 2) or a literal base other than ±1 (2 ** k) cannot meet those
cases and keeps the bare call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:32:38 -07:00
Malte UblandClaude Opus 5.5 6a2d43b462 Keep declaration-file rest callbacks on their existing paths
Library callback slots such as String#replace's (substring, ...args: any[])
have intrinsic lowerings that inspect the unmapped function type, so only
rest parameters declared in program source take the checked-dynamic
variadic ABI. Found by the zero-cost gate on corpus 6165 after merging main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:30:57 -07:00
Malte UblandClaude Opus 5.5 a1576d7c5f Keep the deferred-class-receiver fixture on a rejected field type
The fixture's class was rejected through a WeakMap-typed field, which
this branch makes legal. A MapIterator-typed field is still refused, so
the class stays rejected and the test still checks that a class reached
only through a receiver probe reports its own diagnostic exactly once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:30:54 -07:00
Chris Tate 7b2b864bdd Place functions too large to inline by their own name (#1283) 2026-10-18 19:30:32 -05:00
Chris Tate 3de2fb56a0 Cancel running subtests when their parent test times out
node:test changes, matching Node 24.22:

- When a test's { timeout } elapses, its subtests that are still running
  are cancelled, the innermost first. Each reports its duration so far and
  fails with "test did not finish before its parent and was cancelled",
  before the parent's result.
- A cancelled subtest's body keeps running unobserved: when it settles it
  reports nothing and leaves the runner's state alone. Before, it reported
  its result after the run's summary.

The timeout-subtests fixture compares the results, counts and failing
section with Node, plain and with SCRIPTC_SAN=1.
2026-10-18 19:28:55 -05:00
Chris Tate 008eb0712c Merge main 2026-10-18 19:13:48 -05:00
Chris Tate 6fd2dedb15 Merge main 2026-10-18 19:04:59 -05:00
Chris Tate 6ab49f33b2 Stringify class instances with JSON.stringify (#1338) 2026-10-18 18:59:22 -05:00
Chris Tate 9414bdc5fe Merge main 2026-10-18 18:58:47 -05:00
Chris Tate aa9289b9d9 Concatenate untyped Buffer lists as Node does (#1272) 2026-10-18 18:46:36 -05:00
Chris Tate 550e00549c Move callSiteReturnType into ir.ts for devirtualization
Lowering may not import ir/validate.ts, and devirtualize.ts imported
callSiteReturnType from it. The function only reads an IrFunction's
types, so it moves next to typeEquals in ir.ts, which both the
validator and lowering import.

The Date formatter fence's hint now lists toUTCString(), which this
branch lowers, so the date-formatters and stdlib-fence snapshots record
the new hint. toString() and toLocaleString() still have no lowering,
and the fixtures keep them.
2026-10-18 18:40:52 -05:00
Chris Tate 707ac5cf77 Carry Hash and Hmac values through unknown and any slots (#1257) 2026-10-18 18:38:52 -05:00
Chris Tate 7d13ac1706 Answer instanceof Writable for Duplex streams as Node does (#1346) 2026-10-18 18:37:49 -05:00
Chris Tate 8d94e5d993 Refuse RegExp and Promise class fields in JSON.stringify
A Date field reads through the class's checked-dynamic property view and
writes its toJSON text, so json-class-fields.ts compiled and matched Node
while the diagnostics test expected a refusal. The Date cases move to
6000-json-stringify-class-instances.ts.

RegExp and Promise fields read through the view but the runtime JSON
traversal has no writer for them: a RegExp field threw at run time where
Node writes {}, and a Promise field failed to link. Both are now refused
at compile time naming the field, also inside a field's arrays, records
and unions, and the fixture covers them.
2026-10-18 18:34:09 -05:00
Chris Tate 1b40157fed Keep pending match metadata in the array's metadata slot
A pending match result now stores its shared match source as a dynamic
handle in ScrArrExt.metadata, next to its start index, instead of in a
separate field with its own release hook. Array teardown already
releases metadata, so scr_arr_ext_free and the publish check keep their
previous code, and a static hello world links the same array code as on
main.
2026-10-18 18:25:56 -05:00
Chris Tate 9239521160 Answer instanceof Writable for Duplex streams as Node does
Node's Writable[Symbol.hasInstance] answers true for any stream with a
writable side, so a Duplex, Transform, PassThrough or zlib stream (and
user subclasses of Duplex) is an instance of Writable without inheriting
from it. `x instanceof Writable` tested only Writable's own interval and
answered false for them. It now also tests Duplex's interval for object,
union and catch-binding operands, and the static folds count Duplex
subclasses. Subclasses of Writable keep the prototype test, as in Node.
2026-10-18 18:15:01 -05:00
Chris Tate cf11fbaaeb WebAssembly W0–W2: free handle tags, a cross-thread clone hook, the WebAssembly types and refusals, and a decoder that reports every module a program can load (#1339) 2026-10-18 18:12:42 -05:00
Chris Tate 4dc25a18c1 Lower iterator helpers on generators and Map and Set iterators (#1342) 2026-10-18 18:11:21 -05:00
Chris Tate 14372c8274 Build "is not a function" errors for Hash, Hmac and KeyObject handles at any length
The Hash/Hmac and KeyObject handle dispatchers formatted "<callee> is not
a function" into a 160-byte stack buffer and passed snprintf's return
value as the message length. A callee text longer than about 140 bytes
made that length exceed the buffer, so the error copied bytes past it:
the message ended in stack contents, and sanitized builds aborted. Both
dispatchers now build the message in a growable buffer, as the shared
dynamic-call path does, so the TypeError carries the whole callee text,
as in Node.

Corpus 5774 calls a missing method on a Hash, an Hmac and a KeyObject
through callee texts longer than 160 bytes, and through a short one, and
corpus-costs.json records its plain and sanitized costs.
2026-10-18 18:09:08 -05:00
Malte UblandClaude Opus 5.5 0ee1205bc4 Keep fs.constants on its own table beside node:constants
Calling the node:constants fs fill from a second site stops that static
from inlining into scr_node_constants and changes every binary linking
node:constants; fs.constants keeps its own table (now with the UV_FS_O_*
Windows-only flags).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:05:10 -07:00
Chris Tate 05b63df116 Check Buffer.concat's length before the elements of an untyped list
Node's Buffer.concat(list, length) validates length (after returning early
for an empty list) before it checks that every element is a Uint8Array.
The untyped-list path checked the elements first, so a bad element and a
bad length threw ERR_INVALID_ARG_TYPE where Node throws the length's
ERR_OUT_OF_RANGE.
2026-10-18 18:05:09 -05:00
Malte UblandClaude Opus 5.5 b8ac5e3f6b Merge origin/main into opt/builtins-npm2
fs.constants as a value now builds its object from the fs group fill that
node:constants added (scr_fs_constants_object, defined after
scr_node_constants so the shared static keeps its place); the fallback
declarations gain the UV_FS_O_* Windows-only flags. Compatibility files
regenerated with pnpm manifest and pnpm node-compat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:03:19 -07:00
Chris Tate 52303baf4b Keep the promise of async generic functions whose body reads a missing element
The instance result of an async generic function whose body returns an
element or index-signature read that can miss is Promise<T | undefined>:
the promise is always present and settles with undefined. The call site
widened the whole promise instead, so the instance and its calls had
different signatures and every call failed to compile.
2026-10-18 18:01:49 -05:00
Chris Tate dd2444fbb6 Keep React Flight's development-only stack parsing out of production builds, and make stack mode cheaper (#1330) 2026-10-18 18:01:06 -05:00
Malte UblandClaude Opus 5.5 f4a8511dca Compile maybe-promise unions, unknown[] rest parameters, and function-valued Maps and Sets
A union may now hold one promise beside data members (`T | Promise<T>`,
`Promise<R | void> | R | void`). The union tag is the `instanceof Promise`
test, and every operation keeps Node's microtask timing: `await` awaits the
promise arm or takes the one non-thenable turn, an async `return` (block
bodies, concise arrows, then handlers) adopts the promise arm after the
function's own finally blocks, `Promise.resolve` returns the promise arm as
is, and `typeof (u as any).then` reads the tag. Data arms that declare
`then`, two promise arms, and function arms beside a promise stay refused
with diagnostics. Inferred JavaScript literals that mix promises and values
keep the checked-dynamic array.

Rest parameters typed `unknown[]` or `any[]` in TypeScript use the
checked-dynamic variadic ABI that JavaScript rest parameters already use,
including spread forwarding (`(...args) => handler(...args)`).

Functions are now Map values, Map keys and Set elements, compared by
closure identity.

Two pre-existing bugs in the same area are fixed: an async lambda in a
`Promise<void> | void` slot got a `Promise<union>` ABI and threw when called
through the slot, and `return cb()` of such a callback in an async function
dropped the promise instead of adopting it.

The retired-engine 2251 and 2790 diagnostics fixtures (tagged templates
with `any[]` rest tags) now compile and match Node, so they move to the
corpus as 6248 and 6249.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:58:57 -07:00
Malte Ubl f18e804ffd Merge remote-tracking branch 'origin/main' into opt/tier-s3-jsval
# Conflicts:
#	packages/compiler/src/backend/llvm/shapes.ts
2026-10-18 15:58:49 -07:00
Malte UblandClaude Opus 5.5 8ec0d9e0b9 Link the emitWarning replacement and extended stdio only where used
The process.emitWarning replacement and the stdin encoding, string data
listener and stdio 'resize' support changed runtime functions every
program with the dyn-async or events unit links (the shared warning
paths, stdin delivery, the process stream object) and grew their
thread-local state. That code now compiles only into new runtime-pack
variants, SCR_WARNING_REPLACE of scr_async_dyn.c and SCR_STDIO_EXTENDED
of scr_events.c, which the link selects when the program assigns
process.emitWarning or reaches setEncoding, a string 'data' listener or
'resize'. The default objects are byte-identical to before, so other
executables do not change. The development and sanitizer builds pass the
same defines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:57:42 -07:00
Malte UblandClaude Opus 5.5 ec4a693091 Map crypto.getRandomValues and fs.constants in the compatibility ledger
crypto.getRandomValues (crypto and webcrypto chapters) is supported with
corpus 6204/6205; fs.constants is partial (the fs group, not the other
platforms' sets) with corpus 6207/6208. Regenerated with pnpm manifest and
pnpm node-compat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:55:27 -07:00
Malte UblandClaude Opus 5.5 5737483a04 Answer typeof for Web constructor globals and their static methods
ky probes `typeof globalThis.AbortSignal === 'function' && typeof
globalThis.AbortSignal.any === 'function'`. In JavaScript sources these
values are identity tokens, so typeof answered "string", and the static
method read refused. Node 24's Web-platform constructor globals and the
static methods of AbortSignal, Response and URL now answer "function",
also inside typeof comparisons, where a fallback-typed receiver narrows to
never after the first guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:55:24 -07:00
Malte UblandClaude Opus 5.5 ffd13d9751 Read fs.constants from the target libc
Only the access-mode and COPYFILE bits of fs.constants lowered; every other
member and the object itself refused. The runtime now builds
node_constants.cc's fs group in its order under the target libc's #ifdef
guards (O_NOATIME and O_DIRECT on Linux, O_SYMLINK on macOS) as a cached
null-prototype object with read-only members. `fs.constants` as a value
reads it, and members beyond the baked literals read from it (undefined
for a constant the target lacks, in JavaScript sources). The fallback
declarations list the full set. The table keeps each name inside its
entry, so the whole table is one data object section GC drops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:55:21 -07:00
Malte UblandClaude Opus 5.5 9e63a937db Test the Buffer brand at run time and alias the buffer module's Buffer
`Buffer.isBuffer` over a Uint8Array-typed value (safe-buffer's
`Buffer.from(...)` result, etag's input) refused at compile time because
the bytes IR shares storage between Buffer and Uint8Array. The view keeps
its Buffer brand at run time, so the test now reads it.

`require('buffer').Buffer`, `const { Buffer } = require('buffer')` and
`buffer.Buffer` through a namespace binding alias the global Buffer (the
module member is the same constructor), so their statics lower like the
global's. A file-scope `var` of the namespace form aliases too when nothing
reassigns it and the require-binding early-reference analysis finds no
earlier reader. `Buffer.from` over a JS-inferred `string | Uint8Array |
undefined` value takes the checked runtime form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:55:18 -07:00
Malte UblandClaude Opus 5.5 8edd2bce8e Add crypto.getRandomValues
nanoid fills its id bytes with `crypto.getRandomValues(buffer.subarray(...))`,
which had no lowering. The WebCrypto global, node:crypto's export and
`webcrypto.getRandomValues` now fill typed views in place and return them
at their own type. Float views, DataViews and other values throw Node's
TypeMismatchError DOMException, views over 65536 bytes QuotaExceededError,
with Node's messages and codes. The fallback declarations gain the
node:crypto export and webcrypto's member (the crypto global stays
unknown in builds without @types/node).

The runtime entry points are new functions reached only from these
calls; the size check is integer arithmetic so no constant joins the
pool every binary links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:55:15 -07:00
Malte UblandClaude Opus 5.5 ba17241220 Run String and Number methods on dynamic values and add substr, locale case and toPrecision
string-width's `character.codePointAt(0)`, camelcase's
`string.toLocaleLowerCase(locale)`, toml's `input.substr(...)` and
pretty-bytes' `number.toPrecision(3)` threw "x.y is not a function": the
dynamic method dispatch had no answer for these names on strings and
numbers.

A checked-dynamic receiver calling a String or Number method the runtime
dispatch cannot answer (codePointAt, substr, toLocaleLowerCase,
toLocaleUpperCase, isWellFormed, toWellFormed, trimLeft, trimRight,
toPrecision, toExponential(digits)) now evaluates the receiver and
arguments once and dispatches on the runtime kind: a string or number runs
the typed lowering, every other kind keeps the runtime dispatch's answer.
Names other dyn kinds share (at, concat, slice) and the Annex B HTML names
chalk-style builders define (bold, link, ...) keep their existing
dispatch.

Typed receivers gain substr, the locale case pair, the HTML methods,
toPrecision and toExponential(fractionDigits), all previously compile-time
refusals. toPrecision/toExponential digits come from exact big-integer
arithmetic over the double (ties up on the exact binary value). Locale
case mapping follows V8: the first requested locale decides, Turkic
dotted/dotless I is implemented, and Lithuanian, Greek uppercasing,
objects and malformed tags throw a 'not supported yet' Error.

A checker-untyped JS binding holding `string | undefined` boxes into the
dyn receiver path for String methods (string-width's baseVisible result).
tests/diagnostics/retired-engine-1112-number-methods.ts compiles now and
moves back to tests/corpus/1112-number-methods.ts.

New runtime functions are reached only from the new lowerings; the
existing runtime objects' sections are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:55:12 -07:00
Chris Tate b32f4e31f3 Merge main 2026-10-18 17:47:55 -05:00
Chris Tate 2f40bcb8d4 Lower iterator helpers on generators and Map and Set iterators
map, filter, take, drop, flatMap, toArray, forEach, reduce, some, every
and find compiled only as one chain from arr.values() or arr.entries()
that ended in a consuming method. Every other form was refused, and in
JavaScript the refusal was thrown at run time.

IteratorObject now maps to the generator type. A consuming method over
a chain of stages fuses into one loop over the source: a generator
driven like for-of, a Map or Set walked live, or an array by index. A
stage on its own builds a helper object: a generator over its source
that supports next(), for...of, spreads, Array.from and yield*. A
for...of whose head is a chain of stages without flatMap runs the
stages inside the loop.

The source is closed where Node closes it: when take reaches its count,
when some, every or find has its answer, when a callback or the loop
body throws, when the loop exits early, and before the RangeError of an
invalid take or drop budget. It is not closed when it is exhausted or
its own next() throws. return() on a helper object before its first
next() closes the source through a new runtime hook,
scr_gen_set_unstarted_return.

When closing an iterator throws while another error propagates, the
first error reaches the caller, as in Node. This applies to for...of
over a generator too, where the error from the close used to replace
the body's error. The try statement takes a dropFinallyErrors flag,
whose cleanup handler calls the new scr_exc_keep_first.

throw() lowers only on values typed as generators. Iterator helpers and
the built-in iterators have no throw method, and a value typed as an
iterator may hold one.

Callbacks in JavaScript bind their unannotated parameters to the
element type, and flatMap callbacks may return generators. A generator
whose record yields share checked storage with its return channel has
each value converted to the declared element type as it is pulled.
2026-10-18 17:46:31 -05:00
Chris Tate 6dff370714 Compile inline require(), computed and chained CommonJS exports, and a module's own exports object (#1329) 2026-10-18 17:45:15 -05:00
Malte UblandClaude Opus 5.5 8a89024e7b Refuse module exports that read this when called through the module binding
`m.f()` where `m` is `const m = require("./x")` or `import * as m from
"./x"` lowers to a direct call of the exported function, so a function
that reads `this` saw no receiver (or another frame's) where Node passes
the module object: `exports.hello = function () { return this.name; }`
threw a TypeError natively. Such calls now refuse with SC1090 (deferred
in JavaScript); exports that do not read this compile as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:41:11 -07:00
Malte UblandClaude Opus 5.5 815ce664e5 Let writes initialize a module let declared without an initializer
`let f: T; f = (n) => ... f(n - 1)` at module scope threw "Cannot access
'f' before initialization": a function-typed (pointer-guarded) binding
kept the null-pointer TDZ test on its writes, but the declaration
already initialized it (to undefined) when it ran. Such bindings now
carry the existing initialization flag: the declaration sets it, writes
that can run earlier check it, and writes no longer test the empty
pointer. A write that really precedes the declaration still throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:41:11 -07:00
Malte UblandClaude Opus 5.5 8514c45b53 Make builtin module functions real function values in JavaScript
uuid 11 threw `TypeError: native.randomUUID is not a function` where
Node works. Root cause: a builtin module function used as a JavaScript
value without a function-value adapter lowered to a string identity
token ("[builtin crypto.randomUUID]"); uuid's native.js stores it in an
exported record (`export default { randomUUID }`), so the call hit a
string and typeof answered "string".

crypto.randomUUID and randomUUIDv7 get value adapters (randomUUID's
through its options-validating entry point, the new valueFn surface
field), boxed adapters carry the builtin's name, and every other
builtin module function value is one function per builtin that throws
the named SC1090 fence when called, keeping identity, typeof and
inspection Node's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:41:10 -07:00
Malte UblandClaude Opus 5.5 e250214e03 Name objects built by plain constructor functions in util.inspect
console.log(new F(2)) printed `{ a: 2 }` where Node prints `F { a: 2 }`.
A construction through a plain function now stamps the first prototype
on the receiver's chain whose own `constructor` data property holds a
named function with that name, as Node's getConstructorName finds it.
The dyn printer already reads such runtime class names, so its code is
unchanged; stamped prototypes stay alive and are cleared before release
at exit. `new F()` boxes F under its name so the prototype's constructor
carries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:40:50 -07:00
Chris Tate 5b0c0f66b9 Compile classes that extend a generic class factory's result (#1335) 2026-10-18 17:38:32 -05:00
Chris Tate 33bed3118c Short-circuit optional chain tails over unchecked reads (#1334) 2026-10-18 17:37:15 -05:00
Chris Tate b119b4cf03 Stringify class instances through the runtime JSON traversal
JSON.stringify of a class instance, or of a record, array or union that
holds one, boxes the value and serializes it with the runtime JSON
traversal. The output matches Node: own enumerable properties in
creation order, properties added after construction, toJSON on the
instance or a base class, and no methods, accessors, #private fields or
static fields. A class whose declared field cannot be read through the
checked-dynamic property view, such as a Date field, is refused at
compile time with SC1090 naming the field.

Boxed Map and Set values now stringify as {}, as Node does, instead of
throwing.
2026-10-18 17:32:17 -05:00