Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate 7eda6fa880 Trim the per-chunk work of compiled object-mode stream callbacks (#2523) 2026-10-29 15:09:24 -05:00
Chris Tate 59eced7a6c Lower ReadableStream tee, pipeTo and pipeThrough in TypeScript (#2513) 2026-10-29 15:09:05 -05:00
Chris Tate fe46ed63b8 Compile the functions a sparse profile never ran as cold code (#2526) 2026-10-29 15:08:45 -05:00
Chris Tate 771b9a96ba Throw Node's TypeError when a nullable destructuring source is null (#2537) 2026-10-29 15:08:26 -05:00
Chris Tate 1784445e31 Split the Linux LLVM artifacts job and take its glibc packages from native_linux (#2536) 2026-10-29 15:08:06 -05:00
Chris Tate 03e461b842 Match macOS objects and paths in the native install, loop poll and profile tests (#2541) 2026-10-29 15:07:46 -05:00
Chris Tate 4db725e622 Don't offset a null pointer when an array has no dense storage (#2559) 2026-10-29 15:07:27 -05:00
Chris Tate a9e2cb994f Deliver the lines a chunk split after a line listener closes the interface (#2533) 2026-10-29 15:07:08 -05:00
Chris Tate a391eef92a Read and write cached regular files on the loop thread in fs streams (#2540) 2026-10-29 15:06:47 -05:00
Chris Tate 08e2b4596a Run for-in over a receiver that may be null or undefined (#2534) 2026-10-29 15:06:00 -05:00
Chris Tate 7d283cbda3 Read at the exact bigint position in fs.readSync (#2535) 2026-10-29 15:05:39 -05:00
Chris Tate 5dc4bfd6d8 Read builtins into untyped TypeScript slots as Node's values (#2556) 2026-10-29 15:05:19 -05:00
Chris Tate d19fad3999 Match Node's emitWarning, kill, chdir and userInfo behavior (#2532) 2026-10-29 15:04:59 -05:00
Chris Tate 98b3277a43 Provide the libnode pack to the shape overlay npm lanes (#2542) 2026-10-29 15:04:40 -05:00
Chris Tate e6f6cd4b98 Release the web view of an abandoned fetch body while its transfer runs (#2570) 2026-10-29 15:04:15 -05:00
Chris Tate 42e57e4ef3 Deliver a line to a single line listener without the listener snapshot (#2531) 2026-10-29 15:03:53 -05:00
Chris Tate ce6d9d0f1a Skip the receiver bracket for adapters of receiver-free callbacks (#2564) 2026-10-29 15:03:34 -05:00
Chris Tate 0f6c3ae700 Count ArrayBuffer storage in process.memoryUsage() (#2569) 2026-10-29 15:03:14 -05:00
Chris Tate 5c58f3db34 Keep the runtime pack's verified artifacts in a record beside the pack (#2566) 2026-10-29 15:02:52 -05:00
Chris Tate 9632906b4d Take from a Readable's buffered queue by moving its head index
Taking the head entry of a Readable's buffer moved every remaining entry
down one slot, so draining a queue of n buffered chunks cost O(n²). The
buffer now keeps the index of its first entry: a take advances it, a push
at the tail moves the entries down only when at least half the slots are
free at the front (and grows otherwise), and unshift takes a freed front
slot when there is one. The drop, trace and boxing loops walk the entries
from that index.

The stream-buffered-drain runtime workload buffers 200,000 objects and
100,000 Buffers before draining them; corpus 8533 covers reads across
entries, unshift into a freed slot and onto a part-read head, setEncoding
after takes, and object-mode takes, unshifts and pushes.
2026-10-29 14:59:10 -05:00
Chris Tate f3ed70667e Count the small roots a partition takes toward its cap
A root under one 4 KiB unit went to the first open partition from its
name's whose load from the larger roots was below the cap. When only one
open partition was below the cap, every such root went there, and that
partition could hold most of the module, which then was optimized whole
and lost the per-partition cache. The small roots a partition takes now
count toward its cap for the small roots after them, and a small root
that finds no partition below the cap goes to its name's. The larger
roots' placement still ignores the small roots, so a helper that an edit
adds or removes moves no larger root.
2026-10-29 14:57:26 -05:00
Chris Tate b8e678b765 Evaluate a drain name expression, and discard a write callback's return value
events.once(process.stdout, name) with a name typed "drain" returned the
pending promise without evaluating the name, so a call or comma
expression producing "drain" never ran. A name that is not a string
literal now runs before the promise is created.

A write completion callback that returns a value (`() => count++`, a
stored function typed `(): number`) reported SC2020. The stream ignores
the return value, so such a callback is adapted to return nothing, as
before the error-argument form was added.

The stream-shims npm case compiles and matches Node now that write
callbacks take the error argument, so its SC2020 expectation is removed.
2026-10-29 14:48:45 -05:00
Chris Tate d67f378a50 Release the web view of an abandoned fetch body while its transfer runs
A Response body's stream gets a web ReadableStream view when a member the
fetch stream lacks is read (for example `body.tee`). The view's source holds
the stream, and the stream released the view only when that source was its
last holder. While the transfer runs it holds the stream too, so a dropped
body with a view kept a reference count of 2: the unobserved-response
discard never ran, the paused socket was never resumed, and a compressed
response dropped before HTTP EOF kept the process alive.

The stream now releases the view when only the view's source and the live
transfer hold it, and then takes the unobserved-response discard, as a
body without a view does.

The static-abandon-view fixture reads `body.tee` and `body.locked` of a
4 MiB incompressible gzip response from the new /gzip-incompressible route
and drops it. It hung until the test's timeout before this change and now
exits as Node does.
2026-10-29 13:44:32 -05:00
Chris Tate f9b024dbab Count ArrayBuffer storage in process.memoryUsage()
process.memoryUsage() reported external and arrayBuffers as 0. arrayBuffers
is now the storage of the ArrayBuffers, typed arrays, Buffers, DataViews
and SharedArrayBuffers the program holds, and external the same storage
without SharedArrayBuffers, as Node splits them. A view over existing
storage adds nothing. v8.getHeapStatistics().external_memory reads the
same figure as external.

Each ScrBytes owner records the bytes it counts in the struct's padding,
so ScrBytes stays 64 bytes and the offsets compiled code reads hold; a
static assertion pins them. Owners are counted in a per-instance counter,
because their reference counts are not atomic and they are released on
their instance's thread. Shared storage moves its owner's count to a
process-wide atomic counter, since any thread may release it.
2026-10-29 13:36:41 -05:00
Chris Tate 62943d7460 Search wrapper graphs once, brand the wrapper prototypes, read call members once
- The wrapper check before JSON, util.inspect, structuredClone and deep
  equality searched the value's graph recursively with only a depth
  limit, so an object with two references to itself took exponentially
  many calls, and a wrapper more than 64 levels deep was not found. The
  search now keeps a set of the objects it reached and an explicit stack:
  each object and array is searched once, at any depth.
- String.prototype, Number.prototype and Boolean.prototype are wrappers of
  "", 0 and false, as in Node, so their valueOf and toString answer the
  primitive instead of throwing the brand TypeError. String.prototype has
  its own `length` of 0.
- A string-named method call on a dynamic receiver in a program with
  wrappers read the member twice: once to check for the builtin
  String.prototype method and once for the call. The call now reads it
  once, and wrapper.isStringMethod checks that value.
- createsWrappers is listed with the lowerer's first-use caches that the
  unit journal leaves out.

The wrapper corpus program covers the prototypes' values, a getter read
once per call and console.log of a 40-level shared graph. The refusal
test covers a wrapper 100 levels deep, a wrapper under a shared graph and
util.inspect of a cyclic value.
2026-10-29 13:31:39 -05:00
Chris Tate 8ccaea7e5c Keep builtin methods of untyped receivers whose name a class declares as a getter
A call whose name some class declares only as a getter reads the member first on instances of those classes, and keeps the runtime's method dispatch for every other receiver, so n.toFixed(2) still formats a number when a class declares get toFixed(). This replaces the list of builtin method names.
2026-10-29 13:14:45 -05:00
Malte UblandClaude Opus 5.5 7dbb3b9c92 Register the identity check latch in the lowerer's journal
The unit journal requires every scalar of the lowerer to be journaled or
left out by name; `genericIdentityCheckDone` is set only by the
end-of-lowering audit, so a unit must leave it as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 09:41:27 -07:00
Malte UblandClaude Opus 5.5 8810d36c5c Keep the identity pin check inside the self-hosted subset
`(info.valueInstances ??= new Set()).add(...)` calls a Set method on the
assignment's result, which the compiler fences as a chained Set method call
(SC2020) when it compiles its own source: the zero-cost gate builds the
candidate compiler with the candidate, and it failed at
generic-functions.ts:1358. Record through a helper that spells the same
steps as statements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 09:41:27 -07:00
Malte UblandClaude Opus 5.5 8d8926dc62 Record ts7 preflight/order baselines for the identity corpus
Four new fixtures (two corpus programs, two diagnostics programs) get
their preflight/module-order answers recorded; no existing baseline
changes, so the frontend order behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 09:41:26 -07:00
Malte UblandClaude Opus 5.5 bb0e0a79d5 Compare generic declarations by identity across pinned signatures
An unpinned generic declaration compared with `===`/`!==` takes its
signature from the other operand (`held === ident`), which clears the pi
`agent-session.ts:590` shape.

That pin alone would answer `false` where JavaScript answers `true` when
the program also holds the declaration at another signature, so a
whole-program check refuses the comparison instead. It covers the direct
form and values compared through `unknown` (the g5 silent divergence:
two instances of one declaration compared unequal), resolving dynamic
operands' provenance only when some declaration is held at more than one
signature.

- generic-functions: `identityComparisonPin` and
  `checkGenericIdentityPins` (runs once at the end of lowering, when every
  value instance is known); `GenericFnInfo.valueInstances` records the
  instances value references produced.
- Corpus: `generic-identity-pin.ts`, `generic-identity-unknown.ts`.
- Diagnostics: `generic-identity-multiple-pins.ts`,
  `generic-identity-unknown.ts`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 09:41:25 -07:00
Chris Tate 42f8f30af6 Merge main 2026-10-29 11:19:38 -05:00
Chris Tate 7a9ad0140f Read omitted optional fields of partial assertions as undefined
A partial record assertion's view has no slot for any field the literal
omits. An omitted optional field now reads as undefined and answers false
to in, as in Node, instead of reaching a slot the view does not have. The
in test's join takes its predecessor from the block the field load ends
in. A write of an omitted field evaluates the receiver and the value
before the presence check, so the right side's side effects run before
the TypeError. The lowerer unit tests' program lists its source files,
which the partial-assertion scan reads.
2026-10-29 11:15:45 -05:00
Chris Tate 14babd6231 Call private static function fields and update private static fields through this
A call of a function-valued private static field through a receiver other
than the class's own name (this.#fn(a) in a static accessor) checks the
receiver and calls the stored function with this bound to the receiver,
instead of reading a public "#fn" property. this.#x++, --this.#x and
this.#x op= v check the receiver where the old value is read and update
the field's native storage, instead of a public "#x" property.
2026-10-29 11:13:10 -05:00
Chris Tate c12f2d64f3 Keep the runtime pack's verified artifacts in a record beside the pack
Each build stages the runtime artifacts it links from the runtime pack and
hashes each one with SHA-256 unless the build cache records that it was
verified with the file identity it still has. A build with an empty cache
therefore hashed every artifact again: on Linux x64, 40 objects and 4.3 MB
for a Benchmarks Game program, about 25 ms of a 300-1100 ms build.

Staging now also reads and writes runtime-pack.verified.json beside the
pack. It lists each verified artifact with its digest and file identity
(device, inode, size, mtime and ctime), as the cache's record does:

- An artifact is trusted only when one of the two records lists it with
  the manifest's digest and size and the identity its file has during the
  read. Any change to the file means hashing it again.
- A missing, unreadable or malformed record, or a malformed entry, is
  treated as absent.
- The record is written to a new file and renamed over the old one, so a
  reader sees a whole record. A write merges the record as it is then.
- When the pack directory cannot be written, the cache's record is the only
  one, as before.

The runtime pack packages do not publish the file. The native CLI build
removes the record its probe builds leave in the bundled pack, and
verify-native-cli.mjs rejects a distribution that has one.
2026-10-29 11:11:35 -05:00
Chris Tate 51c9e31533 Skip the receiver bracket for adapters of receiver-free callbacks
A signature adapter forwards the ambient receiver to its original, and
its closure carries the original's RECEIVER_FREE bit. Calls through the
closure's own pointer tested that bit, but the direct entry of a known
adapter target bracketed every call, because the adapter itself counts
as a receiver reader. The direct entry now tests the bit too.

The receiver readers analysis also admits more node kinds that run no
user code: optional chains, record presence and absent fields, dynamic
boxing, array for-of loops, typed array writes, typed array methods and
constructors over typed operands, the module binding check and region
intrinsics, performance.now, Atomics operations, string and number
library calls over primitives, and new Error(message, options) without
an options object.
2026-10-29 10:42:30 -05:00
Chris Tate 23e3577264 Compute an array's allocation start as an integer
The previous change avoided offsetting a null pointer with a branch in
scr_arr_alloc_start. The function is inlined into scr_arr_release and
scr_arr_destroy, so the branch changed their control flow, and the
functions that changed since the committed runtime profile was collected
then held 5.2% of its counts, over runtime-profile.test.ts's 5% limit.
The address is now computed as an integer, which is defined for a null
block and leaves the callers' control flow as it was.
2026-10-29 10:34:42 -05:00
Chris Tate 8ea6b21f56 Reject import() of specifiers nothing resolves with Node's ERR_MODULE_NOT_FOUND
A literal import() whose specifier resolves to no module lowered to a
deferred SC2012 refusal. It now rejects when the call runs, as Node does:
a package nothing installed provides rejects with "Cannot find package",
and a relative or absolute path to a file that does not exist rejects with
"Cannot find module" and the file's URL as the error's url property.
Builtins and URL specifiers keep their current lowering.

error.nodeThrow takes an optional fourth argument for the url, which the
runtime sets through scr_throw_node_coded_url.
2026-10-29 10:28:08 -05:00
Chris Tate 7aaf98b53c Check nullable destructuring sources through their unit arm
The TypeError check for a null or undefined source now reads the unit arm the value holds as a dyn null or undefined, instead of converting the whole union to dyn. A union whose data arm has no dyn form, such as a record holding a generator, compiles again instead of failing with SC9001.
2026-10-29 10:24:12 -05:00
Chris Tate 12e284aa2a Merge pull request #2560 from vercel-labs/ctate/give-intel-macos-d388b2
Give the Intel macOS release build 90 minutes
v0.4.6
2026-10-29 10:08:21 -05:00
Chris Tate 1941a3e353 Merge main 2026-10-29 09:27:38 -05:00
Chris Tate da6665a98a Keep partition placement stable when an edit adds a small helper
Roots under one 4 KiB unit, mostly runtime helpers, no longer count in the
partition loads that balancing compares. They are placed after the larger
roots, each in the first open partition from its name's partition whose
load from the larger roots is within the cap, so where a helper goes
depends only on the larger roots. A helper that an edit adds or removes
used to change the total and the cap and move several other roots, and
every partition they left or joined was compiled again.

A root's size also leaves out the digits of metadata references, which an
edit elsewhere in the module can renumber, so an unchanged function no
longer crosses a unit boundary because its references were renumbered.
2026-10-29 09:20:26 -05:00
Chris Tate 78400c316f Give a default export the name default in reuse's declaration tables
A default export without a name of its own (`export default function
(...)`, `export default class`, an `export default` expression) declared no
name, so a change to it left the units of its default imports applied. Each
default export now declares `default` beside its own names, and a change to
it reaches the names that import it.
2026-10-29 09:19:10 -05:00
Chris Tate 1ace0b4e88 Expect the map and record-width results the PR's lowering now gives 2026-10-29 09:14:55 -05:00
Chris Tate dbc448efe2 Merge main 2026-10-29 09:14:05 -05:00
Chris Tate 3ee8c41cbd Append long flatMap tuple arms in index order, and read omitted generic members
A flatMap callback's tuple arm appended its elements in the stored field
order, which sorts the names as strings, so a tuple of eleven or more
elements came out as 0, 1, 10, 2, …. The elements now append in index
order.

A generic receiver's member that the instantiation does not store reads
through the checked keyed read, but only for an access that is not a
write. The write test took any binary expression's left operand and any
unary operand for a write, so `options.key ?? fallback`,
`options.key === undefined` and `!options.key` still failed with SC1090.
Only an assignment's target, a delete, and `++`/`--` count as writes now.
2026-10-29 09:12:40 -05:00
Chris Tate 851d3362c2 Give the Intel macOS release build 90 minutes 2026-10-29 08:55:52 -05:00
Chris Tate 59d80c90a3 Merge main 2026-10-29 08:52:33 -05:00
Chris Tate eccb061a12 Find kept record sources through index signatures and record fields
The capture into an index-signature record consults a record's kept source
view only when some checked conversion in the program can keep one. That
scan accepted a closed record target and looked through union arms,
promises and arrays, but stopped at an index-signature record or a record
whose fields hold closed records. A conversion into
`Record<string, { x: string }>` keeps each value's undeclared keys, so a
program whose only such conversion had that shape dropped them in the
capture. The scan now looks through record fields and index-signature
values too, memoizing records by shape as it does unions.
2026-10-29 08:51:27 -05:00
Chris Tate ac978fc686 Don't offset a null pointer when an array has no dense storage
scr_arr_alloc_start subtracted the prefix from the dense block even when
the array had no storage, which computes NULL - 0. That is undefined in C,
and the macOS lane's UndefinedBehaviorSanitizer reports it
("applying zero offset to null pointer" at scr_array.c:60) in
scr_json.test.ts's weak-map program. Without a prefix, the function now
returns the block as it is.
2026-10-29 08:48:35 -05:00
Malte UblandClaude Opus 5.5 4d939e55c7 Sort directory entry names by their raw bytes before decoding them
libuv's uv_fs_scandir sorts the raw directory-entry bytes with strcmp and
decodes the names only after sorting. Sorting the decoded text instead
reorders names that are not valid UTF-8: the malformed bytes 0x80 and 0xfe
decode to U+FFFD, which sorts after "é", and distinct names that decode to
the same text fall into an arbitrary order. readdir and readdirSync listed
those names in an order Node never produces.

scr_fs_scandir now collects the raw names, sorts them with strcmp, and then
decodes each one into the ScrScandir.

The malformed UTF-8 harness test also prints the unsorted readdir order and
carries names whose byte order and decoded order differ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 06:45:11 -07:00