Commit Graph
2626 Commits
Author SHA1 Message Date
Malte Ubl cae3e3bd7e Extend integer clones to arrays, mutual tail calls and proven ranges
Clones now cover more recursive number shapes:

- Recursion over array indices: a function may take number arrays it
  borrows (the caller keeps them alive for the call) and read `length`
  and elements. An element inside the array's hole-free prefix that is
  exactly an int32 is read inline; holes, other indices and other numbers
  bail to the ordinary body. Borrowed entries get dispatchers too.
- Mutual tail recursion: tail calls between members of a recursive
  component return the callee's result as is, so LLVM inlines them into
  one loop (isEven/isOdd), as it already does for the ordinary body.
- The proven int32-slots ranges of number parameters (valid at every
  call site, recursive ones included) become `llvm.assume`s in each
  activation, so `%` of non-negative values divides unsigned without -0
  handling (gcd).

A clone now overflows the stack where the ordinary body would, instead of
bailing after 2^14 loop iterations. LLVM turns the ordinary body's plain
tail calls into loops, so the clone's plain tail calls (self or mutual)
run unchecked too: endless tail recursion runs forever on either path,
as it did before clones. An accumulating call (`1 + f(n - 1)`, fib's
`f(n - 1) + f(n - 2)`) is real recursion in the ordinary body, because a
double sum is not associative. Its clone loop carries a virtual stack
pointer that starts at the real one (an inlined activation one frame
below its parent's) and drops 32 bytes, about an ordinary frame of these
functions, per iteration; passing the stack guard throws the
stack-overflow RangeError. Deep finite chains stay on the fast path, and
`1 + down(n - 1)` with n = 1e7 still throws like Node
(4984-generator-exceptions, corpus 5342).

Corpus 5343 covers array-index recursion, binomials, holes and
non-integer elements.
2026-10-15 05:26:05 -07:00
Malte Ubl 9b084b5881 Run pure number recursion through speculative integer clones
Functions whose parameters, locals and results are numbers or booleans,
and whose bodies only compute with them, branch, and call other such
functions, get an integer clone (`<symbol>.int`): number parameters
arrive as i32, `+ - * % -x` are overflow checked (a result outside int32,
or -0, bails), bitwise operators are exact, and the result returns as
`{ i32, i1 }` (value, bailed) in registers. Calls between clones need no
pending-exception test: a stack overflow leaves its RangeError pending and
bails, and bails pass through callers unchanged.

Recursive members get a dispatcher with their direct-call ABI. Callers
outside the recursion enter it: it runs the clone when every number
argument is bit-exactly an int32 (never -0, NaN, a fraction or out of
range), and otherwise, or after a bail without a pending exception, reruns
the unchanged double body, which is sound because the clone had no
effect. Calls inside the recursion keep the ordinary entries, so one bail
costs at most one more full run.

Self tail calls and accumulating self calls (`x + f(...)`) become a loop
with an overflow-checked int32 accumulator. Every partial sum and the
result being int32 keeps each double addition of the ordinary body exact.
The loop bails after 2^14 iterations, so recursion too deep for the stack
reruns as ordinary recursion and throws there. One level of non-tail self
calls is inlined into the clone, which removes the call, frame and stack
check of leaf calls.

Programs without clones compile exactly as before.
2026-10-15 05:26:04 -07:00
Chris Tate 54bc586764 Convert nullable records into widened record slots (#1033) 2026-10-15 07:21:16 -05:00
Malte Ubl 5223ec65ad Keep proven module numbers in an i32 local of their initializer
A module-level number that only its file's top-level code reads and
writes (an initializer binding) was already analyzed like a local, so
its loop arithmetic was proven int32, but it kept its double global. A
loop-carried `v = step(v, n)` at top level therefore converted v to a
double and back on every iteration: LLVM promotes the global to a
register, but cannot fold fptosi(sitofp) through the double phi.

When every write is a proven int32 (or uint32), the initializer now
keeps the binding in an i32 local that starts at the global's zero
value. Nothing else can observe the global: no other function reads or
writes it, the guard runs the body at most once, library builds have no
initializer bindings, and import-cycle bindings (checked through their
initialization flag elsewhere) keep their global. Debug builds keep the
global for the variable view.

The top-level closure kernel's v becomes an i32 local. Corpus 5324 pins
localized call results, inline remainders, uint32 hashes, labeled exits
and ++/--, and the bindings that must stay doubles or globals (growth
past int32 and 2^53, -0, fractions, NaN, reads by other functions and
closures) against Node.
2026-10-15 05:07:10 -07:00
Malte Ubl 5053cedc5b Narrow whole-program int32 facts to the values written
Parameter and result facts stopped at their level: non-negative int32 or
int32. A remainder by a parameter therefore never proved its result
(the divisor may be 0), and `(x * 31 + n) % m` kept i64 arithmetic in
the callee because x might be as large as 2^31 - 1, even when every call
passes a divisor of 1000003 and a fed-back result below it.

narrowSlotRanges runs after the optimistic fixpoint as a verified
descending iteration. Each round assumes a range per parameter and
result slot, reanalyzes the functions that read a changed assumption,
and joins every write into each slot. The assumptions are kept only when
they are inductive: every write lands inside its slot's assumed range.
A failing slot falls back to its last verified range, a reanalyzed
function must still prove its field and box writes, and each bound
narrows at most once, so recursion that shifts its argument cannot walk
a bound down one step per round. Results the level dropped are revived
when every return becomes an int32 under the narrowed parameters. The
first round reuses each function's last fixpoint analysis.

`hash(n, m)` with `h = (h * 33 + i) % m` now keeps h in an i32 slot with
urem i32, and `step(x, n)` multiplies in i32. Corpus 5332 pins narrowed
and revived facts and every case that must stay double (zero, negative,
fractional and runtime divisors, -0 remainders, doubling and
counting-down recursion, mutual recursion, results past int32) against
Node.
2026-10-15 05:02:50 -07:00
Chris Tate e10ea93fd3 Support socket and server ref() and unref() (#1032) 2026-10-15 06:54:33 -05:00
Chris Tate 3b379bf623 Convert nullable records into widened record slots
A record parameter of a function the record-flow analysis does not track
widens to the fields its own arguments can leave undefined, and since the
flow-class change those arguments include object literals and call
results. Another call can pass that parameter a value stored as
`R | undefined`: a for-of element whose loop the element analysis cannot
prove free of holes binds `R | undefined` while the checker types it `R`.
Coercing that union into the widened layout of R found no mapping and
failed with SC2003, where the declared parameter used to take the checked
extraction of the record arm.

A union of one record arm and unit arms flowing into a record that arm
lifts to now takes the same checked extraction, then the record's width
lift into the destination layout.
2026-10-15 06:50:27 -05:00
Malte Ubl a56fd8ef4d Pin regex and file-read text surfaces against Node
A corpus program feeds UTF-8 file contents (sync, fd, promise and Buffer
reads, BOM and CRLF lines) through regex test, exec span and full
results, match, matchAll, replace/replaceAll with substitutions and
callbacks, and split by regex, so the faster exec results and in-place
UTF-8 reads are covered beyond exec and readFileSync.
2026-10-15 04:47:05 -07:00
Malte Ubl b275f47fb3 Read UTF-8 files straight into string storage
readFileSync(path | fd, "utf8") (any ASCII-case utf8/utf-8 spelling and
the empty default) used to read into a Buffer, scan it for UTF-8
validity, then copy it into a new string while scanning it again for
ASCII. It now reads directly into the final string allocation, scans
each read chunk for ASCII while it is still in cache, and validates the
remainder in place (scr_str_adopt_utf8_lossy). Only malformed input is
repaired into a new string, with the same WHATWG maximal-subpart U+FFFD
replacement as Buffer#toString; a BOM is kept.

The raw-string readers behind fs.promises.readFile(path, "utf8") and
readFileSync(fd, "utf8") now decode the same way, so malformed bytes and
encoded surrogates become U+FFFD as in Node instead of passing through.

The ASCII prefix scan tests 32 bytes per step (scr_bytes_ascii_span),
which also speeds up Buffer and TextDecoder UTF-8 decoding of long ASCII
runs.

The corpus program covers empty files, BOMs, every malformed-subpart
class, truncation at EOF, chunk-boundary offsets, large ASCII and
multibyte files, /proc pseudo-files, fd and promise reads; the runtime
unit test checks the in-place decode under ASan and the RC audit.
2026-10-15 04:46:55 -07:00
Malte Ubl c863eaa5a7 Speed up regex exec results and parseInt of numeric captures
exec/match results now allocate their slots inline with the array header
(scr_arr_new_inline): one allocation and one free per match instead of a
header plus a separately grown dense block. The capture fill loop keeps
the storage pointers in registers instead of reloading them after every
slot store.

parseInt(digits, 10) of a short all-digit string (1-15 digits, the shape
of numeric fields and regex captures) now converts directly. The general
path no longer divides per digit to detect u64 overflow (one bound per
call; the exact bignum fallback is unchanged) and skips fmod for integral
radixes in 2..36.

The new corpus program pins match-array mutation past the captured length
(push, length, unshift, sort, splice, reverse, slice) with index/input,
and parseInt across lengths, signs, prefixes, overflow and radix forms.
2026-10-15 04:46:38 -07:00
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 740bd38e0f Support socket and server ref() and unref()
socket.ref()/unref() and server.ref()/unref() decide whether the handle
keeps the event loop alive, on typed handles and through checked-dynamic
handles. The loop's net liveness now counts only ref'd sockets and
servers; unref'd ones still deliver their events while other work keeps
the loop running, because the sleep polls the net unit's fd whenever any
handle exists.

Corpus 5595 covers the server, client and accepted sockets.
2026-10-15 06:24:48 -05:00
Chris Tate 416311f2c5 Append to strings in place for fields, concat chains and character codes
The runtime appends to a string in place when the left operand is
uniquely owned, but three common forms never got there:

- `obj.field += s` on a plain class or record field kept a second
  reference to the old value, so every append copied the whole string.
  The compound assignment now reads a stored string field into a mutable
  local, stores "" into the field (no code runs in between: both operands
  are already strings), runs the self-append `old = old + rhs` and stores
  the result.
- `s = s + a + b` copied per append: the self-append recognized only
  `s = s + x`. A concat chain whose leftmost operand is the target now
  takes the remaining operands as the suffix, in the same order.
- `s += String.fromCharCode(c)` built a one-character string first.
  scr_str_concat_move_char_code appends the character, in place for ASCII.

Self-appends also borrow the suffix instead of retaining it, and a
discarded compound assignment no longer retains and releases its result.
2026-10-15 06:17:38 -05:00
Chris Tate 97a9257fa5 Inline string retains and dynamic value reference counting in release builds
Release builds now emit the string retain and the dyn retain and release
fast paths inline. The dyn release decides the cases scr_dyn_release
decides by kind and calls scr_dyn_release_survivor for capsules and
handles. String releases and the other families stay runtime calls, as
string-heavy programs have many release sites. The speed posture keeps
every fast path and gains the dyn release. The runtime ABI version moves
to 21 because program objects call the new runtime entry point.
2026-10-15 05:15:33 -05:00
Chris Tate 9c11a8d1ff Merge main 2026-10-15 04:54:28 -05:00
Chris Tate bbc1d6d0b0 Merge main 2026-10-15 04:52:43 -05:00
Chris Tate af67284e3a Merge main 2026-10-15 04:50:57 -05: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 7a96feb962 Merge main 2026-10-15 04:11:04 -05:00
Chris Tate c85f08cb7b Search arrays of objects and strings inline (#1027) 2026-10-15 04:03:09 -05:00
Chris Tate 3cd856b61b Merge main 2026-10-15 03:59:47 -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 a08fbd2746 Merge main into createCipheriv support 2026-10-15 03:12:04 -05:00
Chris Tate 9a1effd421 Complete fs.Stats in the typed and callback lanes 2026-10-15 03:03:32 -05:00
Chris Tate 15202442be Merge main 2026-10-15 02:52:25 -05:00
Chris Tate 0b27e07e7a Merge main 2026-10-15 02:38:31 -05:00
Chris Tate 5d3315e268 Merge main 2026-10-15 02:36:37 -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 4819818cdf Add assert.AssertionError with Node's properties and constructor
Assertion failures throw an assert.AssertionError with Node 24.22's
generatedMessage, code, actual, expected, operator and diff properties,
in Node's key order. new AssertionError(options) validates its options
and builds Node's message from operator, actual and expected.
AssertionError type annotations, instanceof and assert.throws(fn,
AssertionError) compile.

Failure paths record actual, expected and operator before they throw;
passing assertions do no extra work. The deep forms lower through a
per-type function that returns the verdict, so the operands stay call
temporaries. assert.equal and notEqual inspect their operands only when
they fail.

Values without a checked-dynamic form, and values whose type can be
cyclic, record undefined.
2026-10-15 02:27:13 -05:00
Chris Tate 07ce602de5 Close Node 24 gaps in util.types keys, HTTP header limits and unread requests
- Object.keys(util.types) lists Node's 43 keys in Node's order, and
  Object.keys over the node:util/types namespace lists them sorted with
  "default". Object.values and Object.entries over util.types are refused,
  because some members do not compile yet.
- The HTTP server answers 431 Request Header Fields Too Large when a head
  or trailer block exceeds 1000 fields or the header byte bound, and the
  trailer block counts its fields on its own, like Node. Parser errors send
  Node's fixed responses without a Content-Length header.
- Server requests start paused like Node's IncomingMessage: body bytes and
  'end' wait for a 'data' listener, pipe() or resume(). When the response
  finishes, an unread request is dumped and emits 'end', before the
  response's 'close' for an empty body and after it otherwise. A body that
  is still arriving when the response finishes keeps reaching its
  listeners. req.complete reads true once the body has arrived.
- Document that small Buffers own exact-size ArrayBuffers instead of
  sharing Node's 64 KiB pool.

Corpus 5405-5409 cover each case.
2026-10-15 02:26:31 -05:00
Chris Tate 97b1a14c53 Add secret KeyObjects: createSecretKey, generateKey and generateKeySync (#1016) 2026-10-15 02:22:30 -05:00
Chris Tate 5336966743 Merge main into createCipheriv support
Keeps the cipher support together with the hash, random and
timingSafeEqual surface added on main.
2026-10-15 02:22:02 -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 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