Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate 1c2c5eef48 Cover the combining Latin letters and vulgar fractions in localeCompare (#1014) 2026-10-15 01:50:58 -05:00
Chris Tate a06d3a6099 Compare any view or ArrayBuffer in timingSafeEqual (#1013) 2026-10-15 01:47:26 -05:00
Chris Tate 71deae1acf Read dyn array elements and lengths by number without a key string
Indexing a value typed unknown with a number, such as `items[i]` on an
unknown[], converted the index to a string and looked it up as a property
key, and `items.length` read the property by name and boxed the result.
In a loop over an unknown[] that costs a string allocation, a key parse
and a number box per iteration.

The computed-read and `length` helpers the class dispatch generates are
now marked for a numeric fast path. A call to one of them with a number
index first asks the runtime for the element of a dyn array or a live
array capsule (scr_dyn_index_get), and a `length` read checked as a number
asks for the array's length (scr_dyn_array_length). The key string is
built and the helper runs only when the receiver is anything else, so
objects, strings, proxies, class instances, holes and out-of-range or
non-index numbers keep the keyed path.
2026-10-15 01:21:46 -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 61d38f9dbe Check a stream chunk's kind after the read that produced it
A typed read() checked the stream's representation before reading, but
the read itself can change it: a lazy Readable.from pull over a dynamic
array makes the stream hold strings with its first string, and _read can
call setEncoding(). The string then came back typed as a Buffer. Reads
now answer each chunk's kind as it was taken from the buffer: typed
read() and the typed iterator throw the encoding fence for a string, the
dynamic read boxes by that kind, and entries released after a 'data' emit
are released by it.
2026-10-15 00:59:09 -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 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 88d83c2ad9 Link scr_hash.c into the sort unit test
scr_lib.c calls the digest functions in scr_hash.c since #1006, and sort.test.ts copied array.test.ts's source list from before that change, so the test binary failed to link (undefined scr_ripemd160_init and others). The list now matches array.test.ts's.
2026-10-15 00:16:52 -05:00
Chris Tate 1ac40a220b Strip VT control characters in one pass (#1010) 2026-10-15 00:13:02 -05:00
Chris Tate 9964e83818 Format the crypto lowering with oxfmt
Wraps the lines in builtins/crypto.ts that exceed the formatter's width, so pnpm format:check passes.
2026-10-15 00:10:51 -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 a5064d23a5 Add 8-byte allocator size classes below 128 bytes (#1003) 2026-10-14 23:36:08 -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 229e336382 Hold TimSort gallop bounds in ptrdiff_t and check gallop results
The gallop searches in the comparator sort held their search bounds in
long, which is 32 bits on Windows x64, so a run of more than 2^31 slots
gave a truncated bound and the merges could copy outside the array. The
bounds are now ptrdiff_t; every other index, length, offset, hint, run
length and run-stack entry in the sort was already size_t. Each gallop
result is checked against its run length (0 <= k <= length) before any
merge uses it, and a result outside the run is an internal error rather
than a copy.

A new runtime unit test calls gallop and the merge step, through test-only
entry points built with SCR_SORT_TEST, at indices past 2^32 and over runs
of more than 2^32 slots in a sparse mapping, and checks that an
out-of-range gallop result traps.
2026-10-14 23:13:45 -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 b25443f44a Restore main's changes to the JSON runtime and runtime header
The previous commits carried copies of scr_json.c and scr_runtime.h
without changes main made to them; this keeps both main's changes and
this PR's.
2026-10-14 23:05:28 -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 84c8706b31 Restore the per-architecture parseInt rounding from main
The previous commit carried pre-#993 copies of the string runtime and its
oracle tests; this puts main's versions back.
2026-10-14 23:00:33 -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 9010aa3435 Restore the per-architecture parseInt rounding from main
The previous commit carried pre-#993 copies of the string runtime and its
oracle tests; this puts main's versions back.
2026-10-14 22:58:26 -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 74e8dec17f Match typed JSON.parse member names against the text and read scalar members inline (#995) 2026-10-14 22:29:44 -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 76dee28d8a Add 8-byte allocator size classes below 128 bytes 2026-10-14 21:28:04 -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 def59ed975 Merge main 2026-10-14 21:22:07 -05:00