1737 Commits
Author SHA1 Message Date
Chris Tate 850710fbea Box TextEncoder and TextDecoder records as checked-dynamic handles (#2022) 2026-10-23 15:03:43 -05:00
Chris Tate adb8690f11 Cache untyped property access in programs that declare classes (reland) (#2017) 2026-10-23 14:47:10 -05:00
Chris Tate 807d281baf Support raw key formats, X25519 keys and crypto.diffieHellman (#2029) 2026-10-23 14:40:52 -05:00
Chris Tate e7c2462e7a Rule out global constructor names by their first byte (#2028) 2026-10-23 14:27:01 -05:00
Chris Tate cd48b2b926 Prepare v0.4.3 release (#2019) 2026-10-23 14:18:34 -05:00
Chris Tate 3a5f563e70 Iterate untyped arrays without the generic iterator protocol (#2027) 2026-10-23 14:18:14 -05:00
Chris Tate 96a5954940 Merge main 2026-10-23 14:09:13 -05:00
Chris Tate 693e33742a Support the raw-public and raw-private key formats
EC and Ed25519 keys read and write Node 24's raw formats:

- generateKeyPair(Sync) encodings: raw-public gives the EC point,
  uncompressed or, with type "compressed", compressed, or the Ed25519
  public key; raw-private gives the EC scalar at the curve's size or the
  Ed25519 seed.
- KeyObject.export({ format: "raw-public" | "raw-private" | "raw-seed" }),
  with Node's check of the point type, and Node's error for a passphrase
  with any format other than PEM and DER, which now also covers JWK.
- createPublicKey and createPrivateKey of { key, format,
  asymmetricKeyType, namedCurve }, in Node's argument order, with
  compressed EC points, and createPublicKey of a raw private key.

As in Node, raw-seed and every raw format of an RSA, RSA-PSS, DSA or DH
key throw ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS, which generateKeyPair
passes to its callback. Invalid key data throws ERR_INVALID_ARG_VALUE.
Raw X25519, X448, Ed448 and post-quantum keys, EC scalars of 0 or at
least the curve order, and the EC point at infinity, which Node accepts,
throw a coded SC2020 error.
2026-10-23 13:59:28 -05:00
Chris Tate 47636f2fa5 Implement the identity TransformStream natively (#2009) 2026-10-23 13:51:52 -05:00
Malte UblandClaude Opus 5.5 c81ee69da7 Rule out global constructor names by their first byte
A global constructor read as a value in untyped code (zod's
`data instanceof Map`, `Set`, `Date` in getParsedType) resolves its name
at run time: Function, then the Error constructors, then the other
globals, each by strcmp. Every miss in the Error and global tables cost a
call, about a dozen per read.

Each table entry is now skipped unless its first byte matches the name's,
before the strcmp. The tables, their order and the values they return are
unchanged.

zod 3, z.array(Order).parse over 500 orders: 187.3M -> 183.2M
instructions per round (-2.2%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:49:36 -07:00
Malte UblandClaude Opus 5.5 26d2c8bae1 Iterate untyped arrays without the generic iterator protocol
for...of, spread and destructuring over an untyped array opened the
iterator by calling the builtin values() function through scr_dyn_call
with the array as `this`, and stepped it through the general element
path: retain the source, compute the live length, read the element with
hole handling, release the source.

An array whose iterator method is the builtin values() now gets the
native iterator that call returns directly. A step over a plain array
whose index is an own element, or past the end, answers without the
general path; no getter can run there. Holes, keys(), entries(), typed
array capsules and every other source keep the general path.

zod 3, z.array(Order).parse over 500 orders: 187.3M -> 182.2M
instructions per round (-2.7%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:33:28 -07:00
Chris Tate 285a92354e Wake TransformStream pulls waiting on a static fetch body 2026-10-23 13:21:29 -05:00
Chris Tate c7fd9dcdf0 Merge main 2026-10-23 12:59:08 -05:00
Chris Tate 934d254a41 Keep the inline-cache record list within the shape budget 2026-10-23 12:44:35 -05:00
Chris Tate 49e35b6755 Answer untyped property reads of plain values without the class tables (#2013) 2026-10-23 12:38:38 -05:00
Malte UblandClaude Opus 5.5 f14997776b Box TextEncoder and TextDecoder records as checked-dynamic handles
A codec stored in untyped storage (hono's StreamingApi keeps
`this.encoder = new TextEncoder()` in a JavaScript class field) had no
dynamic representation and threw SC1101 at run time. The codec records
now cross 'unknown' slots as handles of two new tags that hold the record
itself, so identity and a decoder's streaming state survive the round
trip, and checked casts unbox the same record.

The handles answer encode, encodeInto and decode (with options objects),
the encoding, fatal and ignoreBOM properties, method values with Node's
ERR_INVALID_THIS, instanceof, String() tags and JSON's {}. The unit
scr_text_codec_dyn.c links only into programs that convert a codec to or
from an untyped value (the textCodecDyn runtime feature, found with the
dgram carrier walk, now shared).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:33:06 -07:00
Chris Tate d891a245ab Clone Response and Request bodies by teeing their streams (#2008) 2026-10-23 12:16:46 -05:00
Chris Tate a36ef0b4ca Settle aborts and pull TransformStream uploads as Node does 2026-10-23 12:11:48 -05:00
Chris Tate 4dcb83c418 Free a typed teardown's blocks in the order it met them (#1849) 2026-10-23 12:09:37 -05:00
Chris Tate 849fad0ab6 Prepare v0.4.3 release 2026-10-23 12:08:08 -05:00
Malte Ubl 00fc647ca1 Merge remote-tracking branch 'origin/main' into perf/dyn-plain-property-reads 2026-10-23 10:05:34 -07:00
Chris Tate bd36b77a8f Cache untyped property access in programs that declare classes (reland) 2026-10-23 11:57:22 -05:00
Chris Tate dc1d99835d Merge main 2026-10-23 11:54:20 -05:00
Malte UblandClaude Opus 5.5 a77fe8c21c Decide an untyped property write with one own-property lookup
scr_dyn_object_key_set, behind every untyped property write and every
member of an untyped object literal, looked the key up as many as five
times: a linear scan for an own accessor or read-only entry, a lookup
before the prototype walk, one before the non-extensible check, one to
know whether the key was created, and one more inside the store.

The own property is now looked up once. An own data property is written
in place (an own setter still runs; a read-only or getter-only property
still refuses), and an absent key walks the prototype chain as before
and is then added by scr_dyn_obj_set, which is unchanged.

zod 3, z.array(Order).parse over 500 orders: 199.8M -> 194.1M
instructions per round (-2.8%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:24:47 -07:00
Chris Tate 6828c23b50 Error tee branches when the source errors with no read pending
The tee learned a source error only through a pending read's error
steps, so a source that errored while both branches held buffered
chunks and no read was pending left the branches readable: the next read
returned a chunk where Node rejects. The tee now watches its source's
error, as the streams spec reacts to the source reader's closed promise,
and errors both branches in a microtask (resolving the cancel promise
unless both branches were canceled). A read's error steps only end the
read, and a clone of an already errored body errors both branches the
same way.
2026-10-23 11:20:05 -05:00
Chris Tate 2d4aac0976 Read Web Headers pairs without copying them, and cache their sorted list (#2000) 2026-10-23 10:50:13 -05:00
Malte UblandClaude Opus 5.5 3c2911ccad Implement the identity TransformStream natively
'new TransformStream()' had no lowering, so hono's stream() and
streamSSE() helpers failed to compile their checked-dynamic paths. The
constructor without arguments now builds a native handle: its readable
side is a native ReadableStream with a zero high-water mark, and its
writable side runs the streams spec's WritableStream state machine
(getWriter, abort, close, locked; the writer's write, close, abort,
releaseLock, ready, closed and desiredSize). A write completes when the
readable side reads its chunk, close/abort/cancel propagate across the
pair, and deferred reactions take as many microtask turns as in Node.
Web ReadableStreams can also be weak collection keys (hono keys a
WeakMap by the response stream). A transformer, queuing strategies and
the WritableStream constructor keep the SC2020 refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 08:47:47 -07:00
Malte UblandClaude Opus 5.5 e84f656172 Clone Response and Request bodies by teeing their streams
Response.clone() and Request.clone() on checked-dynamic handles answered
'res.clone is not a function' (hono's etag middleware hashes
res.clone().body). The runtime now implements the streams spec's
ReadableStreamDefaultTee over native streams and clones the body with it,
so the original and the clone each read every chunk. A used or locked body
throws undici's TypeErrors, the clone's headers are a copy with the same
guard, and a cloned Request's signal follows the original's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 08:47:43 -07:00
Chris Tate 998a385bde Mark constructed headers canonical only when every value is trimmed
The Headers and Response constructors combine duplicate entries (a: x,
a: "") into one value with the separator, which can end in its space
("x, "). They marked the pairs canonical anyway, so a copy took them
unchanged where Node trims the copied value ("x,"). The constructors
now mark the pairs canonical only when every value is already trimmed;
otherwise copies normalize them pair by pair.
2026-10-23 10:45:26 -05:00
Chris Tate c11253b228 Reject other classes' capsules inline in class-table brand probes (#2001) 2026-10-23 10:35:30 -05:00
Chris Tate b6e8e149b5 Trim combined header values again when copying after an empty append
Appending an empty value to an existing header leaves the separator's
space at the end of the combined value ("x, "). Node keeps that in the
Headers object but trims it when the value is copied into another
Headers, Request or Response ("x,"). The copy fast path copied a
canonical object's pairs as they were, so the copy kept the space. Such
an append now marks the pairs as no longer canonical, and copies
normalize them pair by pair.
2026-10-23 10:25:37 -05:00
Malte UblandClaude Opus 5.5 a705a3fd09 Reject other classes' capsules inline in class-table brand probes
A class table (property bags, normalization, computed reads) tests the
receiver's capsule key against every compiled class in turn. A capsule
boxed by another class has another key pointer, and each such probe called
scr_dyn_typed_ref_is_key to compare the names. The probe now rejects it
inline when the key length or its last eight bytes differ, and calls the
out-of-line comparison only for a likely match, so capsules boxed with an
equal key elsewhere still match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 08:04:00 -07:00
Malte UblandClaude Opus 5.5 7b8a07e0f2 Search proven-ASCII strings backwards by byte in lastIndexOf
lastIndexOf built the receiver's UTF-16 index map and converted positions
both ways even when the receiver is proven ASCII, where byte offsets are
UTF-16 indexes (indexOf already has this fast path). A short id such as
"bk_00042".lastIndexOf("_") cost about 200 instructions; the Hono
fixture's list endpoint does it 80 times per request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 08:04:00 -07:00
Malte UblandClaude Opus 5.5 056dda3633 Read Web Headers pairs without copying them, and cache their sorted list
Headers kept its name/value pairs in a string array and read every element
through scr_arr_get_ref (index checks, retain, release). forEach and the
iterators re-sorted and rescanned the whole list for every step, and every
Response or Headers built from another Headers object re-validated and
re-normalized each copied pair. The Hono fixture spends 12-15% of a request
there.

- Pair loops read borrowed elements of the dense array (sf_pair_at).
- The sort-and-combine list is cached on the Headers object and dropped by
  every mutation, so live iteration keeps Node's semantics.
- get() of a single value returns the stored string.
- Copying a Headers object whose pairs are canonical (built by the
  constructor or a Response) into an empty list copies the pairs; wire
  response headers keep the per-pair path because they may repeat names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 08:03:59 -07:00
Chris Tate 4b60ac0de9 Copy class-view property bags entry by entry (#1976) 2026-10-23 08:43:19 -05:00
Malte Ubl 3be0a1e468 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-23 06:38:28 -07:00
Malte Ubl 2365c04417 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-23 06:38:10 -07:00
Chris Tate 8476f68136 Support writev on Transform and PassThrough, and runtime stream option values (#1944) 2026-10-23 07:53:59 -05:00
Chris Tate 1221104388 Merge main and fix CI 2026-10-23 07:50:09 -05:00
Chris Tate a85c7c0a3b Read native handle methods as function values (#1984) 2026-10-23 06:02:41 -05:00
Chris Tate d9544bb71d Release EventTarget's shared method values at exit when only a signal read them
The EventTarget method values are cached for the program, and exit
released them only when an EventTarget, an event or a listener had
registered the unit's teardown. A program that read an AbortSignal's
addEventListener and never added a listener or made a target left the
cached functions live, and RC-audit builds exited with 99. Caching a
method value now registers the teardown too.
2026-10-23 05:57:37 -05:00
Chris Tate 8d17fe4e57 Support crypto.getCipherInfo, AES-CCM and AES key wrap (#1873) 2026-10-23 05:36:21 -05:00
Chris Tate 19e0fe8bcd Merge main and read cipher options through getters and prototypes 2026-10-23 05:33:14 -05:00
Chris Tate 40c3f69b40 Read an AbortSignal's EventTarget methods as EventTarget's own
An AbortSignal answered addEventListener, removeEventListener and
dispatchEvent read as values with functions of its own, which only ran on
AbortSignal handles: calling one on a plain EventTarget threw
ERR_INVALID_THIS. In Node they are EventTarget.prototype's methods,
shared with every target. The signal's property read now answers the
EventTarget method values (scr_event_target_method_value), which run on
any EventTarget receiver, AbortSignal included, so
signal.addEventListener === new EventTarget().addEventListener holds.
The signal's own method table keeps throwIfAborted.
2026-10-23 05:31:21 -05:00
Chris Tate 63fd3cecb7 Build @hono/node-server 2.x into scriptc (#1886) 2026-10-23 05:25:14 -05:00
Chris Tate eb2fa3c829 Fire spawn listeners for a child that exits before its first poll pass
A child spawned from a listener that runs during a poll pass, such as a
'close' listener, can exit before the next pass fires its 'spawn'
listeners. The same pass's waitpid loop then settled it with 'spawn' never
emitted. Settling an exited child now fires its pending 'spawn' listeners
first, before its stream data and 'exit', as Node emits them.
2026-10-23 05:14:51 -05:00
Chris Tate 952e01d2ed Stream request bodies in the Node server adapter 2026-10-23 05:03:12 -05:00
Chris Tate 0222bb1058 Keep fiber stacks across short event-loop sleeps (#1977) 2026-10-23 04:52:57 -05:00
Chris Tateandblurname d1f3eda732 Freeze arrays, Maps and Sets that other references still hold
Object.freeze of an array that another reference may still hold stopped at
compile time with SC2020, and freezing an array, Map or Set held in an
untyped value threw SC2020 at run time. Node freezes the object itself: a
frozen array refuses every later write through any reference with the
TypeError of the operation's first write, and a frozen Map or Set only
stops being extensible, since its entries are not properties.

- An array a program may freeze keeps a frozen bit in its storage flags.
  An array type is freezable when a typed Object.freeze names it, or, in a
  program that freezes untyped values, when a value of it can be held as a
  live untyped reference. The writes of those types test the bit with the
  guard published arrays already use; other array types are unchanged.
- The guard's errors follow Node for every operation: splice, fill,
  copyWithin, sort and reverse throw at their first write, and do not
  throw when they would write nothing. Published arrays get the same
  errors, and a statement splice of a published array is now guarded.
- A Map or Set records that it is not extensible, for Object.freeze, seal
  and preventExtensions, typed or untyped. Object.isFrozen, isSealed and
  isExtensible report it, and its entries stay writable.
- Writes to a frozen array through an untyped value are refused with the
  same errors, as are writes to frozen checked arrays and objects, which
  now name the property and the object as Node does.
- Programs that neither freeze nor publish arrays compile to the same IR
  and LLVM.

Corpus 7406 covers typed and untyped freezes of arrays, Maps and Sets,
every array write on frozen arrays of zero, one and three elements,
comparator sorts, nested arrays, and frozen arrays that are released, and
matches Node.

Refs #1374

Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>
2026-10-23 04:50:06 -05:00
Chris Tate 900bd2cf01 Check generateKeyPair arguments and RSA parameters as Node does (#1973) 2026-10-23 04:39:34 -05:00