Commit Graph
1647 Commits
Author SHA1 Message Date
Chris Tate ac3cb1c98f Merge main 2026-10-22 22:59:19 -05:00
Chris Tate 97921c2764 Read and write Error.stackTraceLimit through the Error constructor value (#1932) 2026-10-22 22:21:42 -05:00
Chris Tate 143f2e11e9 Keep an own stackTraceLimit on the Error value ahead of the limit 2026-10-22 22:15:57 -05:00
Chris Tate 75cf8f0e52 Run zod 4's core constructor factory in the dynamic tier (#1918) 2026-10-22 22:08:46 -05:00
Chris Tate 6a1e5103f0 Array keys and properties: non-index number keys, Object.keys, console.log (#1769) 2026-10-22 22:00:27 -05:00
Chris Tate 77c0636826 Include star re-exports in namespace objects, and read causes and instanceof Error like Node under a runtime Error parent
- A module namespace converted to an untyped object, and a computed
  namespace read, use the module's resolved exports, which include the
  members of `export *` declarations.
- `super(message, options)` through a runtime Error parent installs the
  cause as Error does: an inherited `cause` counts, the value is read
  through its getter, and the getter's exception propagates.
- `value instanceof Error` on an instance of a class whose heritage is
  evaluated at run time tests that class's prototype chain for
  Error.prototype. A program with such a class checks prototype chains
  for `instanceof Error` on untyped values.
- The cache of names written on the global object is a Lowerer field
  instead of a WeakMap keyed by the Lowerer.
- errors.test.ts: `"prototype" in Map` answers from the builtin
  constructor's keys now, so it leaves the refused reads and joins a
  check of `in` probes against Node's answers.
2026-10-22 22:00:01 -05:00
Chris Tate 12166956b8 Spread protocol iterables into Math.min and Math.max
Math.min(...src) and Math.max(...src) over an untyped source now take its
values the way a spread does: arrays, strings, bytes and collections pack,
a native iterator (a Map's keys()) drains, and other iterables (a generator,
an object with a Symbol.iterator method) step their iterator to the end.
Before, only the packing kinds were accepted and every other iterable threw
the spread TypeError.
2026-10-22 21:57:29 -05:00
Chris Tate 09c132fb2e Merge main 2026-10-22 21:55:35 -05:00
Chris Tate e0f6f094bf Keep int32 slots for long number arrays with checked integer stores (#1931) 2026-10-22 21:46:03 -05:00
Chris Tate e665913200 Print accessor properties of arrays and fence number keys in defineProperty
- An enumerable getter or setter on an array printed as undefined: the
  property loop rendered the accessor's unused data slot. It now prints
  [Getter], [Setter] or [Getter/Setter], as util.inspect does.
- Object.defineProperty on a live native array with a number key that is
  not an array index (-3, 1.5) skipped the numeric-property fence and
  lost the property on commit. A number key now takes the same fence as
  its string spelling.
2026-10-22 21:39:37 -05:00
Chris Tate fe406429eb Keep int32 slots for long number arrays with checked integer stores
A local number[] candidate whose stores are exact integers but not all
proven int32s (a distance array updated with dist[v] = dist[u]! + 1), and
that is only ever allocated by new Array(n).fill(v), now holds SCR_ELEM_I32
slots when n is at least 4096. A shorter fill creates doubles, which fit a
typical L1 data cache. The first store of a value that is not an int32
widens the array to doubles in place (scr_arr_widen_i32).

An innermost loop that accesses such arrays tests their kinds once on
entry (backend/llvm/int32-kind-loops.ts). Its fast copy reads and writes
them as int32 arrays; a store that would widen one leaves for a copy that
re-executes it with kind tests and finishes the loop on doubles. When the
arrays already hold doubles, a copy accesses them as double arrays. Other
accesses test the kind each time.

Only these arrays take kind tests: every other array, and every program
without one, emits the same code as before.
2026-10-22 21:37:12 -05:00
Chris Tate 1501559d4a Read and write the "undefined" property for element indexes that are undefined
An element index the checker types as a number can hold undefined at run
time (an element read from a holey array). JavaScript then names the
array's "undefined" property, but the index converted with ToNumber, so
such reads and writes used the "NaN" property: Node prints `10 0,7,0,7`
for a program that sets `values[NaN] = 10` and then writes through two
holes, the compiled program printed `7 0,7,0,7`.

The runtime's noncanonical property table, which holds the properties
named by non-index numbers, now spells one NaN payload
(scr_arr_undefined_key) "undefined". At array element reads, writes,
compound and update writes, destructuring targets and writes in value
position, an index that can be undefined is that key when it is
undefined. The property stays a typed entry of the array, so the Int32[]
contract checks cover it like any element store.

A typed array cannot hold the property: a write through an undefined
index throws the catchable SC1090 error where Node adds it.

Fixes #1640.
2026-10-22 21:34:06 -05:00
Chris Tate e470edc111 Merge main 2026-10-22 21:24:30 -05:00
Chris Tate 107d773382 Pass zlib's flush sentinels to _transform as Buffers
flush() on a zlib stream writes a zero-length chunk that _transform
recognizes by identity and compresses with that flush kind. Since
writes of a typed array that is not a Buffer reach _write as a Buffer
over the same bytes (as Node's Stream._uint8ArrayToBuffer does), the
sentinel, created as a plain Uint8Array, reached _transform as a new
Buffer. _transform then used the default NO_FLUSH, so flush() emitted
nothing: corpus 5374-zlib-stream-flush-params decoded an empty prefix
where Node decodes "hello ".

The sentinels are Buffers, as Node's kFlushBuffers are (Buffer.alloc(0)),
so write() passes them through unchanged.

The npm fixture stream-shims, whose test expected a deferred SC1090, now
runs to a later deferred refusal, SC2020 (write completion callbacks
taking the error argument); its expectation names that code.
2026-10-22 21:14:00 -05:00
Malte UblandClaude Opus 5.5 3041df387d Spread untyped iterables into Math.min and Math.max
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:59:56 -07:00
Malte UblandClaude Opus 5.5 7f7e11a1a7 Read and write Error.stackTraceLimit through the Error constructor value
zod 4 saves and restores the limit through an alias,
`const E = "captureStackTrace" in Error ? Error : null`. The interned
Error constructor value now answers `stackTraceLimit` from the same limit
the static member and stack capture use, and numeric writes set it, so
`E.stackTraceLimit` reads 10 instead of undefined and `E.stackTraceLimit = 0`
reaches `Error.stackTraceLimit`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:59:34 -07:00
Chris Tate 7b79d73a55 Store local number arrays of proven int32s in 4-byte slots
A local number[] that only compiled element operations reach, and whose
every stored element the int32 slot analysis proves an int32 (never -0),
is now stored as SCR_ELEM_I32: 4-byte int32 slots, with state bytes once
it holds a hole or undefined, as for every array. Element reads give
consumers an exact int32, so indexing with an element skips the double
round trip, and the slots take half the memory.

The runtime's dense slot accessors convert between the int32 slot and the
double, so its double-valued entry points serve int32 arrays unchanged;
growth, copies, fill and length changes use the 4-byte width.

ir/int32-arrays.ts proposes the candidates and inlines their element-reader
helper calls; the int32 slot analysis keeps the proven ones, and
selectInt32Arrays rewrites the module for exactly those, so a program
without an int32 array emits the same LLVM IR as before.
2026-10-22 20:52:38 -05:00
Chris Tate dcd86a17da Merge main 2026-10-22 20:52:05 -05:00
Chris Tate a9ee38611e Register the global object's names again after a library re-init 2026-10-22 20:20:56 -05:00
Malte UblandClaude Opus 5.5 122c98cf4f Read the global object without rebuilding its name table
Every evaluation of `globalThis` as a value built an array literal of
every name the global type declares (hundreds of strings) and registered
each one in the known-name table again. A server that reads a singleton
through `globalThis` per request spent about a tenth of its time there.

Emit the name table as one immortal static array per program (the
tagged-template site array, keyed by its contents), and return the
global object at once when the runtime sees a table it has already
registered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:10:05 -07:00
Chris Tate 1660531c7b Support FinalizationRegistry with bookkeeping and no cleanup callbacks (#1854) 2026-10-22 20:03:31 -05:00
Chris Tate 62a0426193 Keep Linux poller timers in a heap behind one timerfd (#1896) 2026-10-22 20:02:08 -05:00
Chris Tate 636ffa21be Merge main 2026-10-22 19:59:51 -05:00
Chris Tate 56149fde04 Count each epoll test deadline from its own arm call
The poller test recorded every initial deadline as the same start time
plus the delay, while the poller reads the clock at each arm call; when
arming the 2,000 timers took over 1 ms, the recorded order could differ
from the delivered one by more than the assertion's slack. Each deadline
now counts from the time read just before its scrp_timer_arm call, as
the re-arm loop's do, and a 3 ms pause halfway through arming keeps the
test exercising that case.
2026-10-22 19:56:53 -05:00
Chris Tate b7d355ed13 Stringify live typed records and arrays without a key list (#1897) 2026-10-22 19:52:19 -05:00
Malte UblandClaude Opus 5.5 f210b1102a Run zod 4's core constructor factory in the dynamic tier
zod 4 builds every schema through `$constructor`, a factory of JavaScript
patterns the dynamic tier refused or answered wrongly. Each is fixed
generally:

- `"key" in BuiltinCtor` (`"captureStackTrace" in Error`) folds to Node's
  answer from a table of the builtin constructor globals' keys.
- A class whose heritage evaluates to Object or Error at runtime
  (`class D extends (params?.Parent ?? Object)`): Object means no
  heritage; Error links Error.prototype and super() installs message,
  cause and stack.
- `Object.defineProperty` on a class value boxes to its constructor, and
  a JavaScript class targeted by defineProperty gets a live static table.
- A Symbol.hasInstance defined on a function's own table answers
  untyped `instanceof`; an error constructor held as a value answers by
  error kind.
- A builtin error constructor stored in a slot of its class value type
  (`{ Parent: Error }`) is the class value, not a checked cast that fails.
- A function hoisted above a local class it references captures the
  class binding as a TDZ box the class statement initializes.
- Names a program stores on the global object read back from it instead
  of folding to undefined.
- A module namespace whose exports never change converts to an untyped
  object (`{ ...ns }`).
- An untyped call of a String-only method name (`x.trim()`) dispatches on
  the receiver's runtime kind, so objects defining that member work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:43:27 -07:00
Chris Tate d4a7b82dd8 Free arrays whose elements need no release without a teardown
When the last reference to an array dropped, the runtime ran the generic
teardown: scr_rc_destroy's depth bookkeeping, an indirect call to the array
destroy, and a loop over the element slots. Arrays of numbers, booleans and
compact ProgramLifetime references release nothing, and neither do arrays of
other ProgramLifetime instances, whose release does nothing.

- scr_arr_release frees an acyclic array without an ext directly when its
  elements are numbers, booleans or compact references, or its descriptor
  has SCR_ARR_OPS_RELEASE_NOTHING. Every other array keeps the full
  teardown.
- The backend sets SCR_ARR_OPS_RELEASE_NOTHING in the descriptor's flags
  word on 64-bit targets for ProgramLifetime element types that are not
  compact, plain or as the pointer arm of a nullable union, in programs
  without lifetime regions. Registration copies it into a table indexed by
  the descriptor's registry id before the id is published. Descriptors
  without the flag read 0 and keep the full teardown.

Corpus 7401 drops arrays of ProgramLifetime instances, numbers and booleans
through every array operation, with holes and far indices, next to arrays of
ordinary objects, and matches Node.
2026-10-22 19:22:55 -05:00
Chris Tate 3024e57eeb Revert "Enumerate each record in the key order its object literal wrote (#1802)" 2026-10-22 19:02:41 -05:00
Chris Tate 53c43dd140 Merge main 2026-10-22 18:44:10 -05:00
Malte UblandClaude Opus 5.5 481f9a5e31 Stringify live typed records and arrays without a key list
JSON.stringify of a value reached through `unknown` walks native records
and arrays through their live capsules. The protocol walk built a
key-string array for every record and formatted every array index with
snprintf into a fresh string, only to look the member up again by that
key. A JSON API response of a few records spent most of its
serialization time there.

Without a replacer, walk a live record by field index over a snapshot of
its present fields (its keys are the immortal field names) and a live
array by element index, forming an index string only for a toJSON call
that receives it. Members are still read as scr_json_member reads them,
so callbacks that mutate or materialize the container see the same
results. Index keys elsewhere use the integer formatter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:43:54 -07:00
Chris Tate 81891d55f7 Fix boxed record field hooks and moving key writes, and merge main 2026-10-22 18:19:14 -05:00
Chris Tate b955ea3817 Give arrays state bytes only once they hold a hole or undefined (#1827) 2026-10-22 18:09:47 -05:00
Chris Tate 6fe5612e48 Merge main 2026-10-22 18:05:53 -05:00
Chris Tate dc2b238df7 Revert "Dispatch checked-dynamic method calls on method ids (#1851)" 2026-10-22 18:02:51 -05:00
Chris Tate 78c9317a2c Construct builtins through stored constructors and fix function-local class names and dispatch (#1807) 2026-10-22 17:56:42 -05:00
Chris Tate db7bd1111f Find Buffer search candidates with memchr (#1830) 2026-10-22 17:56:21 -05:00
Chris Tate 5372c69459 Pass { signal } to stream operator callbacks and Duplex.from bodies 2026-10-22 17:50:29 -05:00
Chris Tate 98712c364a Merge main 2026-10-22 17:49:58 -05:00
Malte UblandClaude Opus 5.5 a4287e9122 Keep Linux poller timers in a heap behind one timerfd
The epoll poller gave every armed timer its own timerfd. A keep-alive
HTTP server re-arms and cancels the socket idle timer on every request,
which cost six system calls per request (timerfd_create,
timerfd_settime twice, epoll_ctl twice, close) on top of the read and
the send; Node makes two.

Armed keys now live in a deadline min-heap with a pointer hash, and one
timerfd wakes the loop at the earliest deadline. Arming a later
deadline, re-arming and cancelling make no system call; the timerfd is
reprogrammed only when an earlier deadline arrives or when it fires.
The fd table is indexed by fd number instead of scanned. Delivery is
unchanged: each expired timer reports once (one-shot), now in deadline
order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:48:23 -07:00
Chris Tate 5195e2b63a Merge main 2026-10-22 17:19:37 -05:00
Chris Tate b2bb7be0b4 Support the env option of spawnSync (#1825) 2026-10-22 17:00:17 -05:00
Chris Tate 57c10fc767 Build Sets, Maps and weak collections from checked-dynamic iterables (#1856) 2026-10-22 16:53:29 -05:00
Chris Tate b03cb004ad Send an http client request's head and body in one write (#1867) 2026-10-22 16:50:41 -05:00
Chris Tate 74a958408f Compile chalk 5 and hot-shots 8 from built-in shims with callable class instances (#1836) 2026-10-22 16:49:19 -05:00
Chris Tate 36db0613cc Bound the compiler's cyclic garbage to twice its live heap (#1863) 2026-10-22 16:46:32 -05:00
Chris Tate 7a5fdae1bd Send an http client request's head and body in one write
request.end(body) on a request whose head was not sent yet wrote the
head and then the body. On a socket that was already connected (a
reused keep-alive connection), each write is sent at once, so the two
went out as separate segments, and the server could handle the head and
answer before the body arrived. Node sends the head together with the
body. When the client and the server run in one program, the server's
'end' and 'close' for the request and the client's reading of the
response then depended on which socket the poller reported first: corpus
5409-http-held-request-body printed the client's line before the
request's 'end' and 'close' in a macOS dev build.

The head and a body known at end() now go out as one write, as Node
sends them (the server's response already did).
2026-10-22 16:45:59 -05:00
Chris Tate d7717ff1e9 Type empty array literals by their slots, and report package diagnostics accurately (#1799) 2026-10-22 16:40:40 -05:00
Chris Tate ee865bcb0d Support the legacy createCipheriv modes and crypto.checkPrime/generatePrime (#1832) 2026-10-22 16:32:12 -05:00
Chris Tate 48a08dce8e Store arrays of ProgramLifetime instances in 4-byte slots on Linux (#1847) 2026-10-22 16:29:42 -05:00
Chris Tate e74bd658fd Inline Map retains and Map.size in release builds (#1862) 2026-10-22 16:24:56 -05:00