Commit Graph
1933 Commits
Author SHA1 Message Date
Chris Tate eca815da02 Merge main 2026-10-25 10:55:32 -05:00
Chris Tate b7915b92a7 Merge main 2026-10-25 06:38:44 -05:00
Chris Tate c8b40bd73e Move small number-key maps to the direct index from the second key (#2204) 2026-10-25 06:34:30 -05:00
Chris Tate cac5a19fc0 Fill Buffers with multi-byte values, swap byte order and decode utf16le a word at a time
- fill with a string or Buffer value longer than one byte: writes the
  value once, then doubles the filled prefix with memcpy, as Node does.
  The value is copied with memmove, so a value that is a view of the
  buffer being filled gives Node's result; the previous per-byte loop
  read such a view while overwriting it. The loop also computed a
  division per byte.
- swap16, swap32 and swap64: one load, byte swap and store per group,
  through a local data pointer.
- toString("utf16le" | "ucs2"): four units below 0x80 copy as four bytes,
  and other units outside the surrogate range are written inline (a
  three-byte sequence as one four-byte store). Surrogates keep the
  previous path. The result records its UTF-16 length (one unit per
  input unit), or is marked ASCII, so `.length` does not walk it.
2026-10-25 05:58:42 -05:00
Chris Tate b3c98139e4 Move small number-key maps to the direct index from the second key
A number-keyed Map or Set with keys the direct index allows now moves to it
at its second entry rather than its fifth, so a map over a few small
integer keys (a histogram over 0..3) reads through the inline direct index
instead of the out-of-line linear scan.
2026-10-25 05:48:40 -05:00
Chris Tate edae637526 Support named properties and Symbol.species on typed arrays held as untyped values (#2068) 2026-10-25 05:48:27 -05:00
Chris Tate cbc062a35f Skip NULL children and key-cache walks when disposing dynamic values
Disposing a checked-dynamic node released each entry's getter and setter
and the node's prototype and symbol tables through scr_dyn_release, though
almost all of them are NULL: in a zod validation loop, over half of all
dyn releases. Dispose now tests each child first.

A pooled object also walked all its slots again on reuse, to take the key
bytes it kept back from the pool's key budget. It now records that total
when it is pooled, and reuse subtracts it.
2026-10-25 05:03:04 -05:00
Chris Tate 03b754e891 Convert dynamic values into nested unions in one pass (#1914) 2026-10-25 04:18:21 -05:00
Chris Tate 45171ea02c Use a built-in typed-array constructor as its own species before the species symbol exists
A typed array whose constructor property is set to another built-in
typed-array constructor now creates that kind from map, filter, slice
and subarray when the program has not evaluated Symbol.species.
2026-10-25 03:59:10 -05:00
Chris Tate 9cb9750f92 Set a trusted build aside while a getter runs outside a visit session
A getter or proxy trap that runs during a trusted build outside any
visit session now runs with the build's state set aside, as inside a
session. A conversion the getter starts, such as a recursive union
that opens and ends its own session, no longer clears the mark that
keeps the rest of the outer build from trusting its earlier match.
2026-10-25 03:34:33 -05:00
Chris Tate 975270cef5 Decode base64 eight characters at a time
Buffer.from(text, "base64" | "base64url") decoded each four-character
group with four lookups and a sign test, then stored its three bytes one
at a time. The decoder now first takes complete groups two at a time:
each character's value comes from a table that holds it already shifted
into place within its group's 24 bits, a single bit test covers all
eight characters, and each group's three bytes go out in one four-byte
store whose last byte the next store overwrites. A group with any other
character (padding, whitespace, an invalid character) takes the
previous path, and the two-group step resumes after it, so line-wrapped
text keeps most of the gain. The destination already holds two spare
bytes, which cover the extra store byte.
2026-10-25 03:02:53 -05:00
Chris Tate 12bbb9bde6 Keep a throttler's pending-call storage within its live calls
A queue that never emptied kept the slots of calls already taken, so its
storage grew with lifetime traffic even when maxPending bounded the live
calls. Before growing, the queue now moves its live calls to the front.
2026-10-25 03:00:30 -05:00
Chris Tate 0eef6251de Merge main 2026-10-25 02:28:32 -05:00
Chris Tate db2cb5c99e Split readline input in one scan and copy ASCII strings into buffers whole
readline split redirected input byte by byte, twice per chunk (once to
find whether the chunk ends a line, once to split it). Before splitting
it copied each chunk out of its Buffer and into a scratch buffer, decoded
it into a new string, and copied the unfinished line and the whole chunk
into a joined buffer. Now:

- one scan per chunk: memchr for a CR once, then LF to LF with memchr
  when the chunk has none, and the LF, CR and CRLF split otherwise;
- the unfinished line joins only the first line of the chunk;
- an ASCII chunk with no partial UTF-8 sequence held from the previous
  one is split where it is, and a chunk that is not shared memory is
  read from its own storage. Lines are still copied into their strings
  before the first is delivered.

Buffer.from(string, 'latin1' | 'ascii') encoded character by character;
an ASCII prefix, all of an ASCII string, is now copied with memcpy.

Line events, their order and the CRLF delay are unchanged. Corpus 8344
covers lines joined across chunks, a line longer than a chunk, empty
lines, mixed line endings and UTF-8 split across chunks. The new
line-reader workload reads a 67 MB log through readline over a file
stream.
2026-10-25 01:35:28 -05:00
Chris Tate 25d0ad9193 Parse JSON into discriminated unions in one pass (#2189) 2026-10-25 01:16:43 -05:00
Chris Tate 5d3e0c7a8b Support X509Certificate fields and methods, TLS certificates and tls.Server contexts (#2052) 2026-10-25 01:00:24 -05:00
Chris Tate 4963dcf8ec Cache absent properties in the dynamic tier's property caches (#2186) 2026-10-25 00:46:10 -05:00
Chris Tate e5ba073468 Parse JSON into discriminated unions in one pass
`JSON.parse(text) as T` for a discriminated union of records took the
checked-dynamic route: it parsed the text into a dynamic tree, matched
and copied that tree into records, then freed it. The one-pass schema
parser now builds these unions directly, as record members, array
elements and whole documents. For an object, a shallow scan of its
members finds the discriminant's value, which selects the one record arm
that holds that literal, and the object is parsed with that arm's
schema. An object with a missing, non-string or unknown discriminant, or
any member the arm rejects, declines, and the checked route then gives
the result or the error as before.

The compiler admits unions whose record arms each name their string
literals, every literal naming one arm, plus null and undefined arms.
2026-10-25 00:44:25 -05:00
Chris Tate 713f28dcd2 Merge main 2026-10-25 00:27:44 -05:00
Chris Tate 3e54e0edb3 Read fs.promises as a module and check fs read and write arguments as Node does (#2098) 2026-10-25 00:23:19 -05:00
Chris Tate 2269fd9e1f Merge main 2026-10-24 23:48:53 -05:00
Chris Tate 3e115ce08c Cache absent properties in the dynamic tier's property caches
A read site's cache only remembered properties it found on the receiver
or its first prototype. Reads of properties an object lacks, which
validation libraries make constantly (zod reads ctx.skipChecks,
payload.memo, x.aborted and _zod.optin on every check), walked the whole
shape chain and then repeated the lookup on the generic path every time:
7.9 million such reads in one zod benchmark run.

- Read sites call a new probe, scr_js_get_probe, that answers undefined
  for a key the receiver and its chain of plain shaped objects (at most
  one prototype, ending in the default prototype) lack, and caches that
  answer. The entry holds while the receiver's shape, its prototype link,
  and that prototype's shape are unchanged. Its tag and placeholder
  holder make the inline entry-0 test in compiled code miss, so the
  answer always comes from the probe. Method reads keep
  scr_js_load_probe, so a missing method still reaches the slow path and
  its "is not a function" error. Names of Object.prototype members are
  never cached as absent.
- Receivers of one shape with different prototypes (zod's internals
  objects) each keep an entry instead of replacing one another.
- A site whose four-way record is full consults a per-thread table keyed
  by shape and key before walking shapes, and fills it.
2026-10-24 23:47:41 -05:00
Chris Tate 37031fa7c2 Convert a loop's string receiver to UTF-16 at once, and publish it only when the scan holds another string (#2180) 2026-10-24 23:34:50 -05:00
Chris Tate 6a29947998 Run the native compiler's nursery passes every 1024 candidates
The native compiler now starts with the cycle collector's nursery trigger
at 1024 new candidates instead of 256. scr_cyc_set_nursery_threshold sets
the trigger that SCR_CYCLE_THRESHOLD sets, on the pattern of
scr_cyc_set_growth_cap; the environment variable still takes precedence.

The compiler sets it from a constructor in its host object, before its
modules initialize. Set from main, the trigger leaves the build's
collection work where it was: the cost depends on the collector's first
passes. Programs keep the default trigger.

A cold tsc-ts build takes 4% less wall time (12 paired runs) and its peak
RSS goes from 1.41 GB to 1.31 GB. The program's IR is unchanged.
2026-10-24 22:59:02 -05:00
Chris Tate add5f4869b Convert a loop's string receiver to UTF-16 at once, and publish it only when the scan holds another string 2026-10-24 22:53:15 -05:00
Chris Tate be878b4eb9 Freeze class instances held in untyped values (#2061) 2026-10-24 22:43:12 -05:00
Chris Tate 0fbb47382d Read elements of native arrays passed to untyped code by index (#2171) 2026-10-24 22:32:09 -05:00
Chris Tate b08efe27e5 Complete consecutive synchronous writes in one afterWrite step (#2169) 2026-10-24 22:24:33 -05:00
Chris Tate 4241ca533f Read elements of native arrays passed to untyped code by index
A typed array such as a number[] reaches untyped code as a capsule of the
native array. A keyed read `xs[i]` there missed the inline cache, which
answers only dyn arrays, and went to the member helper, which converted
the index to a string; the capsule's key lookup then parsed the string
back into a number for every element.

Keyed sites now read an element of a native number[] capsule in place
when the index is an integer below the array's hole-free prefix, and
`length` sites read the native array's length. Other capsule elements
(holes, out-of-range and fractional indices, arrays of other element
types) are answered by the keyed probe through scr_dyn_index_get without
forming the key, or by the helper as before.
2026-10-24 21:59:28 -05:00
Chris Tate d6c8b0f260 Close the afterWrite batch whenever an afterWrite tick runs
Node's afterWriteTick sets afterWriteTickInfo to null before it runs the
callbacks, whichever tick it is. The runtime cleared the queued batch
only when the running tick was that batch, so a write made from an
earlier tick's callback joined a later queued tick with the same
callback and completed ahead of nextTicks queued between them. Any
afterWrite tick now closes the batch, and such a write queues its own.
2026-10-24 21:52:18 -05:00
Chris Tate 7b946ff610 Read Math, JSON and Reflect as real objects in JavaScript (#2006) 2026-10-24 21:50:17 -05:00
Chris Tate 492d1558be Complete consecutive synchronous writes in one afterWrite step
Node's onwrite queues one afterWrite nextTick for a synchronous write
completion and lets later synchronous completions with the same callback
join it (afterWriteTickInfo.count); writes without a callback share one.
The step then checks 'drain' once, calls the callback once per write and
runs finishMaybe once. The runtime queued a stream tick and a nextTick
entry per write, which ran the callbacks of later writes after nextTicks
queued between them. The queued afterWrite tick now counts the writes
that join it, as Node's does.
2026-10-24 21:22:54 -05:00
Chris Tate 8f6f8929dd Merge main 2026-10-24 21:15:49 -05:00
Chris Tate 90f0dd3dc5 Merge main 2026-10-24 21:09:40 -05:00
Chris Tate 0eb7cc12e5 Reuse symbol storage keys and checked-dynamic symbol boxes
Every read or write of a symbol-keyed property on a checked-dynamic value
formatted the symbol's address into a new string to find its storage key,
and every conversion of a symbol to a checked value allocated a new box.
Programs that key hot properties by symbols, such as Effect's type ids
checked on each combinator, spent a large share of their time there.

The storage key is a function of the address, so a 256-entry per-thread
cache by address now returns the existing string, released at exit. A
symbol also keeps a weak pointer to its live box: conversions share it,
and the box clears the pointer when it is freed.
2026-10-24 20:51:37 -05:00
Chris Tate 03af6fb9ff Iterate classes with object-literal iterators and close them on early exits (#1860) 2026-10-24 20:30:36 -05:00
Chris Tate f1bc74f934 Give the module namespace handle its own tag (#2161) 2026-10-24 20:29:29 -05:00
Chris Tate e6a8d735ca Bound strict throttle state and evaluate MIME arguments before the call
A strict throttler kept every invocation timestamp until its whole window
emptied, so sustained traffic grew the array without bound; ticks that left
the window now give their slots back, and at most limit ticks are live.

new MIMEType, new MIMEParams and MIMEType.parse in JavaScript evaluated
surplus arguments after the call. All arguments now evaluate first, in
order, as in JavaScript, so a surplus argument's effects happen before the
constructor coerces or throws.
2026-10-24 20:20:39 -05:00
Chris Tate 320a97bf3b Throw Node's TypeError for util.diff of two distinct empty sequences
util.diff([], []) with two arrays read one element past its trace row
while backtracking. Node's myersDiff answers undefined for two empty
sequences, and util.diff's ArrayPrototypeReverse throws "Cannot convert
undefined or null to object"; scr_util_diff now throws the same TypeError
before it allocates the trace.
2026-10-24 20:19:50 -05:00
Chris Tate 304612b7e2 Inline destructured literal results and read ASCII slice ranges inline (#2151) 2026-10-24 20:11:44 -05:00
Chris Tate c091adc0e4 Build symbol-keyed fields into frozen class views whatever their attributes
A JavaScript class whose instances track which fields they have copies
the instance's property bag into its view first, attributes included,
and then wrote each symbol-keyed field with an ordinary assignment. Once
the instance was frozen, its symbol entry was read-only and the copy
non-extensible, so any later reflection that rebuilt the view
(Object.isFrozen, a property read) threw "Cannot assign to a read only
symbol property".

String-keyed fields already use scr_dyn_obj_set_field, which replaces a
field's value whatever the attributes copied into the view. The new
scr_dyn_symbol_field_set does the same for symbol-keyed fields, and the
view uses it when it copies the bag or carries integrity attributes.
2026-10-24 20:09:28 -05:00
Chris Tate 50421e1cbd Merge main 2026-10-24 20:05:11 -05:00
Chris Tate c41af6c36d Move the MIME handle tags to 81 and 82
Main now uses tag 78 (STRING_DECODER), 79 (MODULE_NAMESPACE, after #2161)
and 80; 81 and 82 are the next free values.
2026-10-24 19:04:38 -05:00
Chris Tate e0db07a46d Merge main 2026-10-24 19:03:30 -05:00
Chris Tate 97c3b04f73 Merge main and add the Node 24 util batch 2026-10-24 18:55:14 -05:00
Chris Tate 53bddc6ed7 Give the module namespace handle its own tag 2026-10-24 18:46:36 -05:00
Chris Tate 9e55835014 Compile more npm and CommonJS JavaScript dependencies statically (#2122) 2026-10-24 18:30:56 -05:00
Chris Tate 6997d8b211 Allocate stream ticks from the small-block allocator, and emit stream events by immortal names (#2152) 2026-10-24 18:22:03 -05:00
Chris Tate 6c18c58708 Merge main 2026-10-24 18:21:25 -05:00
Chris Tate 29e5bacb9c Pump untyped iterable pipeline sources (#2111) 2026-10-24 18:03:36 -05:00