Commit Graph
4507 Commits
Author SHA1 Message Date
Chris Tate 4acf7f047d Keep the key order of records spread from values JavaScript built (#2024) 2026-10-23 15:52:49 -05:00
Chris Tate 24a89488f9 Give glibc's per-thread heaps huge pages (#2032) 2026-10-23 15:41:30 -05:00
Chris Tate 45a6f9d9a7 Import and list exports whose names start with "__" (#2033) 2026-10-23 15:35:46 -05:00
Chris Tate 9a2d378fba Run completion callbacks in their call's AsyncLocalStorage context (#1804) 2026-10-23 15:29:35 -05:00
Chris Tate 59e5c0cf8a Merge main and keep declared keys in ordered spreads 2026-10-23 15:20:26 -05:00
Chris Tate 97b64fae1c Turn the thread-heap huge-page policy off under memory tagging 2026-10-23 15:13:36 -05:00
Chris Tate c70652d081 Keep record key order through typed iterators and generator resumes (#1999) v0.4.3 2026-10-23 15:12:48 -05:00
Chris Tateandthekevinbrown 19d6736b01 Look up exports whose names start with "__" by their escaped key
TypeScript keys a symbol table entry whose name starts with `__` with an
extra `_` (`__x` is stored as `___x`; `symbolName` removes it). Native ESM
link checking looked an import up by its own spelling, so a program that
imports such an export compiled to Node's startup SyntaxError:

    // dep.ts
    export const __x = 1;
    // main.ts
    import { __x } from "./dep.ts"; // The requested module './dep.ts'
                                    // does not provide an export named '__x'

`escapedSymbolName`, the inverse of `symbolName`, gives a name's table key.
Link checking, external type lookups, CommonJS export lookups and the
`this` check of ES module exports now look names up through it. `Object.keys`
over a module namespace skipped every key starting with `__` to drop
TypeScript's internal names, so it also dropped these exports; it now
skips only internal names and lists exports by their own names.

Closes #1582.

Co-authored-by: thekevinbrown <1531750+thekevinbrown@users.noreply.github.com>
2026-10-23 15:04:56 -05:00
Chris Tate 850710fbea Box TextEncoder and TextDecoder records as checked-dynamic handles (#2022) 2026-10-23 15:03:43 -05:00
Chris Tate 5bb9fc556d Run Test262 tests that include testTypedArray.js (#2031) 2026-10-23 14:59:25 -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 46b00f69c9 Give glibc's per-thread heaps huge pages
Blocks above 512 bytes come from glibc, which serves each worker thread
from a 64 MiB heap of its own and makes it accessible with mprotect a
page-rounded step at a time. Every 2 MiB range of a heap is first touched
while part of it is still inaccessible, so the heaps never get transparent
huge pages: on a 16-checker type check of VS Code they hold about 0.9 GB on
small pages, close to half of the process's page faults.

Once a worker thread has allocated 16 MiB of large blocks, the heap of each
large block it gets is made accessible whole and advised MADV_HUGEPAGE,
once per heap. glibc's calloc still clears only what lies below its own
high-water mark, so no page is touched early. glibc's mmap and trim
thresholds are set to 4 MiB and 8 MiB, so that the largest blocks get
mappings of their own and a heap's free top goes back to the system: with
huge pages, a heap otherwise keeps resident memory it never writes. The brk
heap policy's own thresholds take precedence once it switches on.

The policy is off unless THP is in madvise mode and malloc is glibc's, and
when the glibc.malloc.hugetlb tunable is set or the system forbids
overcommit. A heap is advised only when /proc/self/maps shows anonymous
mappings covering exactly its range. Library archives leave their
embedder's heaps alone.
2026-10-23 14:30:29 -05:00
Chris Tate 89d122574f Run Test262 tests that include testTypedArray.js 2026-10-23 14:27:20 -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 c296362834 Park typed collection iterators on the entry they read
An early exit from a loop over an iterator-typed Map or Set method parks
the iterator with a second yield of the current entry. That yield read the
entry again by its index, which aborted with a map iteration error when
the loop had deleted the entry. Each step now reads the entry once into a
local that both yields answer.

`entries()` takes the typed walk only for a slot whose yield type is the
key and value pair record. Other yield types, such as
`IterableIterator<any>` or `<unknown>`, keep the checked adapter instead of
failing to compile.
2026-10-23 14:21:21 -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 c24ae2d7e9 Keep caches for property helpers that answer plain values first
Property helpers now start with an exit for values no class table
matches: if the receiver is neither a class capsule nor a function, they
run the generic read or write on it directly. The inline-cache
recognizer accepted only capsule branches and the subclass normalization
before a helper's generic tail, so these helpers stopped being cache
sites. It now also accepts that exit when its operation is the one the
site caches: a read or write of the same key on the receiver.
2026-10-23 14:11:21 -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 08f282e1b6 Merge main and run finish, close and pipeline callbacks in their AsyncLocalStorage context 2026-10-23 13:57:55 -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
Chris Tate 0a98779dd9 Convert arrays and records that hold text codecs to unknown
The recursive boxability check rejected TextEncoder and TextDecoder
records, so an array or record holding a codec could not convert to
'unknown' (SC1101), although the converter boxes a codec as its own
handle at any depth. Codec records are now boxable there, and a live
view of a record or array boxes a codec field or element as its handle
too, instead of a view of the codec's internal fields.
2026-10-23 13:49:22 -05:00
Chris Tate 6fbac8a9a3 Run typed arrays that the checker types any on their native bytes (#2023) 2026-10-23 13:46:39 -05:00
Chris Tate 40de8bf974 Freeze records that other references still hold (#1963) 2026-10-23 13:39:48 -05: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 9f4e654e7a Evaluate an element assignment's receiver and index before its value (#2018) 2026-10-23 13:32:38 -05:00
Chris Tate 47b2dec523 Version two-counter loops over typed arrays (#2021) 2026-10-23 13:28:16 -05:00
Chris Tate 285a92354e Wake TransformStream pulls waiting on a static fetch body 2026-10-23 13:21:29 -05:00
Chris Tate 8b6d3329f4 Hold an element assignment's receiver against its index's effects
When the index assigns the receiver's binding (`a[(a = b, 0)] = c[0]!`),
holding the index alone left the receiver read at the store, after the
index ran, so the value landed in the replacement array. The receiver is
now held whenever the index or the value could change what it reads.
2026-10-23 13:00:50 -05:00
Chris Tate c7fd9dcdf0 Merge main 2026-10-23 12:59:08 -05:00
Chris Tate 0af86e2e3e Version two-counter loops over typed arrays
A loop with two counters closing in on each other, such as
`for (let i = lo, j = hi; i < j; i++, j--)`, checked the bounds of every
typed-array element it read or wrote at either counter. Within the body
the low counter only rises and stays below the high one, which only
falls, so every such index lies between the two starts.

Typed-array loop versions now cover these loops. The guard tests that
both starts are exact integers, that the low start is at least 0, and
that the high start is below the length of each view the loop indexes at
a counter. The fast copy then reads and writes those elements without
checks, and the checked copy runs whenever the guard fails. In the fast
copy the guard also stands for the loop's own start test.

The exact-integer test of a start is now one function, shared by the
typed-array version guards and the dense array two-counter guard. The
dense guard, which compared the round trip with `fcmp oeq`, now also
leaves a -0 start to the checked loop.
2026-10-23 12:58:05 -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
Chris Tate 0aae517c07 docs: render the compatibility matrix's tooltips on demand (#2020) 2026-10-23 12:33:40 -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
Malte UblandClaude Opus 5.5 cda584260f Run typed arrays that the checker types any on their native bytes
A JavaScript binding referenced by its own initializer's inference
collapses to `any` in the checker while it still lowers to a Uint8Array:
hono's etag digest declares `const nextChunk = new Uint8Array(new
ArrayBuffer(f(chunk)))` beside `chunk = nextChunk`. The typed-array
lowerings key on the checker type and declined such a receiver, and the
dynamic paths required a dyn value, so `nextChunk.set(...)` and
`nextChunk.byteLength` threw SC1090/SC2020 at run time and multi-chunk
etag digests failed.

Method calls and property reads on such a receiver now box it by
reference (the box shares the native storage, so writes stay visible) and
dispatch on the runtime kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:33:05 -07:00
Chris Tate 4d70630229 Keep a keyboard-focused badge's tooltip open when the pointer leaves
Hover and keyboard focus now open the tooltip separately, as the
CSS-only tooltip's hover and focus-visible rules did, so moving the
pointer off a focused badge no longer unmounts its tooltip or drops
aria-describedby. Escape still dismisses it.
2026-10-23 12:30:31 -05:00
Chris Tate dbbefae54c Mark typed-array header loads invariant (#2016) 2026-10-23 12:21:01 -05:00
Chris Tateandjonbaldie efa797382e Render the compatibility matrix's tooltips on demand
The matrix mounted every badge's tooltip up front, hidden by CSS, and
re-rendered every row on each keystroke. Tooltips now mount while their
badge is hovered or has keyboard focus, are named by aria-describedby
and close on Escape. Badges and rows are memoized, each row's search
text is built once, the filter makes one pass for its rows and its
count, and the table follows deferred search and status values, with
expand all and collapse all as transitions, so input stays responsive.

The tree and filter passes move to src/lib/compatibility-tree.ts, with
unit tests that also render the badges on the server (pnpm test:unit,
part of pnpm check).

Closes #601

Co-authored-by: jonbaldie <8376953+jonbaldie@users.noreply.github.com>
2026-10-23 12:20:28 -05:00
Chris Tate d891a245ab Clone Response and Request bodies by teeing their streams (#2008) 2026-10-23 12:16:46 -05:00
Malte UblandClaude Opus 5.5 7fb73b7d9d Keep the key order of records spread from values JavaScript built
A literal that starts with `...value` creates value's keys first, in
value's own order. When checked conversions build value's shape (records
JavaScript hands back through replaceable methods, field setters,
generator calls or callbacks), the key-order witness could not order the
literal statically and kept the declared table, so `{ ...draft, id }`
and `{ ...current, ...patch }` enumerated `id` first natively.

Every value of a record shape already enumerates either in its table or
in the order its record source holds: a checked conversion keeps the
source's order there whenever it differs from the table. Such a literal
(an ordered spread) now carries that order over: after it builds the
record, record.sourceSpread gives the record a source listing the keys
it declares in value's order, and the keys value lacks follow in the
table's order, which every literal sequence of the shape agrees with.
The literal then resolves as if value were in its table's order, and its
own shape counts as built at run time, since its values may carry a
source too. It qualifies only when every value of the source has all of
the source's keys, both shapes can keep a source, every later spread of
a shape whose values may carry a source only overwrites the source's
keys, and the source shape resolves (or is the literal's own shape).
Literals of shapes that do not resolve are left as they were.

Programs without such literals compile to identical IR.

Corpus 7750-key-order-javascript-spread-sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:15:58 -07: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
Chris Tate d8816b4731 Evaluate an element assignment's receiver and index before its value
An element assignment whose value is an optional element read or call,
such as `out[n++] = src[n]!`, held the value in a temporary and then
evaluated the receiver and index inside the store, after the value. So
`n++` ran after `src[n]` was read, and `src[0]` was stored where Node
stores `src[1]`. The receiver and index are now evaluated first whenever
the value could change what they read: when it assigns a binding they
read, or runs code that can assign their `let` and `var` bindings or
fields. A literal, or a binding the value cannot change, is still read
at the store.
2026-10-23 12:05:34 -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 029554fbf5 Decide an untyped property write with one own-property lookup (#2014) 2026-10-23 12:04:52 -05:00