Commit Graph
1186 Commits
Author SHA1 Message Date
Malte Ubl 796fcbb9ce Start whole-program int32 slot facts at non-negative int32
The optimistic int32-slots fixpoint assumed exactly int32 for parameter,
result and captured-box candidates. That is too weak for remainders:
with x and n only known to be int32, `(x * 31 + n) % m` may have a
negative dividend (and a -0 result), so a kernel called as
`v = step(v, n)` could not prove step's result, v stayed a double, and
the parameters fell back to doubles too.

Unpinned candidates now start one level lower, at non-negative int32. A
write that is an int32 but may be negative demotes the slot to int32 and
reanalyzes its readers (a parameter that seeds a box demotes the box as
well); any other write drops it as before. Slots only ever widen, so the
fixpoint still ends with every remaining assumption proven at every
write. Int32 contract pins keep their int32 facts. The call-site range
retry is unchanged for slots that fail both levels.

The direct-call kernel now passes i32 to step.i32 and keeps v an i32
slot (sandbox: 2563 ms -> 964 ms, node 1256 ms). Corpus 5323 pins the
non-negative facts and every widening path (negative arguments, results
and box writes, -0 remainders, count-down recursion) against Node.
2026-10-15 04:46:13 -07:00
Malte Ubl 52ed742ae1 Keep the integer-range closure inside the self-hosted subset
The self-hosted compiler lowers nested functions as closure values, and
closure values with optional or defaulted parameters are refused
(SC1090), so the closure's defaulted `invariant` parameter broke the
self-hosting integer-range, emission, LLVM emitter and native driver
builds. closedLocals now takes the invariant ranges explicitly, havoc
keeps its three parameters, and havocWith passes a loop counter's range.

The self-hosting range cases pin the narrowed closed range: x enters as 3
and the region writes 11, so the closures keep [3, 11]; the for loop's
`x = x` initializer still evaluates to the whole assumed bound.
2026-10-15 04:46:13 -07:00
Malte Ubl 79c9c596c1 Bound counters stepped by a loop-invariant positive binding
boundedIntegerLoopFacts only accepted constant positive steps, so the
sieve's inner loop `for (let j = i * 2; j <= LIMIT; j += i)` kept j a
double: addsd, a floating bound compare, and an fptosi/sitofp exactness
check on every typed-array index.

A step may now be a binding the loop never writes (stable: not boxed,
TDZ or captured) whose entry fact is a positive integer range up to
2^31 - 1, added (or a negative range subtracted). Each update then adds
an exact integer in that range, so the counter climbs from its start to
at most the limit plus the largest step; the iteration budget for
secondary cursors uses the smallest step. j becomes an i32 slot updated
with add nsw i32.

Corpus 5322 pins zero, -0, negative, fractional, NaN and infinite steps,
steps written by the body, early exits, and counters crossing 2^31, 2^32
and 2^53 against Node.
2026-10-15 04:46:13 -07:00
Malte Ubl ea8cd32016 Close loop-carried locals under exact integer arithmetic
analyzeIntegerRanges discarded loop-carried facts except for closed locals,
whose writes only qualified syntactically (bitwise results, literals,
imul/clz32). A local like `v = (v * 31 + n) % 1000003` stayed a double, so
every iteration paid fmul/fadd plus the checked floating remainder
(fptosi, exactness and 2^53 range checks, srem, -0 select, sitofp).

The closure now evaluates each write by the same interval rules as
`expr` (exact safe integers, never -0: `*` refuses a zero times a
negative, `%` needs a non-negative dividend and positive divisor, unary
minus refuses ranges containing 0) while every candidate is assumed
inside a fixed bound: int32, uint32, or the new non-negative int32 bound
that `%` closes under. The greatest fixpoint keeps candidates whose every
write lands inside the bound; their range then narrows to the entry fact
joined with the writes' values (sound by induction over the region), and
the closures intersect. A for loop's counter, which only the update
writes, contributes its header range to reads inside the region, so
`h = (h * 131 + i) % 65521` closes too.

