Commit Graph
4679 Commits
Author SHA1 Message Date
Chris Tate bde0b64004 Test that move-state refuses a state file committed as a link outside the checkout
A branch can commit the file a state group writes (or that the config
creates from a template) as a symlink. The split tools resolve every path
they read or write through confine.mjs, so move-state refuses such a file
instead of appending the state through the link. The test covers that
path: the group file is listed under create, exists as a link to a file
outside the checkout, and the tool exits with an error and leaves the
outside file unchanged.
2026-10-22 05:36:03 -05:00
Chris Tate d7994f49a5 Merge main 2026-10-22 05:31:16 -05:00
Chris Tate 1917d1aa00 Test an array read's hole-free prefix before its capacity
The dense path of an element read that may be undefined (the
`%arr.idxOr` reads behind `a[i]` as `T | undefined`, the store
`a[x] = a[y]!`, and the element reads of the lowered array methods)
tested the index against the capacity, then against the hole-free prefix
(`filled`), and only above the prefix against the length and the slot's
state. `filled` is at most the length and the capacity (scr_array.h), so
an index below it needs no other test: the read now tests `filled` first
and takes the value path on that test alone. Above the prefix it tests
the capacity, the length and the state as before.

A double index first branches to the runtime lookup when it is not an
exact integer, then takes the same tests on its i64 value, which a 32-bit
target compares before truncating it.
2026-10-22 05:25:02 -05:00
Chris Tate 4923d21679 Merge main 2026-10-22 05:24:24 -05:00
Chris Tate 1360830149 Support new tls.TLSSocket and tls.connect({ socket }) 2026-10-22 04:52:44 -05:00
Chris Tate 92b57cf8be Merge main 2026-10-22 04:52:19 -05:00
Chris Tate 883f22ddd6 Run two-counter for loops on integer counters
`for (let lo = a, hi = b; lo < hi; lo++, hi--)` kept both counters as
doubles: the counted-loop match takes a single counter, so every
`a[lo]`/`a[hi]` converted and tested its index. A loop whose two counters
close in on each other by positive literal steps, are not written by the
body and index something now stores both as i64: on every body entry both
lie between the first start and the second. A start without a proven
integer range is tested at run time on a versioned loop, as a counted
loop's is.
2026-10-22 04:50:13 -05:00
Chris Tate feec4bf4df Call typed-array callbacks directly and visit on an integer counter
The helpers of the typed-array callback methods (forEach, map, filter,
some, every, find, findIndex, findLast, findLastIndex, reduce,
reduceRight) called their callback through the closure's code pointer on
every element, and one helper served every call with the same signature,
so the callback was never inlined. A literal callback now gets a helper
of its own, as for the array methods, and the constant-callback census
accepts the typed-array helpers, so the helper calls that function
directly.

The visit is now a loop on its own counter, `for (let i = 0; i < a.length;
i++)` or down from the last index, instead of a loop on a counter
declared before it, so the counter is an integer and element reads need no
index test. reduce and reduceRight without an initial value read their
seed at the first visited index and start one past it.

Adds reduce, some and map callbacks to the typed-array-kernels workload.
2026-10-22 04:44:34 -05:00
Chris Tate b03e965bf9 Resolve 7058's promises with undefined so both checkers agree 2026-10-22 04:35:27 -05:00
Chris Tate cab2db4f96 Drop the Cipheriv stream fence from the node-types fixture 2026-10-22 04:27:50 -05:00
Chris Tate c47a0e90ae Compile JSX with the classic runtime
Fixes #889
Co-authored-by: papack <33105506+papack@users.noreply.github.com>
2026-10-22 04:27:35 -05:00
Chris Tate 67247dd26b Support objectMode streams with any value as a chunk
Readable, Writable, Duplex, Transform and PassThrough take the
objectMode option, and a Duplex, Transform or PassThrough the
readableObjectMode and writableObjectMode options, as true or false
literals in an options object literal or in an options value. An
object-mode side holds any value that converts to a checked-dynamic
value: push, unshift, write, end, pipe, pipeline, for await, 'data'
listeners, Readable.from and the transform callback carry values in
TypeScript and JavaScript, and constructor-function streams and options
held in variables do the same. The object-mode high-water mark counts
values and defaults to 16. A readable buffer that mixes strings, Buffers
and other values holds them all, and push(undefined) and other values on
a byte-mode stream report ERR_INVALID_ARG_TYPE through 'error', as in
Node.

