Commit Graph
2626 Commits
Author SHA1 Message Date
Chris Tate ffddeb531b Match Node's collation for l·, U+0130 and ligatures (#1011) 2026-10-15 00:27:53 -05:00
Chris Tate 94657591fd Give zlib stream chunks the Buffer brand
Node pushes Buffer slices of its output buffer, so a chunk from a zlib
stream converts to its text. The runtime pushed plain Uint8Array
storage: String(chunk), template literals and chunk + "" printed the
bytes comma-separated. 5370 and 5371 check the conversions and
Buffer.isBuffer.
2026-10-15 00:25:52 -05:00
Chris Tate 83c5b8f327 Make the stream operators concurrency program independent of timer precision
5337 ordered the concurrent map() callbacks with 20 ms and 5 ms timers, so
a slow machine changed which callback ended first, in Node as well. The
callbacks now resume on setImmediate, and "a" waits for a promise that
"c" resolves, which keeps the same order and output.
2026-10-15 00:21:35 -05:00
Chris Tate 10539c9c42 Fail closed on cipher backend errors and on missing chacha20-poly1305 tags
- Every mbedTLS call in the cipher runtime is checked. A failed update(),
  final() or setAAD() returns no output and retires the context, so later
  calls throw and no tag is returned or accepted. An update past the
  AES-GCM limit of 2^36 - 32 bytes per message throws Node's "Trying to
  add data in unsupported state" error; it used to return zeroed output
  and leave the tag covering only the earlier data.
- chacha20-poly1305 decryption without setAuthTag() fails in final()
  again. Node 24.22 skips the tag check when authTagLength is not passed
  either and returns unverified plaintext; limitations.mdx records the
  difference.
- A runtime unit test moves contexts next to the GCM message and AAD
  limits and checks that the failure is permanent; errors.test.ts covers
  decryption without a tag.
2026-10-15 00:06:26 -05:00
Chris Tate 820c4eaf77 Match Node's collation for l·, U+0130 and ligatures
The generated localeCompare table now agrees with Node on spellings that
tie at the primary and secondary levels. "l·" and "L·" collate as one
unit, equal to U+0140 and U+013F, through a contraction table. Precomposed
characters take their decompositions' elements after the case weights are
assigned, so U+0130 equals I + U+0307. Ligatures whose spelled-out letters
tie with them (ij and ij) get the case weight Node places them at among
their first letter's case forms, and ligatures with their own secondary
(Æ) keep their own case. The generator's check now also compares every
covered character's decompositions, case forms and ligature spellings in
four contexts. A per-thread class table lets the lazy primary comparison
find characters that need element lists with one lookup.
2026-10-14 23:49:11 -05:00
Chris Tate 731462be5c Strip VT control characters in one pass
util.stripVTControlCharacters ran Node's ANSI pattern through the
regex replace loop, which tried the OSC branch's lazy scan at every
introducer. Text with many unterminated OSC introducers took quadratic
time (about 200 s for 100,000 of them).

The function now scans once and gives the same result as Node 24.22's
pattern: the OSC branch ends at the first terminator after ESC ], and
that position is cached across introducers; the CSI branch resolves its
first match without backtracking. A removal that brings lone surrogates
together keeps canonical storage.