The kernel's v becomes an i32 slot with mul/add nsw i32 and urem i32.
Corpus 5321 pins the closed forms and every case that must stay double
(negative dividends and -0, products above 2^53, NaN/Infinity/fraction
entries, zero and fractional divisors) against Node.
2026-10-15 04:46:12 -07:00
Chris Tate f3cd85ea17 Pass results from fs.mkdir, readFile and write callbacks (#1020) 2026-10-15 04:29:45 -05:00
Chris Tate 54b240266c List readdir entries in byte order on POSIX hosts (#1023) 2026-10-15 04:25:48 -05:00
Chris Tate c85f08cb7b Search arrays of objects and strings inline (#1027) 2026-10-15 04:03:09 -05:00
Chris Tate 1dedd5eff0 Search arrays of objects and strings inline
indexOf and includes on arrays of class instances, records, arrays, maps,
sets, typed arrays and strings call an internal helper that LLVM inlines.
When the array's hole-free prefix covers its whole length, the helper
scans the dense slots itself: references compare as 64-bit slots, four
per branch, and strings compare by pointer, then length, then bytes, with
strings of up to 16 bytes compared as two words and longer ones through
scr_str_eq. Arrays with holes or sparse entries, and every other element
type, keep the runtime search.
2026-10-15 03:35:00 -05:00
Chris Tate 6232b532c7 Check corpus 5442's fractional-offset fill over eight runs
randomFillSync(new Uint16Array(32), 8.5, 8) fills bytes 17 to 32, so the
last 4-byte group the check prints holds one random byte, which is zero
1 time in 256, in Node as well. The check now ORs the bytes of eight
fills before it reports the groups. The expected output is unchanged.
2026-10-15 03:19:50 -05:00
Chris Tate 15202442be Merge main 2026-10-15 02:52:25 -05:00
Chris Tate 04ca0618dd List readdir entries in byte order on POSIX hosts
libuv's uv_fs_scandir sorts entries with strcmp on POSIX hosts, so Node's
readdirSync, readdir and fs/promises readdir list names in byte order
(with and without withFileTypes). The runtime returned the directory's
raw order, which differs from Node on most filesystems. The POSIX
scandir listing now sorts its names and entry kinds together, and
scr_fs_readdir reads names from that listing as the Windows path already
did. Windows keeps the directory's own order, as libuv does.

Corpus 5447 checks the listings against Node.
2026-10-15 02:31:42 -05:00
Chris Tate 278be10650 Compare ASCII localeCompare strings eight bytes at a time (#1018) 2026-10-15 02:30:41 -05:00
Chris Tate 97b1a14c53 Add secret KeyObjects: createSecretKey, generateKey and generateKeySync (#1016) 2026-10-15 02:22:30 -05:00
Chris Tate 1843399f25 Pass results from fs.mkdir, readFile and write callback forms 2026-10-15 02:18:22 -05:00
Chris Tate 80cc39a7dd Compare ASCII localeCompare strings eight bytes at a time
The single-pass ASCII comparison now reads both strings a word at a time
when the covered ASCII bytes are TAB..CR and 0x20..0x7E, as the generated
table has them. A word of bytes 0x20..0x7E needs no per-byte coverage
check, equal words have equal primaries, and the rest of both strings is
validated by words after the first differing primary. A non-ASCII byte
answers at once that the strings need the general path; a word with a
tab, line break or another control goes to the per-byte pass. Runs
shorter than a word are read as the eight bytes ending at their last
byte, which stay inside the string object because the header precedes
the data.

The byte-equality check moves after the primary pass on the ASCII path,
where it only runs when every primary ties, and the scan and element-list
path moves out of line, so the ASCII path no longer sets up the element
buffers' stack frame.
2026-10-15 01:59:15 -05:00
Chris Tate 14bcd1aad4 Add secret KeyObjects: createSecretKey, generateKey and generateKeySync
crypto.createSecretKey and generateKey/generateKeySync for "hmac" and
"aes" return SecretKeyObject values, native dyn handles over a copy of
the key bytes. The type and symmetricKeySize getters, export() as a
Buffer or a JWK, equals(), instanceof KeyObject, String(),
JSON.stringify and util.inspect follow Node 24.22, as do the argument
checks, their order and their errors. createHmac and hkdf/hkdfSync
accept these keys. toCryptoKey() and KeyObject.from() need WebCrypto
values and are refused with SC2020.

Dyn handle ops gain a @@toStringTag that differs from the class name
and a flag for views with no properties, which util.inspect prints at
any depth. KeyObject method calls dispatch through dynInvoke.
2026-10-15 01:51:36 -05:00
Chris Tate 1c2c5eef48 Cover the combining Latin letters and vulgar fractions in localeCompare (#1014) 2026-10-15 01:50:58 -05:00
Chris Tate d544a8efe8 Cover the combining Latin letters and vulgar fractions in localeCompare
The localeCompare table now covers U+0363-U+036F, U+00BC-U+00BE and
U+2044 FRACTION SLASH, so strings containing them follow Node's collation
instead of code-point order. A combining Latin letter is its letter's
primary at the letter's secondary, with a tertiary weight between the
letter's case forms where Node places it, and the combining class it
shares with the diacritics it reorders with. A vulgar fraction is the
elements of its compatibility decomposition (1, U+2044, 4), each with the
tertiary weight Node places the fraction at among that position's forms,
so it orders after both 1⁄4 and ¹⁄4. The generator's check adds the
fractions' slash and superscript spellings and the combining letters
around each diacritic. SC6003 and the limitations page describe the new
coverage.

The generated lookup indexes one dense table for every code point below
U+0370, with the gaps between ranges marked uncovered, and checks the
ranges above it (now U+2044) after that, so a Latin lookup is one
comparison instead of up to four.
2026-10-15 01:18:54 -05:00
Chris Tate b245572d31 Compare any view or ArrayBuffer in timingSafeEqual
crypto.timingSafeEqual accepts every TypedArray, DataView, Buffer,
ArrayBuffer and SharedArrayBuffer and compares their bytes. Values of
other types throw Node 24.22's ERR_INVALID_ARG_TYPE for buf1 and then
buf2, before the byte lengths are compared. Views of every element kind
keep the typed call; other arguments take the checks at run time.
2026-10-15 01:15:44 -05:00
Chris Tate bb3c995350 Support randomFill, randomInt callbacks, randomUUID options and randomUUIDv7 (#1012) 2026-10-15 01:11:31 -05:00
Chris Tate 1344e372f6 Run V8's TimSort for comparator sorts (#1002) 2026-10-15 00:44:24 -05:00
Chris Tate 5e9acfcfca Support randomFill, randomInt callbacks, randomUUID options and randomUUIDv7
randomFillSync accepts every TypedArray, DataView, Buffer, ArrayBuffer and
SharedArrayBuffer and returns its argument at its own type. Offsets and
sizes count elements and are checked in bytes with Node 24.22's
ERR_OUT_OF_RANGE and ERR_INVALID_ARG_TYPE errors. randomFill fills the
same buffers and calls back in a later macrotask, or synchronously when
nothing is filled, as Node does.

randomInt takes a callback in both forms, and its errors carry Node's
received values and the max or "max - min" label. Values come from a
6 KiB cache, as in Node, so a callback runs on the next tick while the
cache has data and after an asynchronous refill when it is empty.
randomUUID and the new randomUUIDv7 validate options.disableEntropyCache
and draw from a 128-UUID batch unless it is set.

A non-u8 view passed to randomFillSync no longer needs a Uint8Array
type, and the bytes callback adapters pass views of any element kind
and dyn values through.
2026-10-15 00:41:22 -05:00
Chris Tate 2544ded696 Support every Hash and Hmac encoding, view and option, and crypto.hash's options (#1009) 2026-10-15 00:38:34 -05:00
Chris Tate ffddeb531b Match Node's collation for l·, U+0130 and ligatures (#1011) 2026-10-15 00:27:53 -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 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 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 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 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 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 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 f6e9fbe056 Support subclassing EventTarget and Event (#988) 2026-10-14 18:57:20 -05:00