In typed code a write or transform callback, or a _write or _transform
method, may declare its chunk with any such type, which converts when
the callback runs. Writes pass the encoding argument to _write and
_transform as Node does: undefined when absent, null when a callback
took its place, otherwise the value unchanged. A parameter declared
string | null | undefined receives that value and one declared string
receives "" for undefined and null. write(chunk, encoding) on a
byte-mode stream encodes a string chunk with any known encoding and
throws ERR_UNKNOWN_ENCODING for an unknown one. Readable.from takes
typed Maps and Sets of any such value, and arrays that mix strings and
Buffers.

for await over a readable-only stream that will emit 'close' now ends
after 'close', as Node's eos does. Buffered writes that complete inside
clearBuffer run their callbacks in order, and 'finish' waits for a
deferred afterWrite that carries a callback, as Node's pendingcb does,
unless a _final callback schedules it.

The 'data' listener adapters take three slots and write and transform
callbacks read their chunk and encoding through runtime accessors, so
RUNTIME_ABI_VERSION is now 26.
2026-10-22 04:21:55 -05:00
Chris Tate 4919576d09 Filter regex start positions by their leading character sets 2026-10-22 04:16:06 -05:00
Chris Tate 6e25c5ddb7 Compile js-yaml and smol-toml statically and match Node (#1751) 2026-10-22 04:15:26 -05:00
Chris Tate d5489cabd7 Merge main 2026-10-22 04:10:59 -05:00
Chris Tate 6e753cd5c8 Merge main 2026-10-22 04:08:52 -05:00
Chris Tate b99094543f Revert "Read error constructors as values and destructure workerData in JavaScript"
This reverts #1776, which made main CI fail.
2026-10-22 04:04:16 -05:00
Chris Tate ebbc6c1831 Fix recursive JSON unions and primitive constructors as array callbacks 2026-10-22 03:56:15 -05:00
Chris Tate a9e23d2a09 Store assignment expressions into module lets declared without an initializer (#1780) 2026-10-22 03:45:00 -05:00
Chris Tate 144bfb2724 Store assignment expressions into module lets declared without an initializer
A module `let` declared without an initializer whose type uses its null
pointer as the TDZ sentinel (a function, for example) is initialized when
its declaration runs, while its slot is still empty. markUninitializedWrites
marked the assignment statements that run after the declaration as
initializing, but not assignments in expression position. Those ran the
pointer check and threw "ReferenceError: Cannot access 'g' before
initialization" where Node stores the value, so the deferred-resolver
pattern `let resolve!: ...; new Promise((r) => (resolve = r))` failed, as
did `(h) => (g = h)` and `const r = (g = h)`.

An assignment expression to such a binding now becomes an initializing
`assign` whose value a temp holds and yields. Writes that can run before
the declaration keep the flag check, so they still throw Node's
ReferenceError.
2026-10-22 03:41:43 -05:00
Chris Tate 931f6d48b5 Evaluate the extends expression of classes defined inside functions
A class defined inside a function whose base resolves to a known class
lowered to its class value alone, so the `extends` expression never ran.
Node evaluates it each time the definition runs, so its calls and other
effects were missing (`class Local extends select(2) {}` never called
select). The class value now follows the heritage expression's
evaluation when that expression can have effects: a call, construction,
assignment, update, delete, await or yield outside nested functions.
Module-scope classes already evaluated it.
2026-10-22 03:35:33 -05:00
Chris Tate b5fb4115e1 Merge main 2026-10-22 03:33:29 -05:00
Chris Tate 8e3c115cec Serialize the self-hosted emitter's IR compactly in its test (#1778) 2026-10-22 03:32:27 -05:00
Chris Tate e37b94cc94 Serialize the self-hosted emitter's IR compactly in its test
The pretty-printed IR of the self-hosted LLVM emitter is close to the
JavaScript string length limit on CI checkouts, so any growth of the
emitter fails the test with "Invalid string length".
2026-10-22 03:29:38 -05:00
Chris Tate f3efa0b38b Read error constructors as values and destructure workerData in JavaScript (#1776) 2026-10-22 03:29:21 -05:00
Chris Tate 3a879f4f05 Read error constructors as values and destructure workerData in JavaScript 2026-10-22 03:24:50 -05:00
Chris Tate 7d20212dd4 Merge main 2026-10-22 03:24:23 -05:00
Chris Tate 7019893664 Compute index temps of a typed-array loop counter as integers (#1775) 2026-10-22 03:16:13 -05:00
Chris Tate fe02190d1d Compile JavaScript-path calls that Node runs (#1756) 2026-10-22 03:15:48 -05:00
Chris Tate 921f054891 Extend a generic factory's local class returned through a constructor type (#1761) 2026-10-22 03:15:25 -05:00
Chris Tate ec0574f44b Read the byte offset of every untyped ArrayBuffer Buffer.from in one place
All Buffer.from paths over an untyped ArrayBuffer or SharedArrayBuffer now
go through one function that takes the byte offset explicitly, as Node's
fromArrayBuffer does. The start-offset RangeError prints the offset as
JavaScript does (-1.5, -1e+21, -Infinity) and builds its message without a
fixed-size buffer, which a large negative offset overran.
2026-10-22 03:13:24 -05:00
Chris Tate 42dd9390c1 Compute index temps of a typed-array loop counter as integers
In `for (let i = 0; i < out.length; i++) { const j = i * 4; ... px[j + 1] }`
the fast copy of the typed-array loop version computed `j` as a double and
converted every index back. The version gives each affine index operand
its range under the guard's bound on the counter, but kept the range an
operand already had, and the counter of a loop to a length is only known
to be below 2^53, so `i * 4` stayed a double multiply. The version now
narrows the existing range to the guard's.

A constant number local whose initializer has an exact integer form now
keeps that integer in an i64 slot beside the double, and its reads carry
both, so `px[j + 1]` indexes with integer arithmetic. Only optimized
synchronous functions keep these integers, and every other declaration of
the local drops its integer, since one declaration is emitted once per
loop copy.
2026-10-22 03:10:30 -05:00
Chris Tate b2b216c3df Extend a generic factory's anonymous local class returned through a constructor type
A generic factory declared to return a constructor type now also extends
when it returns an anonymous class expression (`return class { ... } as
Struct<S>`) or a `const` bound to one (`const Hold = class { ... }; return
Hold`). The result's class value is the class expression's own
constructor type; before, it was read through the class's name, so an
anonymous class refused with SC1090 and SC2008.
2026-10-22 03:10:12 -05:00
Chris Tate 32da078c11 Match Node's AsyncLocalStorage context in async prefixes and generator steps 2026-10-22 03:00:21 -05:00
Chris Tate 70697b45c3 Give generic arrow bindings Node's behavior before their declarations run
Generic arrow and function-expression bindings compile to shared
instances, and their declarations emit no store. Code that can run before
the declaration then diverged from Node.

- Inside a function, a nested function lowered before the declaration
  reads the binding through a TDZ box that nothing initialized. That
  covers closures above the declaration and hoisted function
  declarations. Such a read threw a ReferenceError even after the
  declaration ran. The declaration now stores the instance its declared
  signature pins into that box.
- At module scope, calls and pinned value reads went straight to the
  instances even when a hoisted function or an earlier closure ran them
  before the declaration. A binding with such a use now has a flag that
  its declaration sets.
  - Uses that can run first check the flag.
  - A let or const throws Node's ReferenceError before the arguments
    run.
  - A var runs the arguments and then throws "is not a function".
- Uses that follow the declaration stay direct calls: later statements,
  closures created after it, its own initializer, and function
  declarations that only such code calls.

A var binding inside a function that is called from a place that can run
before its declaration would need a per-call flag shared with its
closures. That shape now fails with SC1090 instead of calling the
instance.
2026-10-22 02:56:39 -05:00
Chris Tate f00428ccb1 Keep pipe() answering its destination and evaluate pipeline() arguments in source order 2026-10-22 02:53:03 -05:00
Chris Tate 1773ffbef3 WebCrypto: the crypto global, node:crypto's webcrypto, and secret-key SubtleCrypto 2026-10-22 02:52:01 -05:00
Chris Tateandsaboursepideh 1a240fbf10 Support asymmetric keys, signatures and Ed25519 in node:crypto and WebCrypto
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-22 02:49:33 -05:00
Chris Tate d1ff061833 Revert "Run two-counter for loops on integer counters"
This reverts #1768 (8fec4484d), which made main CI fail.
2026-10-22 02:48:13 -05:00
Chris Tate 054cb50ea4 Array keys and properties: non-index number keys, Object.keys, console.log 2026-10-22 02:31:20 -05:00
Chris Tate 48176a471b Merge main 2026-10-22 02:23:51 -05:00
Chris Tate 8fec4484de Run two-counter for loops on integer counters (#1768) 2026-10-22 02:16:04 -05:00
Chris Tate 16cf12c20d Run two-counter for loops on integer counters
`for (let lo = a, hi = b; lo < hi; lo++, hi--)` kept both counters as
doubles: the counted-loop match takes a single counter, so every
`a[lo]`/`a[hi]` converted and tested its index. A loop whose two counters
close in on each other by positive literal steps, are not written by the
body and index something now stores both as i64: on every body entry both
lie between the first start and the second. A start without a proven
integer range is tested at run time on a versioned loop, as a counted
loop's is.
2026-10-22 02:11:54 -05:00
Chris Tate 491480e1ad Convert clamped stores inline and test thread-owned views once in typed-array loops
A Uint8ClampedArray store called scr_bytes_to_u8_clamp for every element,
which kept loops of clamped stores scalar. The store now computes
ToUint8Clamp in line: NaN and values at most 0 store 0, values at least
255 store 255, and values between round through (v + 2^52) - 2^52, which
rounds a value in (0, 255) to the nearest integer, ties to even, as the
runtime function does.

In worker-enabled programs, every typed-array element access tests the
view's shared storage and branches between a relaxed atomic access and
the ordinary one, so loops over thread-owned views kept one branch per
access. A view's shared storage is fixed at construction, like its length,
so the typed-array loop version guard now tests it once for each invariant
receiver, and the fast copy reads and writes those views directly. In
worker programs a loop is versioned for this even when no bound needs a
guard. When any receiver is shared, the checked copy runs unchanged.
Programs without workers emit the same code as before.

Adds a Uint8ClampedArray brighten kernel to the typed-array-kernels
workload.
2026-10-22 02:09:43 -05:00
Chris Tate 2fbfc1b270 Keep each build's translation unit private until the build ends
The kept LLVM text and IR are named after the entry (`<stem>.ll`,
`<stem>.ir.json`) in the output directory, so two programs whose entries
share a name and are built into one directory share those files. The
executable cache published the translation unit by copying the shared
`.ll` after the native build, so when the other program wrote it in
between, the stamp recorded the other program's text, and a later cache
hit relinked the wrong program without any error.

A build now writes, compiles and caches private copies beside the shared
names and renames them into place when it ends. A cache hit that relinks
compiles a private copy of the cached translation unit, and leaves a
shared file that already holds those bytes as it is. The native build
reads the private copy and keeps the shared name as the spelling clang
sees, so native cache keys and debug names do not change.
2026-10-22 02:00:30 -05:00
Chris Tate d426960f45 Make Cipheriv and Decipheriv Transforms and accept function stages anywhere in pipeline()
Cipheriv and Decipheriv values are Transforms, as in Node: they can be
written and ended, piped into, used as pipeline() and stream/promises
pipeline stages, composed, read, iterated with for await, read by
stream/consumers, passed to finished(), and listened to, and their stream
properties read as in Node. Each chunk goes through the cipher's
update(), and the end pushes final() or reports its error through
'error'; writing after final() throws out of write(). The stream unit
builds a cipher's Transform on its first stream use from operations the
cipher's handle provides (ScrDynHandleOps.transform), so programs that
use ciphers without streams link as before. The cipher is a
collector-headered object behind a traced handle: its Transform's steps
hold it, and the pair is reclaimed as a cycle. Through untyped values, in
a program that uses streams, a cipher's stream properties and its on,
once, off, pipe, unpipe, read, pause, resume, setEncoding, write, end,
destroy, cork and uncork members run on its Transform; other stream
members throw a coded SC2020 error.

A Transform holds a write's callback while its readable side is at the
high-water mark and runs it at the next read, and runs it on a tick once
the readable side has ended, as Transform._write does. write() on a
PassThrough, a Transform, a Hash or a cipher used to return true however
much the readable side buffered. zlib streams applied the rule already
and keep their timing.

pipeline() accepts function stages anywhere among its streams, so runs of
async generator stages can sit on both sides of a stream. A stage can be
named by a function declaration, or a const bound to a function
expression, that nothing reassigns; its body lowers again at the
pipeline with its source typed by the pipeline. The source can also be
an array, a string, a synchronous generator object, or a synchronous
generator function without parameters: an async generator steps the
source's iterator and yields each element, and the next stage receives
each element on the same tick as with Node's for await over the source.
The runtime's function-stage entry points take a bitmask of the function
positions and one (next, read, return) helper triple for each generator
pumped into a stream, so RUNTIME_ABI_VERSION is now 25. In JavaScript,
the chunks a stage receives from the previous stage's generator read as
the strings or Buffers it yields, so their string and Buffer methods
compile.

Corpus 7055-7059 cover cipher streams in TypeScript and JavaScript, their
errors, close order and backpressure, the pipeline stages and sources
with their tick order, and Transform backpressure.
2026-10-22 01:55:38 -05:00
Chris Tate c081d5a3a7 Apply the byte offset in Buffer.from over untyped ArrayBuffers
Buffer.from(arrayBuffer, byteOffset) with an untyped ArrayBuffer or
SharedArrayBuffer now returns a Buffer from that offset to the end, as
Node's fromArrayBuffer does, where the offset was dropped and the Buffer
covered the whole buffer.
2026-10-22 01:16:55 -05:00
Chris Tate 0763610e93 Version loops to a typed array's length on every typed array they index
A loop such as `for (let i = 0; i < out.length; i++) out[i] = x[i] * y[i]`
keeps its integer induction, and the version guard now also receives the
loop's counted form, so it covers the accesses of `x` and `y` at the
counter. Before, only counted loops (`i < n`) passed a counter to the
guard, and loops to a length kept one bounds check per access of every
other typed array, which kept LLVM from vectorizing them.

Adds the typed-array-kernels runtime workload: element-wise loops over
Float64Array, Float32Array, Uint8Array and Int32Array, and RGBA to gray.
2026-10-22 01:11:37 -05:00
Chris Tateandblurname d68b73e3b4 Extend a generic factory's local class returned through a constructor type
A generic factory declared to return a constructor type (`Struct<S> =
(abstract new () => S) & { decode }`) whose every return gives back one of
its own local classes (`return Base as Struct<S>`) is now that local
class's factory: each instance has its own class, the result maps to the
class value while the instance lowers and at its calls, and `class X
extends struct(...)` extends it like a factory returning the class
directly. Before, the result type mapped to nothing (SC2008) and the
extends clause refused as a computed expression.

Static methods of a function-local class in a TypeScript file now lower as
the class value's runtime statics, as they already do in JavaScript files:
they read the enclosing function's bindings, call each other through
`this`, and inherit into a subclass. Before, they refused with SC1090.
Static fields and static blocks of such classes keep the fence.

Fixes #1372

Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>
2026-10-22 01:10:25 -05:00
Chris Tate 5a414300a9 Store references to ProgramLifetime instances in 4 bytes on Linux (#1748) 2026-10-22 01:02:43 -05:00