Corpus programs 5470-5474 cover OSC and CSI forms, seeded random input,
long unterminated runs and surrogates, and strip-vt-linear.test.ts checks
that 400,000 unterminated introducers finish quickly.
2026-10-14 23:43:52 -05:00
Chris Tate 07e7741c89 Print class names and dynamic Maps and Sets in util.inspect (#1008) 2026-10-14 23:39:48 -05:00
Chris Tate f809f5bd93 Merge main 2026-10-14 23:33:10 -05:00
Chris Tate 7260406393 Merge main 2026-10-14 23:31:17 -05:00
Chris Tate c8d2b378d5 Merge main 2026-10-14 23:28:46 -05:00
Chris Tate c382e170a2 Free number and string dyn boxes without the destroy queue (#1007) 2026-10-14 23:27:27 -05:00
Chris Tate aba8442ad0 Support every Hash and Hmac encoding, view and option, and crypto.hash's options
Hash.update and Hmac.update accept every Buffer input encoding and any
TypedArray or DataView. Hex input of odd length throws Node's
ERR_INVALID_ARG_VALUE, an encoding Node does not know reads as UTF-8, and
data of other types throws ERR_INVALID_ARG_TYPE. digest() returns a
string for every Buffer encoding and a Buffer for a falsy or unknown one,
and a repeated Hmac.digest() returns Node's empty result.

createHash and hash.copy take options.outputLength for the SHAKE and
KECCAK-KMAC digests, with Node 24.22's validation and its
ERR_OSSL_EVP_NOT_XOF_OR_INVALID_LENGTH error, including the library and
reason properties, for other digests. crypto.hash takes an output
encoding or an options object with outputEncoding (including "buffer")
and outputLength. The functionName and customization options are
validated and then refused with Node 24.22's errors. createHmac takes
TypedArray, DataView and ArrayBuffer keys and options.encoding for string
keys, and checks them in Node's order.

Calls whose encodings and options the compiler can resolve keep their
typed fast paths. Other forms go through runtime entry points that apply
Node's checks and return a string or Buffer, checked at the call's type.
A literal digest encoding for which Node returns a Buffer is refused
when the declared result is a string.

The digest compression functions start on a cache line, so their speed
no longer depends on the size of unrelated runtime code.
2026-10-14 23:20:57 -05:00
Chris Tate a1fb131e2c Compare localeCompare strings without building element lists (#1004) 2026-10-14 23:18:13 -05:00
Chris Tate 4f6a972df6 Support every Node 24 digest in createHash, createHmac, hash, pbkdf2 and hkdf (#1006) 2026-10-14 23:15:20 -05:00
Chris Tate 24ecefc4ac Keep stream buffer entries in the buffer's representation
setEncoding() converted the buffered entries once, but Readable.from's
lazy reader, the operators and compose added later object-mode entries in
the kind they arrived in, so a Buffer entry could be read as a string.
Every insertion now passes the entry's kind: Buffer entries decode through
the stream's decoder, entries that would mix strings and Buffers are
refused, and the buffer insert checks the kind. setEncoding() on an ended
stream also flushes the decoder's partial tail, as in Node 24.
2026-10-14 23:11:39 -05:00
Chris Tate 5b0897d943 Hand stream chunks made from strings to user code as Buffers (#1005) 2026-10-14 23:09:36 -05:00
Chris Tate 50c5fb01fc Restore files from main that the previous commit reverted
The previous commit carried stale copies of the record-boundary and
Object.values changes; this puts main's versions back.
2026-10-14 23:02:49 -05:00
Chris Tate 853dbd6790 Print class names and dynamic Maps and Sets in util.inspect
A class instance reached through an untyped or interface-typed value
printed as a plain object ({ r: 1 }); it now prints with its class's name
(Circle { r: 1 }), including subclasses and class expressions. Values
whose static class has subclasses, which were refused, print through the
same view. Maps and Sets held in untyped values printed as an empty
string; they now print as Map(n) { k => v } and Set(n) { v }.
2026-10-14 22:59:06 -05:00
Chris Tate e6d0490546 Build Object.values natively when its result is only iterated or read (#1000) 2026-10-14 22:57:09 -05:00
Chris Tate 5253fb8d07 Widen record parameters and returns by flow class (#998) 2026-10-14 22:53:23 -05:00
Chris Tate a5dd1637c2 Update the node-types fence snapshot for the Brotli hint
The brotliCompressSync fence now carries the Brotli hint that names the
zlib stream classes.
2026-10-14 22:45:57 -05:00
Chris Tate 6b81bcc26b Free number and string dyn boxes without the destroy queue
A number or string seen as unknown lives in a dyn box. Allocating one
cleared the whole value union, although both kinds set a single payload
word, and releasing one went through the destroy queue, the generic dispose
switch and the weak-dispose hook before the node returned to the free list.

Number and string boxes now skip the union clear, and their last release
returns them to the free list directly. They hold at most one string, are
never cycle candidates and cannot be weak-collection keys, so the skipped
steps have nothing to do for them.
2026-10-14 22:43:17 -05:00
Chris Tate 5f18c34ad3 Add Blob and File (#1001) 2026-10-14 22:40:49 -05:00
Chris Tate 3ea4277efe Inline interface dispatch and lend array elements to it (#997) 2026-10-14 22:37:53 -05:00
Chris Tate 23b6cb8819 Support every Node 24 digest in createHash, createHmac, hash, pbkdf2 and hkdf
createHash, createHmac, crypto.hash, pbkdf2, pbkdf2Sync, hkdf and hkdfSync
accept every digest crypto.getHashes() lists in Node 24: SHA-512/224,
SHA-512/256, SHA-256/192, SHA3-224 to SHA3-512, Keccak-224 to Keccak-512,
SHAKE128, SHAKE256, KECCAK-KMAC-128, KECCAK-KMAC-256, RIPEMD-160,
BLAKE2b-512, BLAKE2s-256, SM3 and MD5-SHA1. The new runtime unit
scr_hash.c implements Keccak, RIPEMD-160, BLAKE2b, BLAKE2s and SM3, and
is registered in both runtime pack matrices and the C-compile path.

Names resolve as Node 24.22 resolves them: case-insensitively, with
OpenSSL's provider aliases and object identifiers, ending at the first
NUL, and with trailing spaces allowed except on the RSA signature
aliases. "dss1" and "DSS1" are SHA-1, and createHash and crypto.hash
accept "dss1" in any case. The compiler no longer refuses literal digest
names outside SHA-1/SHA-2 and MD5; unknown names throw Node's errors at
run time.

SHAKE and KMAC digests use their default output lengths. createHmac
rejects them with Node's OpenSSL error, pbkdf2 with "xof digests not
allowed", and hkdf with "Deriving bits failed". pbkdf2 with a key length
of 0 now fails with "Deriving bits failed" as in Node 24.22, and the
callback forms pass these failures to the callback.

PBKDF2 computes the keyed HMAC states once instead of on every
iteration, and digest contexts are cleared with memset and a compiler
barrier instead of a byte loop. The Keccak permutation, the RIPEMD-160
and BLAKE2 compression functions and the SM3 rounds are unrolled with
constant indices.
2026-10-14 22:15:06 -05:00
Chris Tate 712a82ae96 Hand stream chunks made from strings to user code as Buffers
Node converts a string chunk with Buffer.from(chunk, encoding) and
returns Buffer.concat for read(n), so every byte chunk user code
receives from a non-object-mode stream is a Buffer. The runtime made
these chunks as plain Uint8Array storage. A typed write(), transform()
or 'data' callback then printed String(chunk), `${chunk}` and
chunk + "" as comma-separated bytes (97,98) instead of the text, and
read(n) and read() after unshift(string) returned non-Buffer values to
untyped code.

The chunks the stream creates now carry the Buffer brand: write() and
push() of a string (with or without an encoding), transform output given
as a string (the callback or this.push), unshift() of a string, and the
concatenation read(n) returns.

Corpus 5445 (typed callbacks, pipeline and stream/promises pipeline)
and 5446 (untyped code, Buffer.isBuffer at each boundary) check this
against Node.
2026-10-14 21:55:39 -05:00
Chris Tate 481988156e Compare localeCompare strings without building element lists
String.prototype.localeCompare keeps its results and gets fast paths for
the common cases. Byte-equal strings compare equal at once. ASCII strings
compare their primary weights in one pass from a per-thread table and
read the secondary and tertiary weights only when every primary is equal.
Latin text whose marks carry no primary weight compares primaries
lazily, decoding only until the first difference. Other text, and every
tie at the primary level, uses the existing element lists, which no
longer zero their inline buffers or check capacity per element. The
backend lends both strings to the call instead of retaining them.
2026-10-14 21:53:39 -05:00
Chris Tate b34a769aca Match Node 24.22 cipher behavior
- chacha20-poly1305 refuses non-empty AAD once data has been processed
  (ERR_CRYPTO_INVALID_STATE); empty AAD is still accepted.
- chacha20-poly1305 decryption without setAuthTag() succeeds when no
  authTagLength was passed, and is verified when one was.
- Changing the output encoding of update() or final() throws
  ERR_INVALID_ARG_VALUE instead of ERR_INTERNAL_ASSERTION.
- The ctsMode and xtsStandard options are validated in Node's order;
  the implemented ciphers report ERR_CRYPTO_UNSUPPORTED_OPERATION.
- The list of known but unimplemented cipher names follows Node 24.22's
  getCiphers(), so the CBC-HMAC names are unknown ciphers.
2026-10-14 21:44:18 -05:00
Chris Tate da89261676 Run V8's TimSort for comparator sorts
Array.prototype.sort and toSorted with a comparator now run V8's
TimSort (array-sort.tq) step for step in the runtime, in the generated
sort for booleans and unions with undefined, and in the checked-value
sort. The comparator is called as f(x, y) < 0 in Node's argument order,
so a comparator sees Node's calls in Node's order and an inconsistent
comparator yields Node's permutation. Undefined values, including a
union's undefined arm, are set aside before sorting and never reach the
comparator, and a checked comparator's result converts with ToNumber.
Structured inputs take fewer comparator calls than the previous merge
sort. Each thread reuses one sort buffer of up to 1 MiB.
2026-10-14 21:22:39 -05:00
Chris Tate bd438a6d5e Add Blob and File
Blob and File (globals and node:buffer exports) compile to native
handles in the new scr_blob.c, following Node 24.22's
lib/internal/blob.js and file.js. A Blob is a window over a shared,
immutable byte store plus the byte length of each part. Sources (strings,
typed arrays, DataView, ArrayBuffer, Buffer, other Blobs, Set and Map)
convert as a Web IDL sequence, with each non-buffer part converted to a
string, before the options are read, and are copied when the Blob is
built. The constructors throw Node's Web IDL TypeErrors and its
ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE and ERR_MISSING_ARGS errors,
and read and convert their options in Node's order. The type is
lowercased, or cleared when it holds a character outside U+0020..U+007E.
endings: "native" rewrites CRLF, CR and LF to the platform's line break.
File takes its name with lone surrogates replaced and lastModified as a
number (Date.now() when absent).

slice() converts its offsets as a [Clamp] long long (rounded half to
even) and keeps the parts Node's data queue keeps. text(), bytes() and
arrayBuffer() settle on the same microtask turn as in Node, which depends
on the part count. util.inspect prints Node's `Blob { size, type }` and
`File { ..., name, lastModified }` forms, with `[Blob]` past the depth
limit. String() and toString() give the class tag, JSON.stringify gives
{}, and structuredClone returns a new Blob or File over the same bytes.
instanceof works for both spellings. stream(), textStream() and the
Object.prototype members other than toString() fail at compile time.
Non-array iterables other than Set and Map throw SC2020.

Three handle-table additions support this: a structuredClone hook, a
JSON `{}` flag, and a bracketed past-depth inspect flag. scr_blob.c is an
optional unit selected by dynAsync, because the dynamic dispatch path
boxes its reads as dyn promises through scr_async_dyn.c.

Corpus 5285-5290 and a node-types fixture check the behavior against
Node.
2026-10-14 21:17:46 -05:00
Chris Tate b3217f0650 Build Object.values natively when its result is only iterated or read
In TypeScript, Object.values over a checked-dynamic value whose result is
only iterated with for-of, indexed, or passed to every, some, findIndex,
forEach, join or reduce now returns a native vector of the checked values.
The program never observes the array itself, so the checked-dynamic array
and, for for-of, the dynamic iterator protocol are skipped. Typed records
fill the vector through their record ops table and plain data objects
from their entries in own-key order. Other uses, and JavaScript files,
keep the checked-dynamic array.
2026-10-14 21:14:09 -05:00
Chris Tate 8c3b1f9e60 Add the zlib stream classes
Deflate, Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw and Unzip, from
createGzip() and the other factories or new zlib.Gzip(options), are
runtime Transform streams over zlib.

- scr_zlib.c gains a step engine that follows Node's ZlibContext: lazy
  init, Unzip's gzip-magic detection, the dictionary retry, Gunzip's
  multi-member loop, and Node's error messages, codes and errno.
- scr_zlib_stream.c, linked when a program uses zlib and streams, runs
  each write as engine steps on the worker pool with Node's output
  chunking, the finish flush from 'prefinish', read-side backpressure,
  flush(), params(), close(), reset() and bytesWritten, and validates
  options in Node's order.
- The fallback declarations add the classes, factories and a
  literal-typed ZlibConstants; zlib.constants and the module-level Z_*
  copies bake as literals. Brotli streams and their factories are refused
  by name.

Corpus programs 5370-5377 cover round trips, chunking and event order,
errors, options, flush and params, stream/promises, constants and
backpressure.
2026-10-14 21:09:51 -05:00
Chris Tate b072f22c4e Widen record parameters and returns by flow class
A record built from unchecked element reads can hold undefined in a field
the checker calls present. Such a record kept its widened layout only in
an inferred binding: an annotated parameter or return, or an unannotated
return, converted it to the declared layout with a checked narrow, which
throws a TypeError for a missing element where Node passes undefined
through.

Record values that flow between bindings and the parameters and results
of functions and methods whose every call is visible now form flow
classes. A class takes the widened layout, with the union of its members'
optional fields, when it is closed: each value entering it is a fresh
object literal or comes from a member, each use of a member reads or
writes its fields, passes it to another member or only inspects it, and
every member declares the same shape. Members then share one layout, so
no conversion runs at the boundary and the record keeps its identity.

A class that reaches other storage keeps the declared layout for every
member, including the inferred bindings and the record parameters the
analysis widened before. Converting there copied the object, so writes
through a parameter or a pushed record did not reach the caller's object;
such a literal now converts once where it is created.
2026-10-14 21:07:40 -05:00
Chris Tate 9843f8efd2 Inline interface dispatch and lend array elements to it
Interface dispatch now unboxes a receiver it has matched by key with one
load and no reference count change, and keeps its checked fallback out of
line so the key tests inline at each call site. A loop over an array of
interface values lends each element to a dispatch whose implementing
methods cannot release it; the fallback, which can run any code, takes
its own reference to the receiver.
2026-10-14 21:06:15 -05:00
Chris Tate b5cb8b397a Add the Readable operators, the Readable.from options and stream.compose
The Readable operators (map, filter, forEach, toArray, some, find, every,
flatMap, drop, take, reduce) run over string and Buffer chunk streams with
Node's ordering, errors, and concurrency, highWaterMark and signal options.
Readable.from reads arrays lazily and accepts the objectMode and
highWaterMark options, and its JavaScript form follows from.js's checks.
stream.compose returns a Duplex over a pipeline of stream instances.

pipeline now follows pipeline.js: per-stream destroyers and eos watchers,
and the callback on a tick once every watcher finished. 'finish' waits for
a pending _final or _flush callback, and a failed synchronous write emits
'error' one tick later, as in Node.
2026-10-14 20:58:41 -05:00
Chris Tate 1cb9d2201f Match typed JSON.parse member names against the text and read scalar members inline
The schema reader scanned every member name with the general string scan,
compared it with memcmp, and re-entered the recursive reader for every
value. The expected next member's name is now compared directly against
the text and its closing quote when the name has at most eight bytes and
none that JSON text escapes; other keys keep the general scan and compare
names with scr_key_equal. Number, boolean and string members and elements
are read in the record and array loops; records and arrays still recurse.
2026-10-14 20:38:52 -05:00
Chris Tate 7dfe4e8bea Merge main and update test fixtures for the cipher link feature
The native link feature records in the self-hosting tests list cipher,
and the library profile test uses crypto.createSign for its remediation
example now that createCipheriv is supported.
2026-10-14 20:31:47 -05:00
Chris Tate f6e9fbe056 Support subclassing EventTarget and Event (#988) 2026-10-14 18:57:20 -05:00
Chris Tate cfe28b7ad1 Move scriptc to Node 24.22.0 (#989) 2026-10-14 18:49:17 -05:00
Chris Tate bb24a37329 Write typed JSON.stringify arrays from their slots and append fixed text inline (#991) 2026-10-14 18:39:51 -05:00
Chris Tate b710e74acb Lower value-position writes to the writable Event accessors
Assignments to returnValue and cancelBubble on a typed Event subclass
receiver lower in value position as well as in statements: an
initializer (`const r = (ev.returnValue = false)`), a call argument, and
a chained assignment write the accessor through event.set and yield the
assigned value. The short-circuit forms (&&=, ||=, ??=) read the
accessor through event.get and write it only on the branch that assigns.
The receiver now evaluates before the assigned value, as in Node.
2026-10-14 18:25:05 -05:00
Chris Tate f7390b4f05 Match Node 24.22 HTTP write coalescing, fetch connection headers and crypto.constants
OutgoingMessage now corks the message for a tick like Node 24.22, so
chunked writes in one tick (and a corked end(chunk)) go out as one chunk,
for server responses and client requests. Fetch accepts a connection
header that is a comma list of tokens, as Undici 7.29.1 does. The
crypto.constants declaration matches the Node 24.22 table, with a pin test.
2026-10-14 18:12:09 -05:00
Chris Tate d2a84e4a41 Write typed JSON.stringify arrays from their slots and append fixed text inline
The emitted JSON writers read each array element through scr_arr_state
and scr_arr_get_ref, retaining and releasing it, and appended every
bracket, comma and key through a runtime call. Arrays whose indices below
the length all hold values now serialize from their slots
(scr_arr_dense_slots) with borrowed elements; other arrays keep the
element walk. Fixed text is stored inline when it fits the buffer's spare
capacity, booleans write their literal inline, and numbers are formatted
directly into the buffer.
2026-10-14 18:05:50 -05:00
Chris Tate 022ac6eba9 Store arrays of interface values natively and read interface fields directly
Arrays whose elements are class-backed interface values are native arrays
of the element capsules, so iteration, indexing and most array methods no
longer go through the checked-dynamic array, and method calls receive a
receiver they can dispatch. Methods without a native form for these
elements run on the array's live dynamic view.

A read of an interface property signature has the property's static type
and reads the field of an implementing class directly. Accessors, object
literals and JavaScript classes keep the checked property read.
2026-10-14 17:38:02 -05:00
Chris Tate 9835ad0d75 Move scriptc to Node 24.22.0
Pin Node v24.22.0 (commit f14cd3aa7bf9, Undici 7.29.1, OpenSSL 3.5.8) in
.node-version, Dockerfile.sandbox, the docs matrix fallback, the
compatibility pin, the fetch profile, and process.version. A node-pin test
checks that these pins agree. The Node compatibility snapshot is
regenerated from the v24.22.0 API docs; the new stream_iter chapter maps
to node:stream/iter, and the SuiteContext rows no longer match the
TestContext evidence.

Runtime and compiler changes follow Node 24.22 behavior:

- fs.read, fs.readSync, and FileHandle.read validate the position before
  the zero-length return.
- fs.statfs results include frsize; Windows reads
  FileFsFullSizeInformation and reports bfree and bavail separately.
- fs.utimes, futimes, and lutimes keep nanosecond precision.
- ServerResponse sets writableFinished where 'finish' is emitted and runs
  end() callbacks before the 'finish' listeners while the socket is still
  attached; a request read to 'end' closes before its response.
- socket.write(string, 'buffer') throws ERR_UNKNOWN_ENCODING.
- URL protocol-setter rejections keep the current URL, the host setter
  no longer leaves a "/." escape when it adds a port to a "//" path, and
  new URLSearchParams(null) parses "null".
- util.stripVTControlCharacters uses Node 24.22's ANSI pattern,
  util.styleText accepts #RGB and #RRGGBB colors, and
  util.types.isDataView has its JS name and length.
- structuredClone and AbortSignal.any use the "cannot be converted to
  sequence" message.
- crypto.getCiphers, getHashes, and constants match Node 24.22.
- dns.lookup, net.connect, and tls.connect throw ERR_INVALID_ARG_VALUE for
  hosts with null bytes.
- fs.readFile validates options.buffer and refuses its valid forms;
  httpValidation and certificateCompression refuse by name.
- url.domainToASCII, url.domainToUnicode, and url.parse hostnames follow
  ada 4.0: ASCII domains are lowercased without checking their xn--
  labels, the RFC 5893 rule checks how LTR labels end and how RTL labels
  start, a Punycode label whose decoded form begins with "xn--" fails, and
  ToUnicode decodes only labels that pass the ToASCII checks. The url
  legacy test cases are regenerated with Node 24.22.

Corpus programs 5275-5279 cover the new behavior. 1640, 2283, 2599, and
3136 are updated where they pinned Node 24.15 output.
2026-10-14 17:27:02 -05:00
Chris Tate 30e291a341 Support subclassing EventTarget and Event
Typed and JavaScript classes can extend the global EventTarget and Event.
%EventTarget and %Event are runtime-provided hierarchy roots like the
EventEmitter: a subclass instance embeds a prefix pointing at the engine
state that super() creates (the target's listener core, the event's
state), and the backend's emitter prefix handling becomes a table of
runtime prefixes that covers all three bases.

The instances' checked-dynamic capsules use the runtime RC adapters, so
the engine recognizes them: a target instance is its listeners' `this`
and its events' target, and an Event subclass instance is the event its
listeners receive, with its own fields and methods. The inherited
methods and accessors lower directly on typed receivers (including
super.dispatchEvent) and answer through the class property dispatch and
the runtime's capsule hooks for checked-dynamic values, with Node's
util.inspect output, @@toStringTag and leak-warning class name. Members
read as values are the prototype's functions, also for plain handles.

A target keeps its own capsule in its state, so repeated dispatches reuse
it; the state and an event's target are traced, so the collector
reclaims targets whose listeners capture them. Subclass members that
spell an inherited member are refused, since the engine would never call
or read them.

Listener objects that are class instances now work: when a program can
register EventTarget listeners and declares a class with a handleEvent
method, %main installs a compiled `listener.handleEvent` reader that the
engine uses at every dispatch, so the class's own, inherited or
overridden method runs with the instance as `this`, as in Node. An
unannotated arrow in an object literal whose contextual type maps to a
checked-dynamic slot or to no lowered type (an EventTarget subclass's
listener union) no longer adopts the void callback slot's return type,
so `{ handleEvent: (e) => n++ }` passed as a listener keeps the result
its record field promises.
2026-10-14 17:18:04 -05:00
Chris Tate 99386913c4 Inline Map/Set iteration steps and skip deleted leading entries (#987) 2026-10-14 17:01:30 -05:00
Chris Tate c43ff734fa Merge main 2026-10-14 17:00:49 -05:00
Chris Tate 90c035adee Add readline/promises, stream.addAbortSignal and the stream/promises options (#983) 2026-10-14 16:11:25 -05:00
Chris Tate 766b564659 Inline Map/Set iteration steps and skip deleted leading entries 2026-10-14 15:17:21 -05:00