diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 23936e46a..35a328639 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -136,6 +136,8 @@ jobs:
run: pnpm generate:ts7-ast --check
- name: Verify the generated IR field readers
run: pnpm generate:ir-fields --check
+ - name: Verify the generated IR codec
+ run: pnpm generate:ir-codec --check
native_macos:
needs: [validation_scope, lanes]
@@ -464,6 +466,10 @@ jobs:
name: native-linux-x64-gnu-shape-overlay
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
+ # The npm suites' Node island cases load Node from a libnode pack, and
+ # fail rather than skip in this lane when none is configured.
+ - uses: ./.github/actions/libnode
+ if: matrix.suite == 'npm' || matrix.suite == 'npm-static'
- name: Run the suite with the overlay on
run: |
case "${{ matrix.suite }}" in
@@ -686,11 +692,17 @@ jobs:
run: pnpm test:checker-identity --optimization dev
llvm_artifacts_linux_x64:
- needs: [validation_scope, lanes]
+ needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (Linux x64 LLVM artifacts, no clang)
runs-on: ubuntu-24.04
- timeout-minutes: 30
+ # The glibc helper and runtime pack come from native_linux, as the macOS
+ # artifact jobs take theirs from native_macos. The musl packages and the
+ # WASI smoke run in llvm_artifacts_linux_x64_musl and
+ # llvm_artifacts_linux_x64_wasi: together with a rebuild of the glibc
+ # packages, which a runtime change forces, the one job took 25 to 32
+ # minutes.
+ timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
@@ -700,32 +712,13 @@ jobs:
with:
node-version-file: .node-version
cache: pnpm
- - uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- - uses: ./.github/actions/native-packages
+ - uses: actions/download-artifact@v4
with:
- target: linux-x64-gnu
- - name: Build and verify Linux x64 musl release packages
- run: |
- pnpm --filter @scriptc/llvm-linux-x64-musl build:native
- CC=zig AR=zig pnpm --filter @scriptc/runtime-linux-x64-musl build:native
- helper=packages/llvm-linux-x64-musl/bin/scriptc-llvm-codegen
- readelf -l "$helper" > "$RUNNER_TEMP/musl-helper-program-headers"
- if grep -q INTERP "$RUNNER_TEMP/musl-helper-program-headers"; then
- echo "musl helper must be a static executable" >&2
- exit 1
- fi
- docker run --rm --volume "$GITHUB_WORKSPACE:/work:ro" alpine:3.22 \
- /work/$helper version --format=json | node -e '
- const value = JSON.parse(require("node:fs").readFileSync(0, "utf8"));
- if (value.default_target !== "x86_64-unknown-linux-musl" || !value.supported_targets?.includes("wasm32-unknown-wasi")) process.exit(1);
- '
- node packages/runtime-linux-x64-musl/scripts/verify.mjs
- - uses: ./.github/actions/native-packages
- with:
- target: wasm32-wasi
- helper: "false"
+ name: native-linux-x64-gnu
+ path: ${{ runner.temp }}
+ - run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- run: pnpm build
- name: Linux helper object format and no-clang output contract
run: |
@@ -751,6 +744,82 @@ jobs:
SCRIPTC_NO_CACHE=1 SCRIPTC_LEGACY_C_PIPELINE=0 node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o /tmp/hello
test "$(/tmp/hello)" = "hello world"
'
+
+ llvm_artifacts_linux_x64_musl:
+ needs: [validation_scope, lanes, native_linux]
+ if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
+ name: test (Linux x64 LLVM artifacts, no clang, musl)
+ runs-on: ubuntu-24.04
+ # The static musl helper and runtime pack, restored from the
+ # native-packages cache when their inputs are unchanged: about 9 minutes
+ # when both are built. The runtime build emits its units through the
+ # host's glibc helper (runtime-unit.mjs), which comes from native_linux.
+ timeout-minutes: 25
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 11.1.3
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: .node-version
+ cache: pnpm
+ - uses: ./.github/actions/llvm-linux
+ - run: pnpm install --frozen-lockfile
+ - uses: ./.github/actions/zig
+ - uses: actions/download-artifact@v4
+ with:
+ name: native-linux-x64-gnu
+ path: ${{ runner.temp }}
+ - run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
+ - uses: ./.github/actions/native-packages
+ with:
+ target: linux-x64-musl
+ - name: Verify the Linux x64 musl release helper
+ run: |
+ helper=packages/llvm-linux-x64-musl/bin/scriptc-llvm-codegen
+ readelf -l "$helper" > "$RUNNER_TEMP/musl-helper-program-headers"
+ if grep -q INTERP "$RUNNER_TEMP/musl-helper-program-headers"; then
+ echo "musl helper must be a static executable" >&2
+ exit 1
+ fi
+ docker run --rm --volume "$GITHUB_WORKSPACE:/work:ro" alpine:3.22 \
+ /work/$helper version --format=json | node -e '
+ const value = JSON.parse(require("node:fs").readFileSync(0, "utf8"));
+ if (value.default_target !== "x86_64-unknown-linux-musl" || !value.supported_targets?.includes("wasm32-unknown-wasi")) process.exit(1);
+ '
+
+ llvm_artifacts_linux_x64_wasi:
+ needs: [validation_scope, lanes, native_linux]
+ if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
+ name: test (Linux x64 LLVM artifacts, no clang, wasm32)
+ runs-on: ubuntu-24.04
+ # The wasm32-wasi runtime pack (restored from the native-packages cache
+ # when unchanged; its units go through native_linux's glibc helper) and
+ # the WASI smoke tests, which take 6 to 9 minutes.
+ timeout-minutes: 25
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 11.1.3
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: .node-version
+ cache: pnpm
+ - uses: ./.github/actions/llvm-linux
+ - run: pnpm install --frozen-lockfile
+ - uses: ./.github/actions/zig
+ - uses: actions/download-artifact@v4
+ with:
+ name: native-linux-x64-gnu
+ path: ${{ runner.temp }}
+ - run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
+ - uses: ./.github/actions/native-packages
+ with:
+ target: wasm32-wasi
+ helper: "false"
+ - run: pnpm build
- name: WASI helper object/runtime-pack smoke
run: pnpm test packages/cli/test/wasi-runtime-pack.test.ts tests/harness/wasm-library.test.ts
@@ -1120,6 +1189,8 @@ jobs:
ts7_parity,
checker_identity,
llvm_artifacts_linux_x64,
+ llvm_artifacts_linux_x64_musl,
+ llvm_artifacts_linux_x64_wasi,
llvm_artifacts_macos_arm64,
llvm_artifacts_macos_arm64_contracts,
llvm_artifacts_macos_x64,
@@ -1154,6 +1225,8 @@ jobs:
lane ts7_parity "${{ needs.ts7_parity.result }}" "${{ needs.lanes.outputs.linux }}"
lane checker_identity "${{ needs.checker_identity.result }}" "${{ needs.lanes.outputs.linux }}"
lane llvm_artifacts_linux_x64 "${{ needs.llvm_artifacts_linux_x64.result }}" "${{ needs.lanes.outputs.linux }}"
+ lane llvm_artifacts_linux_x64_musl "${{ needs.llvm_artifacts_linux_x64_musl.result }}" "${{ needs.lanes.outputs.linux }}"
+ lane llvm_artifacts_linux_x64_wasi "${{ needs.llvm_artifacts_linux_x64_wasi.result }}" "${{ needs.lanes.outputs.linux }}"
lane llvm_artifacts_macos_arm64 "${{ needs.llvm_artifacts_macos_arm64.result }}" "${{ needs.lanes.outputs.macos }}"
lane llvm_artifacts_macos_arm64_contracts "${{ needs.llvm_artifacts_macos_arm64_contracts.result }}" "${{ needs.lanes.outputs.macos }}"
lane llvm_artifacts_macos_x64 "${{ needs.llvm_artifacts_macos_x64.result }}" "${{ needs.lanes.outputs.macos }}"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ae28f585f..bdbe0edde 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -231,8 +231,8 @@ jobs:
llvm_dist: x64
use_zig: true
runs-on: ${{ matrix.runner }}
- # The Intel macOS build took 24 minutes for 0.3.2 and passed 30 for 0.3.3.
- timeout-minutes: 45
+ # The Intel macOS build took 24 minutes for 0.3.2, 40 for 0.4.5, and passed 45 for 0.4.6.
+ timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
diff --git a/.gitignore b/.gitignore
index 04c889158..36b158611 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ output/
/packages/runtime-*/artifacts/
/packages/runtime-*/runtime-pack.json
/packages/runtime-*/.runtime-pack-*
+/packages/runtime-*/runtime-pack.verified.json*
# libnode packs: built by build:native, never committed
/packages/libnode-*/libnode.so.*
/packages/libnode-*/libnode.*.dylib
diff --git a/benchmarks/runtime/workloads/stream-buffered-drain.json b/benchmarks/runtime/workloads/stream-buffered-drain.json
new file mode 100644
index 000000000..e1746d52b
--- /dev/null
+++ b/benchmarks/runtime/workloads/stream-buffered-drain.json
@@ -0,0 +1,4 @@
+{
+ "entry": "workloads/stream-buffered-drain.ts",
+ "args": []
+}
diff --git a/benchmarks/runtime/workloads/stream-buffered-drain.ts b/benchmarks/runtime/workloads/stream-buffered-drain.ts
new file mode 100644
index 000000000..62f89833b
--- /dev/null
+++ b/benchmarks/runtime/workloads/stream-buffered-drain.ts
@@ -0,0 +1,36 @@
+// Readable streams that buffer many chunks before anyone reads them, then
+// drain: 200,000 objects pushed into an object-mode Readable and taken
+// with read() in a 'readable' handler, and 100,000 small Buffers pushed
+// into a byte-mode Readable and piped into a Writable.
+import { Readable, Writable } from "node:stream";
+
+const objects = new Readable({ objectMode: true, highWaterMark: 16, read() {} });
+for (let i = 0; i < 200000; i++) objects.push({ id: i, qty: i % 9 });
+objects.push(null);
+
+let taken = 0;
+let qty = 0;
+objects.on("readable", () => {
+ let row: { id: number; qty: number } | null;
+ while ((row = objects.read() as { id: number; qty: number } | null) !== null) {
+ taken++;
+ qty += row.qty;
+ }
+});
+objects.on("end", () => {
+ console.log(`objects taken=${taken} qty=${qty}`);
+ const bytes = new Readable({ read() {} });
+ for (let i = 0; i < 100000; i++) bytes.push(Buffer.from([i & 0xff, (i >> 8) & 0xff, 7]));
+ bytes.push(null);
+ let length = 0;
+ let sum = 0;
+ const sink = new Writable({
+ write(chunk: Buffer, _enc, cb) {
+ length += chunk.length;
+ for (const b of chunk) sum = (sum + b) % 65521;
+ cb();
+ },
+ });
+ sink.on("finish", () => console.log(`bytes length=${length} sum=${sum}`));
+ bytes.pipe(sink);
+});
diff --git a/docs/content/docs/cli.mdx b/docs/content/docs/cli.mdx
index f439edf52..4470bad77 100644
--- a/docs/content/docs/cli.mdx
+++ b/docs/content/docs/cli.mdx
@@ -309,14 +309,14 @@ A profile that already holds context-sensitive counts cannot start a second phas
### Block counts
-A profile matches a function only while the function's control flow hashes as it did when the profile was collected, so a change to the program or to scriptc leaves the changed functions without counts. A block-count file beside the profile, with its name and a `.blocks` extension in place of `.profdata` (`app.blocks` beside `app.profdata`), describes each function by the labels of its blocks instead. `--profile-use` reads it when it exists and annotates the functions whose control flow changed (the mismatched ones; a function the profile has no record of keeps no counts): blocks that keep their place in the function take their recorded counts, and the counts of the other blocks follow from the counts of the blocks and edges around them. The report then adds a line, and the warning counts only the functions that remain without counts:
+A profile matches a function only while the function's control flow hashes as it did when the profile was collected, so a change to the program or to scriptc leaves the changed functions without counts. A block-count file beside the profile, with its name and a `.blocks` extension in place of `.profdata` (`app.blocks` beside `app.profdata`), describes each function by the labels of its blocks instead. `--profile-use` reads it when it exists and annotates the functions whose control flow changed (the mismatched ones): blocks that keep their place in the function take their recorded counts, and the counts of the other blocks follow from the counts of the blocks and edges around them. The file also names the functions that never ran while the profile was collected, which a profile merged with `--sparse` has no record of. Those functions are compiled as cold code (an entry count of 0, optimized for size and placed with the other unlikely code), which shortens the build. A function the file does not name, for example one added since the profile was collected, keeps no counts and is optimized as without a profile. The report then adds a line, and the warning counts only the functions that remain without counts:
```
scriptc: profile app-cs.profdata: 0 functions annotated, 1 mismatched, 0 without a record
scriptc: profile app-cs.profdata: 1 function without profile counts annotated from app-cs.blocks
```
-A `--profile-use` build writes the block counts of the functions its profile annotates when the `SCRIPTC_RECORD_PROFILE_BLOCKS` environment variable names a path; such a build reuses no cached artifacts. For a context-sensitive profile, record them in the second phase, which the first-phase profile annotates:
+A `--profile-use` build writes the block counts of the functions its profile annotates, and the names of the functions it has no record of, when the `SCRIPTC_RECORD_PROFILE_BLOCKS` environment variable names a path; such a build reuses no cached artifacts. For a context-sensitive profile, record them in the second phase, which the first-phase profile annotates:
```console
$ SCRIPTC_RECORD_PROFILE_BLOCKS=app-cs.blocks scriptc build app.ts --optimization=speed --profile-use app.profdata --profile-generate profiles-cs/%p.profraw -o app-instrumented-cs
@@ -373,6 +373,8 @@ $ scriptc build app.ts --optimization=speed --cpu-dispatch -o app
The second copy makes the executable larger and the first build of the program slower, since LLVM compiles the program's code twice. Unchanged rebuilds are not affected. Both copies compute the same results: neither contracts floating-point operations into fused multiply-adds.
+A function's `x86-64-v3` copy runs its baseline code when its optimized code has vectors of at most 128 bits, none wider, and no rounding or bit counting in a loop. In such code, `x86-64-v3` only lets LLVM's vectorizers move scalar work onto vector instructions, which can run slower. Its calls still go to the `x86-64-v3` copies of the functions it calls.
+
The `SCRIPTC_CPU_LEVEL` environment variable caps the level the executable uses, for example to test the baseline code on a newer machine. It takes a level from `0` to `4`, `baseline`, or a level name such as `x86-64-v3`; any other value selects the baseline code.
```console
diff --git a/docs/content/docs/limitations.mdx b/docs/content/docs/limitations.mdx
index 88b2ca5fc..192c376be 100644
--- a/docs/content/docs/limitations.mdx
+++ b/docs/content/docs/limitations.mdx
@@ -100,6 +100,8 @@ title: "Limitations"
../limitations/builtin-apis/file-streams.mdx
+../limitations/builtin-apis/file-watching.mdx
+
../limitations/builtin-apis/crypto.mdx
../limitations/builtin-apis/assertions.mdx
@@ -172,6 +174,8 @@ title: "Limitations"
../limitations/runtime-differences-from-node-js.mdx
+../limitations/runtime-differences-from-node-js/event-order-across-native-code-and-islands.mdx
+
### Array bounds and runtime errors
../limitations/array-bounds-and-runtime-errors.mdx
diff --git a/docs/content/limitations/builtin-apis/assertions.mdx b/docs/content/limitations/builtin-apis/assertions.mdx
index 381fc3802..84c830e79 100644
--- a/docs/content/limitations/builtin-apis/assertions.mdx
+++ b/docs/content/limitations/builtin-apis/assertions.mdx
@@ -5,6 +5,8 @@
`assert.AssertionError` used as a value outside `instanceof` throws `SC2020`.
The comparison methods, `ok`, `fail` and `ifError` are function values that assert as the methods do; called through a stored value, `ok` without a message reports `value == true`, where Node quotes the call, and `fail` takes at most one argument.
`throws`, `rejects`, `doesNotThrow`, `doesNotReject`, `match` and `doesNotMatch` as values throw `SC2020`.
+ `strictEqual` and `notStrictEqual` compare a number, string or boolean that can be `null` or `undefined`, such as `child.signalCode`, with any number, string, boolean, `null` or `undefined`.
+ `notStrictEqual` of another value that can be `null` or `undefined`, such as `child.stdin`, against `null` or `undefined` compiles; `strictEqual` of one throws `SC2020`.
`assert.deepEqual` and `assert.notDeepEqual` follow Node's loose comparison, including Maps, Sets, Dates, regular expressions, typed arrays, and errors; operands whose type can be cyclic or holds functions inside a structure are refused.
`assert.partialDeepStrictEqual` follows Node's partial comparison over the same values, with the same refusals.
Checked-dynamic values compared deeply have no cycle detection.
diff --git a/docs/content/limitations/builtin-apis/date.mdx b/docs/content/limitations/builtin-apis/date.mdx
index d783984fa..c15efd94e 100644
--- a/docs/content/limitations/builtin-apis/date.mdx
+++ b/docs/content/limitations/builtin-apis/date.mdx
@@ -1,7 +1,7 @@
-- **Date:** Spread constructor arguments and locale formatters are unsupported.
+- **Date:** Spread constructor arguments, `toLocaleDateString()`, and locale or options arguments to the locale formatters are unsupported.
Dates are objects with identity: `===` and `==` compare identity, `new Date(date)` makes a new Date, setters change the one Date every reference holds, and Dates compile in arrays (`includes`, `indexOf`, `lastIndexOf`, `join`, and default `sort` by `toString()` text), in unions with other types, as Map and Set keys and values, and in records, tuples, class fields, closures, promises and generators.
Thrown and rejected Dates keep their identity; `structuredClone` and `postMessage` copy them.
- Zero-argument, single number/string, and local-time year/month field constructors, `getTime`, `valueOf`, `toISOString`, `toUTCString`, `toJSON` (a `string | null`: null for an invalid date, as in Node), `toString`, `toDateString`, `toTimeString`, the local and UTC calendar getters and setters, `setTime`, `getTimezoneOffset`, and single-string `Date.parse` compile.
+ Zero-argument, single number/string, and local-time year/month field constructors, `getTime`, `valueOf`, `toISOString`, `toUTCString`, `toJSON` (a `string | null`: null for an invalid date, as in Node), `toString`, `toDateString`, `toTimeString`, the argument-free `toLocaleString()` and `toLocaleTimeString()` (the default locale's ICU patterns over the host time zone, as Node prints them), the local and UTC calendar getters and setters, `setTime`, `getTimezoneOffset`, and single-string `Date.parse` compile.
Unary `+`, `-` and `Number(date)` read the time value; `String(date)`, template literals and `+` with a string read the `toString()` text; `date[Symbol.toPrimitive](hint)` compiles with a literal hint.
A Date converted to `unknown` is the same Date object.
See [Dates and timezones](#dates-and-timezones) for timezone data.
diff --git a/docs/content/limitations/builtin-apis/eventemitter.mdx b/docs/content/limitations/builtin-apis/eventemitter.mdx
index 0e085c56f..6bd1dab79 100644
--- a/docs/content/limitations/builtin-apis/eventemitter.mdx
+++ b/docs/content/limitations/builtin-apis/eventemitter.mdx
@@ -1,10 +1,11 @@
- **EventEmitter:** Literal event names use fixed argument tuples.
Computed string names require a constant prefix or suffix that excludes `error`, meta events, and stream-internal events; their arguments must match the exact arity and support checked conversion.
- Unrestricted names, symbols, names that may reach internal events, and typed `listeners()`/`rawListeners()` results for names with possible computed registrations are unsupported.
+ Unrestricted names, symbols, and names that may reach internal events are unsupported.
An emitter reached through an untyped value shares one listener list with its typed uses, so untyped registrations and emits reach typed listeners and typed emits reach untyped listeners.
An untyped `emit` of an event that has a listener with typed parameters converts its arguments to the event's typed tuple before any listener runs, and throws a `TypeError` when they do not convert.
Through an untyped value, `listeners()` and `rawListeners()` of an event with a listener that has typed parameters are unsupported, and `emit('error')` of an object that is not an Error with no `error` listener is unsupported.
- Untyped `newListener` and `removeListener` listeners receive `undefined` in place of a listener registered with typed parameters.
+ A `newListener` or `removeListener` listener registered through an untyped value receives `undefined` in place of a listener registered with typed parameters.
+ A typed `error` or `errorMonitor` listener throws a `TypeError` when an emitted payload that is not an Error does not convert to its parameter.
Reading EventEmitter methods as properties through an untyped value (`typeof emitter.listeners`) answers `undefined`.
`events.once` accepts EventEmitter and stream instances with a literal event name and an optional object-literal `{ signal }`; a stream's `data` event, other options values, and EventTarget emitters are unsupported.
Reading `defaultMaxListeners` through a CommonJS destructured or member binding (`const { defaultMaxListeners } = require("events")`) is unsupported.
diff --git a/docs/content/limitations/builtin-apis/file-watching.mdx b/docs/content/limitations/builtin-apis/file-watching.mdx
new file mode 100644
index 000000000..2d2b386f0
--- /dev/null
+++ b/docs/content/limitations/builtin-apis/file-watching.mdx
@@ -0,0 +1,5 @@
+- **File watching:** `fs.watch` returns an `FSWatcher` whose `on`, `once`, `off`, and `close` take `change`, `rename`, `error`, and `close` listeners.
+ A listener may take the event name and the entry name, but the name arrives as a string (Node's default encoding), so a listener annotated with the `Buffer` arm of `@types/node`'s `change` overload has no lowering.
+ Watching a directory on macOS with a listener that reads the entry name is refused with `SC2020` because FSEvents is not implemented yet; file watches and listeners that ignore the name work on every target.
+- **Stat polling:** `fs.watchFile` polls the resolved path every `interval` milliseconds (default 5007) and calls its listener with `(current, previous)` `Stats`; `fs.unwatchFile` removes the last listener registered with that callback, or every listener when called without one.
+ The call must be a statement of its own, because the `StatWatcher` it returns in Node.js is not lowered, and `bigint: true` is refused.
diff --git a/docs/content/limitations/builtin-apis/intl-segmenter.mdx b/docs/content/limitations/builtin-apis/intl-segmenter.mdx
index 2682acaf0..f762b6712 100644
--- a/docs/content/limitations/builtin-apis/intl-segmenter.mdx
+++ b/docs/content/limitations/builtin-apis/intl-segmenter.mdx
@@ -1,3 +1,4 @@
- **Intl.Segmenter:** Unicode grapheme segmentation, iteration, and `containing()` compile, with the default locale or one language tag of the form language[-script][-region] written as a string literal (grapheme clusters do not depend on the locale).
- In JavaScript dependencies, word and sentence segmenters construct, and their `segment()` throws an SC2020 error.
- Locale negotiation over several locales, word and sentence segmentation, `resolvedOptions()`, and detached methods are unsupported.
+ Word granularity compiles too, over ICU's UAX #29 word iterator at that locale; its segments carry `isWordLike` exactly as Node.js's do.
+ In JavaScript dependencies, sentence segmenters construct, and their `segment()` throws an SC2020 error; in TypeScript sources the construction is refused.
+ Locale negotiation over several locales, sentence segmentation, `resolvedOptions()`, and detached methods are unsupported.
diff --git a/docs/content/limitations/builtin-apis/process-memory.mdx b/docs/content/limitations/builtin-apis/process-memory.mdx
index 6a3121e2f..6865fd7ff 100644
--- a/docs/content/limitations/builtin-apis/process-memory.mdx
+++ b/docs/content/limitations/builtin-apis/process-memory.mdx
@@ -1,2 +1,5 @@
- **Process memory:** `process.memoryUsage()` reads `rss` from the operating system and `heapTotal` and `heapUsed` from the runtime's allocator, so the heap figures start near zero and grow as the program allocates.
- `external` and `arrayBuffers` are `0`, because Buffer and typed-array storage is part of the same heap.
+ `arrayBuffers` is the storage of the ArrayBuffers, typed arrays, Buffers, DataViews and SharedArrayBuffers the program holds, and `external` is the same storage without SharedArrayBuffers, as in Node.
+ Node also counts its own startup allocations in both, so a program that holds no such storage reports `0` where Node reports a few megabytes.
+ In a program with worker threads, `external` and the non-shared part of `arrayBuffers` count the calling thread's storage.
+ The storage comes from the same allocator, so it also counts in `heapTotal` and `heapUsed`.
diff --git a/docs/content/limitations/builtin-apis/streams-and-readline.mdx b/docs/content/limitations/builtin-apis/streams-and-readline.mdx
index 52bf534e8..01cd9eb9d 100644
--- a/docs/content/limitations/builtin-apis/streams-and-readline.mdx
+++ b/docs/content/limitations/builtin-apis/streams-and-readline.mdx
@@ -35,7 +35,9 @@
`stream.compose` accepts streams and async generator functions in any position, an async function as the last stage, and an array, a string, or a generator as the first stage; one stage is `Duplex.from` of it.
A web `ReadableStream` is read as an async iterable by `Readable.from` and as the first stage of `compose` or `pipeline`, and through its reader by `Readable.fromWeb` (with `objectMode`, `highWaterMark`, `encoding`, and `signal` in an options object literal) and `Duplex.from`.
In TypeScript, `new TransformStream()` without arguments builds the identity transform: its readable side is a web `ReadableStream`, its writable side supports `getWriter`, `abort`, `close`, and `locked`, and the writer supports `write`, `close`, `abort`, `releaseLock`, `ready`, `closed`, and `desiredSize`, with Node's backpressure (a write completes when the readable side reads its chunk).
- A web stream in a later compose stage and `Readable.toWeb` are unsupported, and in TypeScript, so are a `TransformStream` with a transformer or queuing strategies, the `WritableStream` constructor, `pipeTo`, and `pipeThrough`.
+ In TypeScript, `tee`, `pipeTo`, and `pipeThrough`, the `WritableStream` constructor with an underlying sink, and a `TransformStream` with a transformer or queuing strategies compile: a program that uses one of them builds its `ReadableStream`, `WritableStream`, and `TransformStream` values with the same classes as JavaScript, and a `Response` or `Request` body has those members too.
+ A TypeScript program that uses only `getReader`, `cancel`, `locked`, and the identity `TransformStream` keeps the smaller native streams.
+ A web stream in a later compose stage and `Readable.toWeb` are unsupported.
The `ERR_INVALID_ARG_VALUE` message for a stream that is no longer readable or writable names the stream's class instead of printing the stream.
`readable.compose(stage, { signal })` is `stream.compose(readable, stage)` with the signal added; the options are an object literal whose `signal` is a variable or a property read.
`pipeline` from `node:stream/promises` accepts a trailing `{ signal, end }` object, with or without function stages, and `finished` accepts `readable`, `writable`, `error`, `cleanup`, and `signal` options, with boolean literals for the flags.
@@ -60,7 +62,7 @@
The `readline/promises` `Readline` class writes to the same outputs.
`readline.emitKeypressEvents` emits `'keypress'` events on `process.stdin` or a readable stream, shared with terminal interfaces on that stream as in Node; a listener's first parameter must admit `undefined`, which it receives for escape sequences.
Chained listener registration is unsupported.
- `node:stream/web` exports the same classes as the globals; in TypeScript, only `ReadableStream` and the identity `TransformStream` have lowerings.
+ `node:stream/web` exports the same classes as the globals; in TypeScript, `ReadableStream`, `WritableStream`, and `TransformStream` have lowerings.
In JavaScript, `ReadableStream`, `WritableStream`, and `TransformStream` with their default readers, writers, and controllers, `CountQueuingStrategy`, `ByteLengthQueuingStrategy`, `TextEncoderStream`, and `TextDecoderStream`, as globals and as `node:stream/web` exports, are constructor values with Node's prototypes, brand checks, and errors.
Their underlying sources, sinks, and transformers, queuing strategies with a `size` function, readers and writers, `tee`, `pipeTo` and `pipeThrough` with their options and an abort signal, `ReadableStream.from`, and async iteration settle promises in Node's order, and the streams take own properties.
`TextDecoderStream` decodes UTF-8; its other encodings are unsupported.
diff --git a/docs/content/limitations/builtin-apis/v8.mdx b/docs/content/limitations/builtin-apis/v8.mdx
index 066fb000d..daafa3d6c 100644
--- a/docs/content/limitations/builtin-apis/v8.mdx
+++ b/docs/content/limitations/builtin-apis/v8.mdx
@@ -1,5 +1,5 @@
- **V8 heap statistics:** `node:v8` can be imported.
- `v8.getHeapStatistics()` returns Node's fields in Node's order, computed from the runtime allocator like `process.memoryUsage()`: `total_heap_size` and `total_physical_size` are the bytes the heap holds, `used_heap_size` the bytes allocated, `heap_size_limit` the machine's memory (the heap has no fixed ceiling), and `total_available_size` the limit minus the used bytes.
- Fields that describe V8 itself (`total_heap_size_executable`, `malloced_memory`, `peak_malloced_memory`, the global handle sizes, `external_memory`, and `does_zap_garbage`) are `0`, and `number_of_native_contexts` is `1`.
+ `v8.getHeapStatistics()` returns Node's fields in Node's order, computed from the runtime allocator like `process.memoryUsage()`: `total_heap_size` and `total_physical_size` are the bytes the heap holds, `used_heap_size` the bytes allocated, `external_memory` the same figure as `process.memoryUsage().external`, `heap_size_limit` the machine's memory (the heap has no fixed ceiling), and `total_available_size` the limit minus the used bytes.
+ Fields that describe V8 itself (`total_heap_size_executable`, `malloced_memory`, `peak_malloced_memory`, the global handle sizes, and `does_zap_garbage`) are `0`, and `number_of_native_contexts` is `1`.
The values are approximate and are not V8 heap figures.
The other `node:v8` members observe the V8 engine, such as heap snapshots, profiles, flags, and serialization, and throw `SC2020` where they are used.
diff --git a/docs/content/limitations/builtin-function-values.mdx b/docs/content/limitations/builtin-function-values.mdx
index 96c48375c..f6a14be60 100644
--- a/docs/content/limitations/builtin-function-values.mdx
+++ b/docs/content/limitations/builtin-function-values.mdx
@@ -1,6 +1,8 @@
Some builtins can be stored and passed as functions:
- Lowered `node:path` functions across the bare, POSIX, and win32 modules, including optional `basename` and variadic `join`/`resolve`.
+ Called through an untyped value, they validate their arguments with Node's `ERR_INVALID_ARG_TYPE`.
+ A path module held by untyped code is a plain object with Node's members in Node's order.
- Supported zero-argument `node:os` functions and `querystring.escape`/`unescape`.
- `util.isDeepStrictEqual`, `styleText`, `stripVTControlCharacters`, `toUSVString`, `inherits`, `deprecate`, `callbackify`, `debuglog`, and `debug`.
- `fs.existsSync`, `unlinkSync`, `chmodSync`, `chownSync`, `renameSync`, and `closeSync`, and `fs/promises.unlink`, `chmod`, and `rename`.
diff --git a/docs/content/limitations/console-and-process-streams.mdx b/docs/content/limitations/console-and-process-streams.mdx
index 946cb8061..8173d037e 100644
--- a/docs/content/limitations/console-and-process-streams.mdx
+++ b/docs/content/limitations/console-and-process-streams.mdx
@@ -12,9 +12,11 @@ Stored global console values and detached output methods retain identity and acc
Stored methods do not support format specifiers with substitution arguments.
Other stored console APIs, custom `Console` instances, and console mutation are unsupported.
-JavaScript can store `process.stdin`, `stdout`, and `stderr`, replace output `write` methods, and call saved methods with their stream receiver.
+JavaScript and TypeScript can store `process.stdin`, `stdout`, and `stderr`, replace output `write` methods, and call saved methods with their stream receiver.
+A replaced `write` method does not receive console's error-callback argument.
Input supports data/end/error listeners, pause/resume, buffered reads, and TTY raw mode.
Output writes are synchronous, with successful callbacks on the next-tick queue; queued backpressure is not modeled.
+Because no write is ever queued, `writableLength` is 0, `writableNeedDrain` is false, `writableHighWaterMark` is 65536, and `'drain'` never fires, so drain listeners register silently and `events.once(process.stdout, "drain")` stays pending, as it does in Node when no write returned false.
Stored streams do not support piping, encoding changes, arbitrary properties, or other events.
A TypeScript `NodeJS.WritableStream` parameter that only receives `process.stdout` or `process.stderr` supports `write(string)`.
When other streams flow into the same parameter, the process streams join them as `Writable` instances with the full writable surface.
diff --git a/docs/content/limitations/language-features/commonjs-require.mdx b/docs/content/limitations/language-features/commonjs-require.mdx
index f0bf67c4e..f13ca143f 100644
--- a/docs/content/limitations/language-features/commonjs-require.mdx
+++ b/docs/content/limitations/language-features/commonjs-require.mdx
@@ -7,3 +7,5 @@
Defaults, rest elements, and nested patterns over other modules are unsupported.
A require cycle compiles when each of its modules assigns all of its exports before its first `require` and uses the other modules of the cycle only inside functions that its top-level code can't call.
Other require cycles are rejected with `SC1016`.
+ A top-level `return` ends the module's evaluation.
+ It is unsupported when the module assigns exports after it, or when a function that exists before it runs reads a binding declared after it, such as a callback created above the `return` or a function declaration.
diff --git a/docs/content/limitations/runtime-differences-from-node-js/event-order-across-native-code-and-islands.mdx b/docs/content/limitations/runtime-differences-from-node-js/event-order-across-native-code-and-islands.mdx
new file mode 100644
index 000000000..49b748f65
--- /dev/null
+++ b/docs/content/limitations/runtime-differences-from-node-js/event-order-across-native-code-and-islands.mdx
@@ -0,0 +1,3 @@
+- **Event order across native code and islands:** Timers, worker output, and other macrotasks from the program and from JavaScript it runs in a Node island share one event loop under Node.js, so their order is fixed.
+ A compiled program runs both on the event loop it hosts for Node.js, but it does not merge them into one order, so callbacks from the two sides can run in a different order than under Node.js, and the order can vary between runs.
+ Only programs that depend on the ordering between native and island callbacks are affected.
diff --git a/docs/content/limitations/socket-data-encoding.mdx b/docs/content/limitations/socket-data-encoding.mdx
index 4384dd97f..955857607 100644
--- a/docs/content/limitations/socket-data-encoding.mdx
+++ b/docs/content/limitations/socket-data-encoding.mdx
@@ -1,3 +1,3 @@
-After `socket.setEncoding("utf8")`, an inline `'data'` listener receives strings, as in Node, whatever type its chunk parameter declares.
+After `socket.setEncoding()`, an inline `'data'` listener receives strings, as in Node, whatever type its chunk parameter declares.
A listener passed by reference, such as a named function whose parameter is typed `Buffer`, keeps its `Buffer` parameter, so a string chunk throws `TypeError: expected Uint8Array at $[0], got string`.
Declare that parameter as `string` or `string | Buffer`, or pass an inline function.
diff --git a/docs/src/generated/node-v24-compatibility.json b/docs/src/generated/node-v24-compatibility.json
index 387f5b21c..391d7a5f8 100644
--- a/docs/src/generated/node-v24-compatibility.json
+++ b/docs/src/generated/node-v24-compatibility.json
@@ -30842,9 +30842,9 @@
"inherited": true
},
"static": {
- "status": "not-implemented",
- "detail": "No static compiler lowering is registered for this API yet.",
- "verification": "registry-gap"
+ "status": "partial",
+ "detail": "Implemented by a dedicated static compiler/runtime path.",
+ "verification": "test-backed"
}
},
{
@@ -30911,9 +30911,9 @@
"inherited": true
},
"static": {
- "status": "not-implemented",
- "detail": "No static compiler lowering is registered for this API yet.",
- "verification": "registry-gap"
+ "status": "partial",
+ "detail": "Implemented by a dedicated static compiler/runtime path.",
+ "verification": "test-backed"
}
},
{
@@ -32659,9 +32659,9 @@
"inherited": true
},
"static": {
- "status": "not-implemented",
- "detail": "No static compiler lowering is registered for this API yet.",
- "verification": "registry-gap"
+ "status": "partial",
+ "detail": "Implemented by a dedicated static compiler/runtime path.",
+ "verification": "test-backed"
}
},
{
diff --git a/internal/compatibility/generated/node-v24-internal.json b/internal/compatibility/generated/node-v24-internal.json
index 00d6f8db3..8aa3a59b5 100644
--- a/internal/compatibility/generated/node-v24-internal.json
+++ b/internal/compatibility/generated/node-v24-internal.json
@@ -44292,6 +44292,7 @@
"evidence": "derived:descendants",
"tests": [
"tests/corpus/1403-buffer-fs.ts",
+ "tests/corpus/1564-fs-watch.ts",
"tests/corpus/2595-fs-arg-ladders.cjs",
"tests/corpus/2686-fs-file-handle.ts",
"tests/corpus/2844-node-disposable-resources.ts",
@@ -44327,7 +44328,10 @@
"tests/corpus/5570-fs-cp-async.ts",
"tests/corpus/6207-fs-constants-object.ts",
"tests/corpus/6208-fs-constants-require.cjs",
+ "tests/corpus/8427-fs-copyfile-writefile-flags.ts",
"tests/corpus/fs-native-realpath.ts",
+ "tests/corpus/fs-watch-file-names.ts",
+ "tests/corpus/fs-watch-file-polling.ts",
"tests/corpus/node24-fs-links-callbacks.cjs",
"tests/corpus/node24-fs-links-errors.cjs",
"tests/corpus/node24-fs-links-options.cjs",
@@ -45350,7 +45354,8 @@
"evidence": "surface-manifest:node-builtin.fs.promises.copyFile",
"tests": [
"tests/corpus/5542-fs-promises-parity.cjs",
- "tests/corpus/5543-fs-promises-typed.ts"
+ "tests/corpus/5543-fs-promises-typed.ts",
+ "tests/corpus/8427-fs-copyfile-writefile-flags.ts"
]
},
"anchorSource": "exact"
@@ -46091,7 +46096,8 @@
"tests/corpus/1403-buffer-fs.ts",
"tests/corpus/2595-fs-arg-ladders.cjs",
"tests/corpus/2919-fs-function-values.ts",
- "tests/corpus/2920-fs-promises-function-values.ts"
+ "tests/corpus/2920-fs-promises-function-values.ts",
+ "tests/corpus/8427-fs-copyfile-writefile-flags.ts"
]
},
"anchorSource": "exact"
@@ -47429,8 +47435,13 @@
"inherited": true
},
"static": {
- "status": "not-implemented",
- "evidence": "compiler-unmatched:fs"
+ "status": "partial",
+ "evidence": "compiler-dedicated:fs.unwatchFile",
+ "tests": [
+ "tests/corpus/1564-fs-watch.ts",
+ "tests/corpus/fs-watch-file-names.ts",
+ "tests/corpus/fs-watch-file-polling.ts"
+ ]
},
"anchorSource": "exact"
},
@@ -47601,8 +47612,13 @@
"inherited": true
},
"static": {
- "status": "not-implemented",
- "evidence": "compiler-unmatched:fs"
+ "status": "partial",
+ "evidence": "compiler-dedicated:fs.watchFile",
+ "tests": [
+ "tests/corpus/1564-fs-watch.ts",
+ "tests/corpus/fs-watch-file-names.ts",
+ "tests/corpus/fs-watch-file-polling.ts"
+ ]
},
"anchorSource": "exact"
},
@@ -47954,7 +47970,8 @@
"tests/corpus/1403-buffer-fs.ts",
"tests/corpus/2595-fs-arg-ladders.cjs",
"tests/corpus/2919-fs-function-values.ts",
- "tests/corpus/2920-fs-promises-function-values.ts"
+ "tests/corpus/2920-fs-promises-function-values.ts",
+ "tests/corpus/8427-fs-copyfile-writefile-flags.ts"
]
},
"anchorSource": "exact"
@@ -49006,7 +49023,8 @@
"tests/corpus/1403-buffer-fs.ts",
"tests/corpus/2595-fs-arg-ladders.cjs",
"tests/corpus/2919-fs-function-values.ts",
- "tests/corpus/2920-fs-promises-function-values.ts"
+ "tests/corpus/2920-fs-promises-function-values.ts",
+ "tests/corpus/8427-fs-copyfile-writefile-flags.ts"
]
},
"anchorSource": "exact"
@@ -49669,8 +49687,13 @@
"inherited": true
},
"static": {
- "status": "not-implemented",
- "evidence": "compiler-unmatched:fs"
+ "status": "partial",
+ "evidence": "compiler-dedicated:fs.FSWatcher",
+ "tests": [
+ "tests/corpus/1564-fs-watch.ts",
+ "tests/corpus/fs-watch-file-names.ts",
+ "tests/corpus/fs-watch-file-polling.ts"
+ ]
},
"anchorSource": "exact"
},
@@ -54770,6 +54793,12 @@
"tests/corpus/2986-http-static-client-socket-finish.ts",
"tests/corpus/2987-http-static-timeout-errors.ts",
"tests/corpus/2988-http-static-client-abort.ts",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/server/cases/http-trailers/main.ts",
"tests/fixtures/server/cases/http2-connect/main.ts",
"tests/fixtures/server/cases/upgrade-both/main.ts"
@@ -56318,7 +56347,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -56401,7 +56436,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -59430,7 +59471,13 @@
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/2672-http-request-response-callback.ts",
- "tests/corpus/2689-http-server-timeout-properties.ts"
+ "tests/corpus/2689-http-server-timeout-properties.ts",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs"
]
},
"anchorSource": "chapter"
@@ -61688,7 +61735,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -61996,7 +62049,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -63824,7 +63883,13 @@
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/2672-http-request-response-callback.ts",
- "tests/corpus/2689-http-server-timeout-properties.ts"
+ "tests/corpus/2689-http-server-timeout-properties.ts",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs"
]
},
"anchorSource": "chapter"
@@ -63920,7 +63985,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -64016,7 +64087,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72011,6 +72088,12 @@
"tests/corpus/2672-http-request-response-callback.ts",
"tests/corpus/2689-http-server-timeout-properties.ts",
"tests/corpus/5659-net-blocklist-socketaddress.ts",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/node-types/net-blocklist.ts"
]
},
@@ -72619,7 +72702,13 @@
"evidence": "compiler-dedicated:server.address",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72641,7 +72730,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72699,7 +72794,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72721,7 +72822,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72743,7 +72850,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72765,7 +72878,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72787,7 +72906,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73130,7 +73255,13 @@
"evidence": "compiler-dedicated:socket.destroy",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73170,7 +73301,13 @@
"evidence": "compiler-dedicated:socket.end",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73192,7 +73329,13 @@
"evidence": "compiler-dedicated:socket.pause",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73253,7 +73396,13 @@
"evidence": "compiler-dedicated:socket.resume",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73275,7 +73424,13 @@
"evidence": "compiler-dedicated:socket.setEncoding",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73369,7 +73524,13 @@
"evidence": "compiler-dedicated:socket.setTimeout",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73448,7 +73609,13 @@
"evidence": "compiler-dedicated:socket.write",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74092,7 +74259,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74114,7 +74287,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74136,7 +74315,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74158,7 +74343,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74180,7 +74371,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74202,7 +74399,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74224,7 +74427,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74246,7 +74455,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74268,7 +74483,13 @@
"evidence": "compiler-dedicated:net.createServer",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74326,7 +74547,13 @@
"evidence": "compiler-dedicated:net.getDefaultAutoSelectFamilyAttemptTimeout",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74348,7 +74575,13 @@
"evidence": "compiler-dedicated:net.setDefaultAutoSelectFamilyAttemptTimeout",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -75805,6 +76038,12 @@
"tests/corpus/5214-perf-entry-inspect.ts",
"tests/corpus/5215-perf-timing-validation.js",
"tests/corpus/6397-event-loop-metrics.ts",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/node-types/event-loop-metrics.ts"
]
},
@@ -76108,7 +76347,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -79679,6 +79924,7 @@
"tests/corpus/process-builtin-values.js",
"tests/corpus/process-hrtime.js",
"tests/corpus/process-named-signals.cjs",
+ "tests/corpus/process-stream-drain.ts",
"tests/corpus/process-uncaught-handler-throws.cjs",
"tests/corpus/process-uncaught-loop.cjs",
"tests/corpus/process-uncaught-module.mjs",
@@ -79686,9 +79932,11 @@
"tests/corpus/process-uncaught-rejection.cjs",
"tests/corpus/process-uncaught-sync.cjs",
"tests/corpus/process-versions-storage.ts",
+ "tests/corpus/process-write-dynamic-chunks.ts",
"tests/corpus/stdio-values.mjs",
"tests/corpus/stdio-write-errors.mjs",
"tests/corpus/stdio-write-overrides.mjs",
+ "tests/corpus/stdout-write-patch.ts",
"tests/fixtures/node-types/env-files.ts",
"tests/fixtures/node-types/util-callables.ts",
"tests/harness/event-loop.test.ts",
@@ -82047,6 +82295,9 @@
"tests/corpus/stdio-values.mjs",
"tests/corpus/stdio-write-overrides.mjs",
"tests/corpus/stdio-write-errors.mjs",
+ "tests/corpus/stdout-write-patch.ts",
+ "tests/corpus/process-stream-drain.ts",
+ "tests/corpus/process-write-dynamic-chunks.ts",
"tests/harness/event-loop.test.ts"
]
},
@@ -82089,6 +82340,9 @@
"tests/corpus/stdio-values.mjs",
"tests/corpus/stdio-write-overrides.mjs",
"tests/corpus/stdio-write-errors.mjs",
+ "tests/corpus/stdout-write-patch.ts",
+ "tests/corpus/process-stream-drain.ts",
+ "tests/corpus/process-write-dynamic-chunks.ts",
"tests/harness/event-loop.test.ts"
]
},
@@ -82131,6 +82385,9 @@
"tests/corpus/stdio-values.mjs",
"tests/corpus/stdio-write-overrides.mjs",
"tests/corpus/stdio-write-errors.mjs",
+ "tests/corpus/stdout-write-patch.ts",
+ "tests/corpus/process-stream-drain.ts",
+ "tests/corpus/process-write-dynamic-chunks.ts",
"tests/harness/event-loop.test.ts"
]
},
@@ -86933,7 +87190,8 @@
"tests/corpus/5611-readable-from-class-iterables.ts",
"tests/corpus/5612-readable-from-iterables.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "chapter"
@@ -87059,7 +87317,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87093,7 +87352,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87127,7 +87387,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87184,7 +87445,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87218,7 +87480,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87252,7 +87515,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87648,7 +87912,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87705,7 +87970,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87739,7 +88005,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -87796,7 +88063,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "ancestor"
@@ -87830,7 +88098,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "ancestor"
@@ -87864,7 +88133,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "ancestor"
@@ -88260,7 +88530,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "ancestor"
@@ -88363,7 +88634,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -88423,7 +88695,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -88457,7 +88730,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -88491,7 +88765,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -88548,7 +88823,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89141,7 +89417,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89175,7 +89452,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89209,7 +89487,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89243,7 +89522,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89277,7 +89557,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89311,7 +89592,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89345,7 +89627,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89379,7 +89662,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -89413,7 +89697,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -90832,7 +91117,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "exact"
@@ -91096,7 +91382,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "ancestor"
@@ -91130,7 +91417,8 @@
"tests/corpus/5425-pipeline-generator-stage.ts",
"tests/corpus/5428-pipeline-generator-stages.cjs",
"tests/corpus/5617-pipeline-function-ends.ts",
- "tests/corpus/5619-pipeline-function-ends.cjs"
+ "tests/corpus/5619-pipeline-function-ends.cjs",
+ "tests/corpus/writable-error-callbacks.ts"
]
},
"anchorSource": "ancestor"
@@ -91296,6 +91584,7 @@
"tests": [
"tests/corpus/1662-string-decoder-encodings.ts",
"tests/corpus/5882-string-decoder-runtime-encoding.ts",
+ "tests/corpus/8428-string-decoder-dynamic-chunks.ts",
"tests/corpus/node24-string-decoder.ts"
]
},
@@ -91324,7 +91613,8 @@
"tests": [
"tests/corpus/1662-string-decoder-encodings.ts",
"tests/corpus/node24-string-decoder.ts",
- "tests/corpus/5882-string-decoder-runtime-encoding.ts"
+ "tests/corpus/5882-string-decoder-runtime-encoding.ts",
+ "tests/corpus/8428-string-decoder-dynamic-chunks.ts"
]
},
"anchorSource": "exact"
@@ -91352,7 +91642,8 @@
"tests": [
"tests/corpus/1662-string-decoder-encodings.ts",
"tests/corpus/node24-string-decoder.ts",
- "tests/corpus/5882-string-decoder-runtime-encoding.ts"
+ "tests/corpus/5882-string-decoder-runtime-encoding.ts",
+ "tests/corpus/8428-string-decoder-dynamic-chunks.ts"
]
},
"anchorSource": "exact"
@@ -91380,7 +91671,8 @@
"tests": [
"tests/corpus/1662-string-decoder-encodings.ts",
"tests/corpus/node24-string-decoder.ts",
- "tests/corpus/5882-string-decoder-runtime-encoding.ts"
+ "tests/corpus/5882-string-decoder-runtime-encoding.ts",
+ "tests/corpus/8428-string-decoder-dynamic-chunks.ts"
]
},
"anchorSource": "exact"
@@ -96006,6 +96298,12 @@
"tests/corpus/7440-tls-server-add-context.ts",
"tests/corpus/7441-tls-client-error.ts",
"tests/corpus/7442-tls-server-contexts.mjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/node-types/tls-client-certificates.ts",
"tests/fixtures/node-types/tls-peer-certificates.ts",
"tests/fixtures/node-types/tls-server-contexts.ts",
@@ -96410,7 +96708,13 @@
"evidence": "compiler-dedicated:server.address",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -96437,7 +96741,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -96487,7 +96797,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
- "tests/corpus/2596-net-arg-ladders.cjs"
+ "tests/corpus/2596-net-arg-ladders.cjs",
+ "tests/corpus/net-socket-set-encoding-names.ts",
+ "tests/corpus/net-socket-write-rejected.cjs",
+ "tests/corpus/net-socket-method-values.cjs",
+ "tests/corpus/net-paused-socket-exit.cjs",
+ "tests/corpus/net-server-socket-peer-close.cjs",
+ "tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -99682,6 +99998,7 @@
"tests/corpus/5229-url-legacy-errors.cjs",
"tests/corpus/5833-url-search-params-iterators.ts",
"tests/corpus/5834-url-search-params-iterators-dynamic.mjs",
+ "tests/corpus/8423-urlsearchparams-implicit-string.ts",
"tests/corpus/node24-file-url-buffer.ts",
"tests/corpus/node24-file-url-errors.js",
"tests/corpus/node24-file-url-options.ts",
@@ -100931,7 +101248,8 @@
"status": "partial",
"evidence": "surface-manifest:node-builtin.url.urlToHttpOptions",
"tests": [
- "tests/corpus/1794-searchparams-url-live.ts"
+ "tests/corpus/1794-searchparams-url-live.ts",
+ "tests/corpus/8423-urlsearchparams-implicit-string.ts"
]
},
"anchorSource": "exact"
diff --git a/internal/compatibility/static-support.json b/internal/compatibility/static-support.json
index 1242ad9a7..933ef8610 100644
--- a/internal/compatibility/static-support.json
+++ b/internal/compatibility/static-support.json
@@ -59,6 +59,8 @@
"node-builtin.v8.getHeapStatistics": ["tests/corpus/v8-heap-statistics.ts", "tests/corpus/v8-require-optional.cjs"],
"node-builtin.events": ["tests/corpus/1677-emitter-listeners.ts", "tests/corpus/2620-ee-override-once-order.ts"],
"node-builtin.fs": ["tests/corpus/1403-buffer-fs.ts", "tests/corpus/2595-fs-arg-ladders.cjs", "tests/corpus/2919-fs-function-values.ts", "tests/corpus/2920-fs-promises-function-values.ts"],
+ "node-builtin.fs.copyFileSync": ["tests/corpus/1403-buffer-fs.ts", "tests/corpus/2595-fs-arg-ladders.cjs", "tests/corpus/2919-fs-function-values.ts", "tests/corpus/2920-fs-promises-function-values.ts", "tests/corpus/8427-fs-copyfile-writefile-flags.ts"],
+ "node-builtin.fs.writeFileSync": ["tests/corpus/1403-buffer-fs.ts", "tests/corpus/2595-fs-arg-ladders.cjs", "tests/corpus/2919-fs-function-values.ts", "tests/corpus/2920-fs-promises-function-values.ts", "tests/corpus/8427-fs-copyfile-writefile-flags.ts"],
"node-builtin.fs.realpathSync.native": ["tests/corpus/fs-native-realpath.ts"],
"node-builtin.fs.fchmodSync": ["tests/corpus/2950-fs-descriptor-publication.ts"],
"node-builtin.fs.fstatSync": ["tests/corpus/2950-fs-descriptor-publication.ts"],
@@ -71,7 +73,8 @@
"node-builtin.fs.promises.utimes": ["tests/corpus/node24-fs-timestamps-promises.ts"],
"node-builtin.fs.promises.access": ["tests/corpus/5542-fs-promises-parity.cjs", "tests/corpus/5543-fs-promises-typed.ts"],
"node-builtin.fs.promises.appendFile": ["tests/corpus/5542-fs-promises-parity.cjs", "tests/corpus/5543-fs-promises-typed.ts"],
- "node-builtin.fs.promises.copyFile": ["tests/corpus/5542-fs-promises-parity.cjs", "tests/corpus/5543-fs-promises-typed.ts"],
+ "node-builtin.fs.promises.copyFile": ["tests/corpus/5542-fs-promises-parity.cjs", "tests/corpus/5543-fs-promises-typed.ts", "tests/corpus/8427-fs-copyfile-writefile-flags.ts"],
+ "node-builtin.fs.promises.writeFile": ["tests/corpus/1403-buffer-fs.ts", "tests/corpus/2595-fs-arg-ladders.cjs", "tests/corpus/2919-fs-function-values.ts", "tests/corpus/2920-fs-promises-function-values.ts", "tests/corpus/8427-fs-copyfile-writefile-flags.ts"],
"node-builtin.fs.promises.mkdtemp": ["tests/corpus/5542-fs-promises-parity.cjs", "tests/corpus/5543-fs-promises-typed.ts"],
"node-builtin.fs.promises.rmdir": ["tests/corpus/5542-fs-promises-parity.cjs", "tests/corpus/5543-fs-promises-typed.ts"],
"node-builtin.fs.promises.truncate": ["tests/corpus/5542-fs-promises-parity.cjs", "tests/corpus/5543-fs-promises-typed.ts"],
@@ -169,12 +172,12 @@
"node-builtin.querystring": ["tests/corpus/957-builtins-namespace.ts", "tests/corpus/2917-builtin-callable-widths.ts"],
"node-builtin.readline": ["tests/corpus/1475-readline-closed-stdin.ts"],
"node-builtin.stream": ["tests/corpus/1685-stream-readable-basics.ts", "tests/corpus/1746-stream-for-await.ts", "tests/corpus/1814-stream-pipeline.cjs", "tests/corpus/2629-stream-consumers.ts", "tests/corpus/2861-readable-iterator-close.ts"],
- "node-builtin.string_decoder": ["tests/corpus/1662-string-decoder-encodings.ts", "tests/corpus/node24-string-decoder.ts"],
+ "node-builtin.string_decoder": ["tests/corpus/1662-string-decoder-encodings.ts", "tests/corpus/node24-string-decoder.ts", "tests/corpus/8428-string-decoder-dynamic-chunks.ts"],
"node-builtin.test": ["tests/harness/node-test.test.ts"],
"node-builtin.timers": ["tests/corpus/1804-timers-module.ts", "tests/corpus/2093-timers-promises.ts"],
"node-builtin.tls": ["tests/corpus/2331-tls-server-options-typed.ts", "tests/corpus/2557-tls-ca-store.ts"],
"node-builtin.tty": ["tests/corpus/1448-stream-columns.ts", "tests/corpus/terminal-stream-properties.ts", "tests/harness/terminal-geometry.test.ts"],
- "node-builtin.url": ["tests/corpus/1794-searchparams-url-live.ts"],
+ "node-builtin.url": ["tests/corpus/1794-searchparams-url-live.ts", "tests/corpus/8423-urlsearchparams-implicit-string.ts"],
"node-builtin.url.pathToFileURL": ["tests/corpus/path-to-file-url-options.ts", "tests/corpus/node24-file-url-options.ts", "tests/corpus/node24-file-url-errors.js"],
"node-builtin.url.fileURLToPath": ["tests/corpus/file-url-percent-decoding.ts", "tests/corpus/node24-file-url-options.ts", "tests/corpus/node24-file-url-errors.js"],
"node-builtin.url.fileURLToPathBuffer": ["tests/corpus/node24-file-url-buffer.ts", "tests/corpus/node24-file-url-options.ts", "tests/corpus/node24-file-url-errors.js", "tests/fixtures/node-types/file-url-conversion.ts"],
@@ -314,7 +317,7 @@
{ "chapter": "process", "signatures": ["`process.hrtime([time])`", "`process.hrtime.bigint()`"], "status": "partial", "evidence": ["tests/corpus/process-hrtime.js"] },
{ "chapter": "errors", "signatures": ["`stackTraceLimit` Type: {number}"], "status": "partial", "evidence": ["tests/corpus/error-stack-limit.ts"] },
{ "chapter": "errors", "signatures": ["`stack` Type: {string}"], "status": "partial", "evidence": ["tests/corpus/error-stack-frames.ts", "tests/corpus/error-stack-limit.ts"] },
- { "chapter": "process", "signatures": ["`stdin` Type: {Stream}", "`stdout` Type: {Stream}", "`stderr` Type: {Stream}"], "status": "partial", "evidence": ["tests/corpus/stdio-values.mjs", "tests/corpus/stdio-write-overrides.mjs", "tests/corpus/stdio-write-errors.mjs", "tests/harness/event-loop.test.ts"] },
+ { "chapter": "process", "signatures": ["`stdin` Type: {Stream}", "`stdout` Type: {Stream}", "`stderr` Type: {Stream}"], "status": "partial", "evidence": ["tests/corpus/stdio-values.mjs", "tests/corpus/stdio-write-overrides.mjs", "tests/corpus/stdio-write-errors.mjs", "tests/corpus/stdout-write-patch.ts", "tests/corpus/process-stream-drain.ts", "tests/corpus/process-write-dynamic-chunks.ts", "tests/harness/event-loop.test.ts"] },
{ "chapter": "process", "signatures": ["Signal events"], "status": "partial", "evidence": ["tests/corpus/1443-signal-handlers.ts", "tests/corpus/process-named-signals.cjs"] },
{ "chapter": "process", "signatures": ["Event: `'uncaughtException'`", "Event: `'uncaughtExceptionMonitor'`"], "status": "partial", "evidence": ["tests/corpus/process-uncaught-sync.cjs", "tests/corpus/process-uncaught-loop.cjs", "tests/corpus/process-uncaught-module.mjs", "tests/corpus/process-uncaught-rejection.cjs", "tests/corpus/process-uncaught-monitor.cjs", "tests/corpus/process-uncaught-handler-throws.cjs"] },
{ "chapter": "process", "signatures": ["`process.getBuiltinModule(id)`"], "status": "partial", "evidence": ["tests/corpus/process-builtin-values.js", "packages/compiler/test/process-builtin-values.test.ts"] },
@@ -475,16 +478,17 @@
{ "symbols": ["EventEmitter", "emitter.on", "emitter.addListener", "emitter.once", "emitter.prependListener", "emitter.prependOnceListener", "emitter.off", "emitter.removeListener", "emitter.removeAllListeners", "emitter.emit", "emitter.listenerCount", "emitter.listeners", "emitter.rawListeners", "emitter.eventNames", "emitter.setMaxListeners", "emitter.getMaxListeners", "events.once"], "status": "partial", "evidence": ["tests/corpus/1677-emitter-listeners.ts", "tests/corpus/2620-ee-override-once-order.ts"] },
{ "symbols": ["events.getEventListeners", "events.getMaxListeners", "events.listenerCount", "events.setMaxListeners", "events.defaultMaxListeners"], "status": "partial", "evidence": ["tests/corpus/5885-events-max-listener-helpers.ts", "tests/corpus/5846-events-esm-default-max.ts", "tests/corpus/5847-events-cjs-default-max.cjs"] },
{ "symbols": ["AsyncLocalStorage", "asyncLocalStorage.run", "asyncLocalStorage.exit", "asyncLocalStorage.getStore", "asyncLocalStorage.enterWith", "asyncLocalStorage.disable", "asyncLocalStorage.name", "AsyncLocalStorage.bind", "AsyncLocalStorage.snapshot"], "status": "partial", "evidence": ["tests/corpus/2213-async-local-storage.cjs", "tests/corpus/5815-als-callback-context.ts", "tests/corpus/7110-als-completion-callbacks.ts", "tests/corpus/7111-als-bind-snapshot-options.ts", "tests/corpus/7112-als-bind-snapshot-dynamic.mjs", "tests/corpus/7119-als-net-event-context.mjs", "tests/corpus/7380-als-http-event-context.mjs", "tests/corpus/7383-als-stream-events.ts", "tests/corpus/7384-als-stream-events.mjs", "tests/corpus/7385-als-child-process-events.ts", "tests/corpus/7386-als-child-fork-events/main.mjs", "tests/corpus/7387-als-http2-events.mjs", "tests/corpus/7388-als-http2-compat-events.mjs", "tests/corpus/7389-als-http2-secure-events.mjs", "tests/corpus/7391-als-response-end-and-finished.mjs"] },
- { "symbols": ["StringDecoder", "stringDecoder.write", "stringDecoder.end"], "status": "partial", "evidence": ["tests/corpus/1662-string-decoder-encodings.ts", "tests/corpus/node24-string-decoder.ts", "tests/corpus/5882-string-decoder-runtime-encoding.ts"] },
+ { "symbols": ["StringDecoder", "stringDecoder.write", "stringDecoder.end"], "status": "partial", "evidence": ["tests/corpus/1662-string-decoder-encodings.ts", "tests/corpus/node24-string-decoder.ts", "tests/corpus/5882-string-decoder-runtime-encoding.ts", "tests/corpus/8428-string-decoder-dynamic-chunks.ts"] },
{ "symbols": ["setImmediate", "setInterval", "setTimeout", "clearImmediate", "clearInterval", "clearTimeout", "timersPromises.setTimeout", "timersPromises.setImmediate", "timersPromises.setInterval", "immediate.hasRef", "immediate.ref", "immediate.unref", "timeout.hasRef", "timeout.ref", "timeout.unref", "timeout.refresh"], "status": "partial", "evidence": ["tests/corpus/1804-timers-module.ts", "tests/corpus/1805-timer-callback-args.ts", "tests/corpus/2093-timers-promises.ts", "tests/corpus/2852-timers-promises-interval.ts", "tests/corpus/5830-timers-promises-signal.ts"] },
{ "symbols": ["timersPromises.scheduler.wait", "timersPromises.scheduler.yield"], "status": "partial", "evidence": ["tests/corpus/5839-timers-promises-scheduler.ts", "tests/corpus/5889-timers-scheduler-signal.ts"] },
- { "symbols": ["stream.finished", "stream.pipeline", "stream.getDefaultHighWaterMark", "readable.push", "readable.unshift", "readable.read", "readable.pause", "readable.resume", "readable.isPaused", "readable.setEncoding", "readable.pipe", "readable.unpipe", "readable.destroy", "writable.write", "writable.end", "writable.cork", "writable.uncork", "writable.setDefaultEncoding"], "status": "partial", "evidence": ["tests/corpus/1685-stream-readable-basics.ts", "tests/corpus/1688-stream-writable-basics.ts", "tests/corpus/1814-stream-pipeline.cjs", "tests/corpus/5302-stream-promises-options.ts", "tests/corpus/5303-stream-finished-callback-opts.cjs", "tests/corpus/5425-pipeline-generator-stage.ts", "tests/corpus/5428-pipeline-generator-stages.cjs", "tests/corpus/5617-pipeline-function-ends.ts", "tests/corpus/5619-pipeline-function-ends.cjs"] },
- { "symbols": ["net.createServer", "net.connect", "net.createConnection", "net.getDefaultAutoSelectFamilyAttemptTimeout", "net.setDefaultAutoSelectFamilyAttemptTimeout", "server.address", "server.close", "server.listen", "socket.destroy", "socket.end", "socket.pause", "socket.resume", "socket.setEncoding", "socket.setTimeout", "socket.write"], "status": "partial", "evidence": ["tests/corpus/2500-net-autosel-timeout.ts", "tests/corpus/2596-net-arg-ladders.cjs"] },
+ { "symbols": ["stream.finished", "stream.pipeline", "stream.getDefaultHighWaterMark", "readable.push", "readable.unshift", "readable.read", "readable.pause", "readable.resume", "readable.isPaused", "readable.setEncoding", "readable.pipe", "readable.unpipe", "readable.destroy", "writable.write", "writable.end", "writable.cork", "writable.uncork", "writable.setDefaultEncoding"], "status": "partial", "evidence": ["tests/corpus/1685-stream-readable-basics.ts", "tests/corpus/1688-stream-writable-basics.ts", "tests/corpus/1814-stream-pipeline.cjs", "tests/corpus/5302-stream-promises-options.ts", "tests/corpus/5303-stream-finished-callback-opts.cjs", "tests/corpus/5425-pipeline-generator-stage.ts", "tests/corpus/5428-pipeline-generator-stages.cjs", "tests/corpus/5617-pipeline-function-ends.ts", "tests/corpus/5619-pipeline-function-ends.cjs", "tests/corpus/writable-error-callbacks.ts"] },
+ { "symbols": ["net.createServer", "net.connect", "net.createConnection", "net.getDefaultAutoSelectFamilyAttemptTimeout", "net.setDefaultAutoSelectFamilyAttemptTimeout", "server.address", "server.close", "server.listen", "socket.destroy", "socket.end", "socket.pause", "socket.resume", "socket.setEncoding", "socket.setTimeout", "socket.write"], "status": "partial", "evidence": ["tests/corpus/2500-net-autosel-timeout.ts", "tests/corpus/2596-net-arg-ladders.cjs", "tests/corpus/net-socket-set-encoding-names.ts", "tests/corpus/net-socket-write-rejected.cjs", "tests/corpus/net-socket-method-values.cjs", "tests/corpus/net-paused-socket-exit.cjs", "tests/corpus/net-server-socket-peer-close.cjs", "tests/corpus/net-socket-encoding-pipe.cjs"] },
{ "symbols": ["dgram.createSocket", "socket.address", "socket.bind", "socket.close", "socket.connect", "socket.ref", "socket.remoteAddress", "socket.send", "socket.unref"], "status": "partial", "evidence": ["tests/corpus/2597-dgram-send-ladders.cjs"] },
{ "symbols": ["net.SocketAddress", "net.BlockList"], "status": "partial", "evidence": ["tests/corpus/5659-net-blocklist-socketaddress.ts", "tests/fixtures/node-types/net-blocklist.ts"] },
{ "symbols": ["SocketAddress.parse", "BlockList.isBlockList", "blockList.addAddress", "blockList.addAddresses", "blockList.addCIDR", "blockList.addCIDRs", "blockList.addRange", "blockList.addSubnet", "blockList.check", "blockList.clear", "blockList.fromJSON", "blockList.removeAddress", "blockList.removeCIDR", "blockList.removeRange", "blockList.removeSubnet", "blockList.toJSON"], "status": "supported", "evidence": ["tests/corpus/5659-net-blocklist-socketaddress.ts", "tests/fixtures/node-types/net-blocklist.ts"] },
{ "symbols": ["readline.createInterface", "rl.close"], "status": "partial", "evidence": ["tests/corpus/1475-readline-closed-stdin.ts", "tests/corpus/5878-readline-dynamic-streams.mjs"] },
{ "symbols": ["test", "it", "test.skip", "test.todo", "test.only", "describe", "suite", "before", "after", "beforeEach", "afterEach", "context.test", "context.skip", "context.todo", "context.assert"], "status": "partial", "evidence": ["tests/harness/node-test.test.ts"] },
- { "symbols": ["http.request", "http.get", "http.createServer", "https.request", "https.get", "https.createServer", "request.end", "request.destroy", "request.setTimeout", "request.write", "response.end", "response.getHeader", "response.hasHeader", "response.removeHeader", "response.setHeader", "response.write", "response.writeHead", "headersTimeout", "requestTimeout", "timeout", "keepAliveTimeout", "keepAliveTimeoutBuffer"], "status": "partial", "evidence": ["tests/corpus/1780-http-res-surface.ts", "tests/corpus/2672-http-request-response-callback.ts", "tests/corpus/2689-http-server-timeout-properties.ts"] }
+ { "symbols": ["http.request", "http.get", "http.createServer", "https.request", "https.get", "https.createServer", "request.end", "request.destroy", "request.setTimeout", "request.write", "response.end", "response.getHeader", "response.hasHeader", "response.removeHeader", "response.setHeader", "response.write", "response.writeHead", "headersTimeout", "requestTimeout", "timeout", "keepAliveTimeout", "keepAliveTimeoutBuffer"], "status": "partial", "evidence": ["tests/corpus/1780-http-res-surface.ts", "tests/corpus/2672-http-request-response-callback.ts", "tests/corpus/2689-http-server-timeout-properties.ts"] },
+ { "symbols": ["fs.FSWatcher", "fs.watchFile", "fs.unwatchFile"], "status": "partial", "evidence": ["tests/corpus/1564-fs-watch.ts", "tests/corpus/fs-watch-file-names.ts", "tests/corpus/fs-watch-file-polling.ts"] }
]
}
diff --git a/native/llvm-codegen/CMakeLists.txt b/native/llvm-codegen/CMakeLists.txt
index fc4c07210..14263ad79 100644
--- a/native/llvm-codegen/CMakeLists.txt
+++ b/native/llvm-codegen/CMakeLists.txt
@@ -84,6 +84,7 @@ endif()
add_executable(scriptc-llvm-codegen
src/cache.cpp
+ src/cpu-copies.cpp
src/diagnostics.cpp
src/emit.cpp
src/main.cpp
diff --git a/native/llvm-codegen/README.md b/native/llvm-codegen/README.md
index 7305db21d..f0afcfd26 100644
--- a/native/llvm-codegen/README.md
+++ b/native/llvm-codegen/README.md
@@ -37,6 +37,8 @@ Repeating `--output` divides the program into that many partitions, which are co
With `--split codegen`, several outputs divide only code generation. The helper optimizes the module whole, as it does for a single output, then divides its functions in module order into runs of about the same instruction count, one per output, and generates code for every output on its own thread. Linking the outputs in order lays the functions out as one output would. A local definition that another output uses becomes hidden so the objects can reference each other; the others stay local. Every output after the first is generated from a copy of the optimized module read back from bitcode with its use-list order, so each function's machine code matches a single output except for jumps to definitions that another output also uses. When one run would hold more than three quarters of the module's instructions, the first output holds the whole module and the other outputs are empty objects. Placement depends only on the optimized module and the output count. `--split codegen` requires an optimized build and no `--cache-dir`; scriptc passes it for optimized executables with more than 128 KiB of LLVM text that stay one partition.
+In a `--cpu-dispatch` module, each function has a baseline copy and an x86-64-v3 copy marked `scriptc-cpu-copy`. Partitioning places each x86-64-v3 copy with its baseline copy. After optimization, an x86-64-v3 copy whose code has vectors of at most 128 bits, none wider, and no rounding or bit-count intrinsic in a loop takes its baseline copy's optimized code, and its calls to baseline copies call their x86-64-v3 copies (`src/cpu-copies.h`).
+
WebAssembly code generation differs from LLVM's default pipeline in two ways that do not change its output. The pass that gives multiple-entry loops a single entry runs only on functions whose control flow is irreducible; its reachability analysis grows with the square of a function's block count even when it has nothing to change. Object emission also leaves out the module pass that collects external symbols for assembly declarations, so each function's machine code is freed once it is printed instead of when the whole module is compiled.
`--cache-dir
` names a private directory the caller owns for one helper build. Simplified partitions are stored under the hash of their exact source and objects under the hash of the exact module after importing, each with a trailing digest and published by rename, so a rebuild recompiles only the partitions an edit can affect. Partitions drop the names of their struct types before the thin link, which would otherwise number same-named types from different partitions in an order that any edit can shift, changing the module after importing in partitions the edit did not touch.
diff --git a/native/llvm-codegen/src/cpu-copies.cpp b/native/llvm-codegen/src/cpu-copies.cpp
new file mode 100644
index 000000000..5bbf41ff8
--- /dev/null
+++ b/native/llvm-codegen/src/cpu-copies.cpp
@@ -0,0 +1,182 @@
+#include "cpu-copies.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/ValueMapper.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace llvm;
+
+namespace scriptc {
+namespace {
+
+// A function's name without the `.llvm.` suffix of the thin link.
+StringRef stem(StringRef Name) {
+ size_t At = Name.rfind(".llvm.");
+ if (At == StringRef::npos)
+ return Name;
+ StringRef Suffix = Name.drop_front(At + 6);
+ if (Suffix.empty() || !all_of(Suffix, isDigit))
+ return Name;
+ return Name.take_front(At);
+}
+
+// The size of a vector type in bits, or 0 for another type.
+uint64_t vectorBits(Type *T) {
+ auto *Vector = dyn_cast(T);
+ return Vector ? Vector->getPrimitiveSizeInBits().getFixedValue() : 0;
+}
+
+// Whether x86-64-v3 changes only the body's vector code of at most 128 bits:
+// the body has such vectors, and nothing that the level compiles to faster
+// code than the baseline, which is vectors of 256 bits (AVX2), and in a
+// loop, rounding (SSE4.1; the baseline calls the C library) and bit counts
+// (POPCNT, LZCNT, TZCNT). Scalar code keeps its x86-64-v3 code, whose VEX
+// encodings need fewer register copies.
+bool onlyNarrowVectors(Function &F) {
+ DominatorTree Dominators(F);
+ LoopInfo Loops(Dominators);
+ bool Narrow = false;
+ for (const BasicBlock &Block : F)
+ for (const Instruction &I : Block) {
+ uint64_t Bits = vectorBits(I.getType());
+ for (const Use &Operand : I.operands())
+ Bits = std::max(Bits, vectorBits(Operand->getType()));
+ if (Bits > 128)
+ return false;
+ Narrow |= Bits != 0;
+ const auto *Call = dyn_cast(&I);
+ if (!Call || !Loops.getLoopFor(&Block))
+ continue;
+ switch (Call->getIntrinsicID()) {
+ case Intrinsic::floor:
+ case Intrinsic::ceil:
+ case Intrinsic::trunc:
+ case Intrinsic::rint:
+ case Intrinsic::nearbyint:
+ case Intrinsic::round:
+ case Intrinsic::roundeven:
+ case Intrinsic::ctpop:
+ case Intrinsic::ctlz:
+ case Intrinsic::cttz:
+ return false;
+ default:
+ break;
+ }
+ }
+ return Narrow;
+}
+
+// Replaces the body of Copy with a clone of Base's. The clone's direct calls
+// to baseline copies call the matching x86-64-v3 copies in Tier.
+void useBaselineBody(Function &Copy, Function &Base,
+ const StringMap &Tier) {
+ GlobalValue::LinkageTypes Linkage = Copy.getLinkage();
+ GlobalValue::VisibilityTypes Visibility = Copy.getVisibility();
+ GlobalValue::UnnamedAddr UnnamedAddr = Copy.getUnnamedAddr();
+ Copy.deleteBody();
+ ValueToValueMapTy Map;
+ for (auto [From, To] : zip(Base.args(), Copy.args()))
+ Map[&From] = &To;
+ SmallVector Returns;
+ CloneFunctionInto(&Copy, &Base, Map,
+ CloneFunctionChangeType::LocalChangesOnly, Returns);
+ Copy.setLinkage(Linkage);
+ Copy.setVisibility(Visibility);
+ Copy.setUnnamedAddr(UnnamedAddr);
+ for (BasicBlock &Block : Copy)
+ for (Instruction &I : Block) {
+ auto *Call = dyn_cast(&I);
+ Function *Callee = Call ? Call->getCalledFunction() : nullptr;
+ if (!Callee)
+ continue;
+ StringRef Name = stem(Callee->getName());
+ if (!Name.consume_back(".base"))
+ continue;
+ auto It = Tier.find((Name + ".v3").str());
+ if (It != Tier.end() &&
+ It->second->getFunctionType() == Callee->getFunctionType())
+ Call->setCalledFunction(It->second);
+ }
+}
+
+// Whether the module counts executions for a profile (`--profile-generate`,
+// also after inlining in a context-sensitive second phase): its functions
+// increment counters named after themselves (`__profc_`).
+bool countsExecutions(const Module &M) {
+ for (const GlobalVariable &Global : M.globals())
+ if (Global.getName().starts_with("__profc_"))
+ return true;
+ for (const Function &F : M)
+ if (F.isIntrinsic() && F.getName().starts_with("llvm.instrprof."))
+ return true;
+ return false;
+}
+
+// Whether Base's body was optimized for fewer runs than Copy's: a profile
+// collected where the executable ran its x86-64-v3 copies gives their
+// baseline copies no runs, and the optimizer treats those as cold code.
+bool ranLess(const Function &Base, const Function &Copy) {
+ std::optional Runs = Copy.getEntryCount();
+ if (!Runs)
+ return false;
+ std::optional BaseRuns = Base.getEntryCount();
+ return !BaseRuns || BaseRuns->getCount() < Runs->getCount();
+}
+
+} // namespace
+
+void useBaselineBodies(Module &M) {
+ // Every function by its stem, so the copies of one function find each
+ // other whichever of them the thin link renamed.
+ StringMap Functions;
+ bool HasCopies = false;
+ for (Function &F : M) {
+ Functions[stem(F.getName())] = &F;
+ HasCopies |= F.hasFnAttribute(CpuCopyAttribute);
+ }
+ // A copy that took its baseline copy's body would count its executions
+ // against the baseline copy, and a build with the profile would then
+ // optimize the x86-64-v3 copy as if it never ran.
+ if (!HasCopies || countsExecutions(M))
+ return;
+ std::vector> Replaced;
+ for (Function &F : M) {
+ if (F.isDeclaration() || !F.hasFnAttribute(CpuCopyAttribute))
+ continue;
+ StringRef Name = stem(F.getName());
+ if (!Name.consume_back(".v3"))
+ continue;
+ auto It = Functions.find((Name + ".base").str());
+ if (It == Functions.end())
+ continue;
+ Function *Base = It->second;
+ if (Base->isDeclaration() || Base->hasAvailableExternallyLinkage() ||
+ Base->getFunctionType() != F.getFunctionType() ||
+ ranLess(*Base, F) || !onlyNarrowVectors(F))
+ continue;
+ Replaced.emplace_back(&F, Base);
+ }
+ for (auto [Copy, Base] : Replaced)
+ useBaselineBody(*Copy, *Base, Functions);
+}
+
+} // namespace scriptc
diff --git a/native/llvm-codegen/src/cpu-copies.h b/native/llvm-codegen/src/cpu-copies.h
new file mode 100644
index 000000000..c9323c976
--- /dev/null
+++ b/native/llvm-codegen/src/cpu-copies.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "llvm/IR/Module.h"
+
+namespace scriptc {
+
+// The string attribute that marks the x86-64-v3 copies of a `--cpu-dispatch`
+// build. The compiler names a function's copies `.base` and
+// `.v3`; the thin link may rename either to `.llvm.`.
+inline constexpr const char *CpuCopyAttribute = "scriptc-cpu-copy";
+
+// Runs after optimization, before code generation. An x86-64-v3 copy whose
+// optimized body has vectors of at most 128 bits takes the optimized body of
+// its baseline copy instead, unless it has something else the baseline
+// compiles to slower code: a vector wider than 128 bits, or a rounding or
+// bit-count intrinsic in a loop. In 128-bit vector code the higher level
+// only adds instructions that the vectorizers prefer over scalar ones, and
+// these can run slower. Scalar code keeps its x86-64-v3 code. The copy keeps
+// its name, so its callers do not change, and the body's direct calls to
+// baseline copies call their x86-64-v3 copies, so that callees still run at
+// the level the executable chose.
+//
+// A copy is left as it is when its baseline copy is not defined in the
+// module, the two copies' types differ, or the profile gives the baseline
+// copy fewer runs (its body is then optimized as colder code). Every copy is
+// left as it is in a module that counts executions for a profile.
+void useBaselineBodies(llvm::Module &M);
+
+} // namespace scriptc
diff --git a/native/llvm-codegen/src/emit.cpp b/native/llvm-codegen/src/emit.cpp
index 17d69be72..f5136c2e5 100644
--- a/native/llvm-codegen/src/emit.cpp
+++ b/native/llvm-codegen/src/emit.cpp
@@ -1,6 +1,7 @@
#include "emit.h"
#include "cache.h"
+#include "cpu-copies.h"
#include "diagnostics.h"
#include "partition.h"
#include "profile-blocks.h"
@@ -252,15 +253,20 @@ struct UnnameStructTypes : PassInfoMixin {
// over partitions: functions that received its counts, functions whose
// control flow changed since it was collected (hash mismatch), and functions
// it has no record of (new since it was collected, or never run in training
-// when the profile was merged with --sparse). A partition reused from the cache without its counts is counted
-// in Unknown. Recovered counts the mismatched functions that --profile-blocks
-// annotated, so it never exceeds Mismatched.
+// when the profile was merged with --sparse). A partition reused from the cache
+// without its counts is counted in Unknown. Recovered counts the mismatched
+// functions that --profile-blocks annotated, so it never exceeds Mismatched.
+// The two weights sum the profile's counters for the annotated and for the
+// mismatched functions, so a changed hot function weighs more than a changed
+// cold one.
struct ProfileMatch {
std::atomic Annotated{0};
std::atomic Mismatched{0};
std::atomic Missing{0};
std::atomic Recovered{0};
std::atomic Unknown{0};
+ std::atomic AnnotatedCounts{0};
+ std::atomic MismatchedCounts{0};
// Set once the annotation before inlining has run. The context-sensitive
// annotation after inlining, which a single-module pipeline also runs,
// reports its mismatched and missing functions again; they are not
@@ -272,27 +278,32 @@ struct ProfileMatch {
Mismatched += Other.Mismatched;
Missing += Other.Missing;
Recovered += Other.Recovered;
+ AnnotatedCounts += Other.AnnotatedCounts;
+ MismatchedCounts += Other.MismatchedCounts;
}
std::string serialize() const {
- return formatv("{0} {1} {2} {3}", Annotated.load(), Mismatched.load(),
- Missing.load(), Recovered.load())
+ return formatv("{0} {1} {2} {3} {4} {5}", Annotated.load(),
+ Mismatched.load(), Missing.load(), Recovered.load(),
+ AnnotatedCounts.load(), MismatchedCounts.load())
.str();
}
bool deserialize(StringRef Text) {
- SmallVector Parts;
+ SmallVector Parts;
Text.split(Parts, ' ');
- uint64_t Values[4];
- if (Parts.size() != 4)
+ uint64_t Values[6];
+ if (Parts.size() != 6)
return false;
- for (size_t I = 0; I < 4; ++I)
+ for (size_t I = 0; I < 6; ++I)
if (Parts[I].getAsInteger(10, Values[I]))
return false;
Annotated += Values[0];
Mismatched += Values[1];
Missing += Values[2];
Recovered += Values[3];
+ AnnotatedCounts += Values[4];
+ MismatchedCounts += Values[5];
return true;
}
};
@@ -374,23 +385,34 @@ bool hasProfileCounts(const Function &F) {
// Runs right after the profile annotation, before inlining and dead code
// elimination change the functions it annotated: counts them and records
-// their block counts, then annotates the functions whose control flow
-// changed from FallbackCounts. A function the profile has no record of keeps
-// no counts, as without block counts. Returns the analyses that remain valid.
+// their block counts, then annotates from FallbackCounts the functions whose
+// control flow changed, and the functions the profile has no record of that
+// FallbackCounts records as never run. A sparse profile leaves out the
+// functions its training never ran; the block counts name them, so they get
+// an entry count of 0 and LLVM optimizes them as cold code, as a profile with
+// zero counts would make it. A function neither names keeps no counts, as
+// without block counts: it was added after the profile was collected.
+// Returns the analyses that remain valid.
PreservedAnalyses afterProfileAnnotation(Module &M, ProfileMatch &Match) {
std::vector Mismatched;
+ std::vector Unrecorded;
for (Function &F : M) {
if (F.isDeclaration())
continue;
if (!hasProfileCounts(F)) {
if (hasHashMismatchTag(F))
Mismatched.push_back(&F);
+ else
+ Unrecorded.push_back(&F);
continue;
}
++Match.Annotated;
if (Recorder)
Recorder->add(F);
}
+ if (Recorder)
+ for (Function *F : Unrecorded)
+ Recorder->addNeverRan(*F);
if (!FallbackCounts)
return PreservedAnalyses::all();
ProfileSummaryInfo Summary(M);
@@ -403,6 +425,13 @@ PreservedAnalyses afterProfileAnnotation(Module &M, ProfileMatch &Match) {
++Match.Recovered;
Preserved = PreservedAnalyses::none();
}
+ for (Function *F : Unrecorded) {
+ auto Record = FallbackCounts->find(F->getName());
+ if (Record == FallbackCounts->end() || Record->second.EntryCount != 0)
+ continue;
+ annotateFromBlocks(*F, Record->second, Summary);
+ Preserved = PreservedAnalyses::none();
+ }
return Preserved;
}
@@ -412,11 +441,77 @@ void reportProfileMatch(const ProfileMatch &Match) {
{"mismatched", Match.Mismatched.load()},
{"missing", Match.Missing.load()},
{"recovered", Match.Recovered.load()},
- {"unknown_partitions", Match.Unknown.load()}};
+ {"unknown_partitions", Match.Unknown.load()},
+ {"annotated_counts", Match.AnnotatedCounts.load()},
+ {"mismatched_counts", Match.MismatchedCounts.load()}};
json::Object Response{{"ok", true}, {"profile", std::move(Profile)}};
outs() << formatv("{0}\n", json::Value(std::move(Response)));
}
+// The sum of each function's counters in the indexed profile at `Path`, by
+// profile name, read once per process. An unreadable profile has no counts
+// (the annotation reports it).
+const StringMap &profileCounts(const std::string &Path) {
+ static std::once_flag Once;
+ static StringMap Counts;
+ std::call_once(Once, [&Path] {
+ Expected> Reader =
+ IndexedInstrProfReader::create(Path, *vfs::getRealFileSystem());
+ if (!Reader) {
+ consumeError(Reader.takeError());
+ return;
+ }
+ for (const NamedInstrProfRecord &Record : **Reader) {
+ uint64_t Sum = 0;
+ for (uint64_t Count : Record.Counts)
+ Sum += Count;
+ Counts[Record.Name] += Sum;
+ }
+ consumeError((*Reader)->getError());
+ });
+ return Counts;
+}
+
+// Weighs one module's profile match by the profile's counters: collects the
+// annotated and the mismatched functions while the module is optimized,
+// then adds their counts to the match.
+class MatchWeigher {
+public:
+ explicit MatchWeigher(const Module &M) {
+ for (const Function &F : M)
+ if (!F.isDeclaration())
+ ProfileNames[F.getName()] = getIRPGOFuncName(F);
+ }
+
+ void weigh(const std::string &ProfileUse, ProfileMatch &Match) const {
+ const StringMap &Counts = profileCounts(ProfileUse);
+ auto CountsOf = [&Counts](StringRef Name) -> uint64_t {
+ auto It = Counts.find(Name);
+ return It == Counts.end() ? 0 : It->second;
+ };
+ // A context-sensitive profile annotates the module twice; each function
+ // counts once.
+ StringSet<> Seen;
+ for (const std::string &Name : Annotated)
+ if (Seen.insert(Name).second)
+ Match.AnnotatedCounts += CountsOf(Name);
+ // The mismatch warnings name functions by symbol.
+ for (const std::string &Name : Mismatched) {
+ auto It = ProfileNames.find(Name);
+ StringRef ProfileName =
+ It == ProfileNames.end() ? StringRef(Name) : StringRef(It->second);
+ if (Seen.insert(ProfileName).second)
+ Match.MismatchedCounts += CountsOf(ProfileName);
+ }
+ }
+
+ std::vector Annotated;
+ std::vector Mismatched;
+
+private:
+ StringMap ProfileNames;
+};
+
// Reads the profile's header once, before any partition needs it: it must
// hold IR-level counts. Context-sensitive counts lay out the code by them;
// a profile that has them cannot take a second set.
@@ -515,13 +610,15 @@ struct Pipeline {
});
}
- // Adds to Names the profile names of the functions the annotation gave
- // counts to, as soon as that pass has run.
+ // Adds to Names the profile names of the functions the annotation before
+ // inlining gave counts to, as soon as that pass has run.
void collectAnnotated(std::vector &Names) {
Callbacks.registerAfterPassCallback(
- [&Names](StringRef PassID, Any IR, const PreservedAnalyses &) {
- if (PassID != "PGOInstrumentationUse")
+ [&Names, Done = false](StringRef PassID, Any IR,
+ const PreservedAnalyses &) mutable {
+ if (Done || PassID != "PGOInstrumentationUse")
return;
+ Done = true;
if (const auto *M = llvm::any_cast(&IR))
for (const Function &F : **M) {
std::optional Entry = F.getEntryCount();
@@ -714,13 +811,22 @@ emitModule(Module &M, TargetMachine &Machine, OptimizationLevel Level,
StringRef OutputPath, std::vector> &Temporaries,
ProfileMatch &Match) {
bool Annotating = Profile && Profile->Action == PGOOptions::IRUse;
+ std::string ProfileUse = Annotating ? Profile->ProfileFile : "";
+ MatchWeigher Weigher(M);
if (Annotating)
M.getContext().setDiagnosticHandler(
- std::make_unique(Match));
+ std::make_unique(Match, &Weigher.Mismatched));
Pipeline P(Machine, Level, std::move(Profile));
- if (Annotating)
+ if (Annotating) {
+ // Registered first, so it lists the functions the profile itself
+ // annotated, before block counts recover any.
+ P.collectAnnotated(Weigher.Annotated);
P.afterProfileAnnotation(Match);
+ }
P.PB.buildPerModuleDefaultPipeline(Level).run(M, P.MAM);
+ if (Annotating)
+ Weigher.weigh(ProfileUse, Match);
+ useBaselineBodies(M);
if (std::optional Failure =
verify(M, "post_optimization_verification_failed"))
return Failure;
@@ -911,14 +1017,16 @@ compileOptimizedPartitions(const EmitOptions &Options,
return Failures.set({"target_machine_failed", LookupError});
LLVMContext Context;
ProfileMatch PartitionMatch;
+ std::vector MismatchedNames;
if (Annotating)
- Context.setDiagnosticHandler(
- std::make_unique(PartitionMatch));
+ Context.setDiagnosticHandler(std::make_unique(
+ PartitionMatch, &MismatchedNames));
Expected> Loaded =
loadPartition(Options, Plan.Modules[I], I, Context, *Machine);
if (!Loaded)
return Failures.set({"partition_failed", toString(Loaded.takeError())});
Module &Mod = **Loaded;
+ MatchWeigher Weigher(Mod);
if (!Options.ImportBitcode.empty()) {
// See emit(): the attribute is inert without a sanitizer pass and
// would block inlining of the imported runtime bodies.
@@ -937,8 +1045,12 @@ compileOptimizedPartitions(const EmitOptions &Options,
// Profile instrumentation or annotation happens here, before the thin
// link; the post-link optimization reads the annotated weights.
Pipeline P(*Machine, Level, profileOptions(Options));
- if (Annotating)
+ if (Annotating) {
+ // Registered first, so it lists the functions the profile itself
+ // annotated, before block counts recover any.
+ P.collectAnnotated(Weigher.Annotated);
P.afterProfileAnnotation(PartitionMatch);
+ }
ModulePassManager MPM = P.PB.buildThinLTOPreLinkDefaultPipeline(Level);
MPM.addPass(UnnameStructTypes());
// The module hash is set at the thin link instead (see Names).
@@ -948,6 +1060,8 @@ compileOptimizedPartitions(const EmitOptions &Options,
MPM.run(Mod, P.MAM);
}
if (Annotating) {
+ Weigher.Mismatched = std::move(MismatchedNames);
+ Weigher.weigh(Options.ProfileUse, PartitionMatch);
Match.add(PartitionMatch);
// Stored before the summary, so a reused summary finds its counts.
Cache.store(Key, "match", PartitionMatch.serialize());
@@ -1046,6 +1160,13 @@ compileOptimizedPartitions(const EmitOptions &Options,
CodeGeneration.run(Mod);
return false;
};
+ else
+ // The backend passes the module it owns; only the hook's signature is
+ // const.
+ Config.PreCodeGenModuleHook = [](unsigned, const Module &Optimized) {
+ useBaselineBodies(const_cast(Optimized));
+ return true;
+ };
// The link refers to module identifiers for its whole lifetime.
std::vector Names(Count);
@@ -1445,15 +1566,23 @@ std::optional
emitSplitCodegen(const EmitOptions &Options, Module &M, TargetMachine &Machine,
OptimizationLevel Level, ProfileMatch &Match) {
bool Annotating = !Options.ProfileUse.empty();
+ MatchWeigher Weigher(M);
if (Annotating)
M.getContext().setDiagnosticHandler(
- std::make_unique(Match));
+ std::make_unique(Match, &Weigher.Mismatched));
{
Pipeline P(Machine, Level, profileOptions(Options));
- if (Annotating)
+ if (Annotating) {
+ // Registered first, so it lists the functions the profile itself
+ // annotated, before block counts recover any.
+ P.collectAnnotated(Weigher.Annotated);
P.afterProfileAnnotation(Match);
+ }
P.PB.buildPerModuleDefaultPipeline(Level).run(M, P.MAM);
}
+ if (Annotating)
+ Weigher.weigh(Options.ProfileUse, Match);
+ useBaselineBodies(M);
if (std::optional Failure =
verify(M, "post_optimization_verification_failed"))
return Failure;
diff --git a/native/llvm-codegen/src/partition.cpp b/native/llvm-codegen/src/partition.cpp
index d56839426..7af4681e7 100644
--- a/native/llvm-codegen/src/partition.cpp
+++ b/native/llvm-codegen/src/partition.cpp
@@ -176,71 +176,108 @@ void balanceRoots(std::vector &Symbols, ArrayRef Roots,
ArrayRef Live, unsigned Count) {
if (Count >= MinGroupedPartitions)
return;
+ // A root's size leaves out the digits of metadata references, which an
+ // edit elsewhere in the module can renumber.
+ auto TextBytes = [](StringRef Text) {
+ uint64_t Bytes = 0;
+ for (size_t I = 0; I < Text.size(); ++I) {
+ ++Bytes;
+ if (Text[I] == '!')
+ while (I + 1 < Text.size() && isDigit(Text[I + 1]))
+ ++I;
+ }
+ return Bytes;
+ };
DenseMap RootBytes;
- uint64_t Total = 0;
for (unsigned I = 0; I < Symbols.size(); ++I)
if (Symbols[I].Kind == SymbolKind::Function && Live[I])
- RootBytes[Roots[I]] += Symbols[I].Header.size() + Symbols[I].Body.size();
- // Each root counts in whole 4 KiB units, so that an edit that grows a root
- // by less than that leaves every comparison, and every other root's
- // partition, unchanged.
- constexpr uint64_t Unit = 4096;
- for (auto &Entry : RootBytes) {
- Entry.second = (Entry.second + Unit - 1) / Unit * Unit;
- Total += Entry.second;
- }
+ RootBytes[Roots[I]] +=
+ TextBytes(Symbols[I].Header) + TextBytes(Symbols[I].Body);
std::vector Order;
for (auto &Entry : RootBytes)
Order.push_back(Entry.first);
llvm::sort(Order, [&](unsigned A, unsigned B) {
return Symbols[A].Name < Symbols[B].Name;
});
+ DenseSet EntryRoots;
+ for (unsigned I = 0; I < Symbols.size(); ++I)
+ if (Symbols[I].Entry && Live[I])
+ EntryRoots.insert(Roots[I]);
+ // A root of at least one unit counts in whole units, so that an edit that
+ // grows it by less than that leaves every comparison, and every other
+ // root's partition, unchanged.
+ constexpr uint64_t Unit = 4096;
+ uint64_t Total = 0;
+ for (unsigned Root : Order)
+ if (RootBytes[Root] >= Unit) {
+ RootBytes[Root] = (RootBytes[Root] + Unit - 1) / Unit * Unit;
+ Total += RootBytes[Root];
+ }
uint64_t Cap = Total * 5 / 4 / Count;
std::vector Load(Count, 0);
// Partitions that take no further root.
std::vector Closed(Count, 0);
DenseMap Placed;
+ auto FirstOpen = [&](unsigned Home) {
+ for (unsigned K = 0; K < Count; ++K)
+ if (!Closed[(Home + K) % Count])
+ return (Home + K) % Count;
+ return Home;
+ };
// A root without room anywhere stays in the first open partition from its
// own.
auto Place = [&](unsigned Root) {
uint64_t Bytes = RootBytes[Root];
- unsigned Home = stableHash(Symbols[Root].Name) % Count, Chosen = Home;
- bool Found = false;
- for (unsigned K = 0; K < Count && !Found; ++K) {
+ unsigned Home = stableHash(Symbols[Root].Name) % Count;
+ unsigned Chosen = FirstOpen(Home);
+ for (unsigned K = 0; K < Count; ++K) {
unsigned P = (Home + K) % Count;
if (!Closed[P] && Load[P] + Bytes <= Cap) {
Chosen = P;
- Found = true;
- }
- }
- for (unsigned K = 0; K < Count && !Found; ++K) {
- unsigned P = (Home + K) % Count;
- if (!Closed[P]) {
- Chosen = P;
- Found = true;
+ break;
}
}
Load[Chosen] += Bytes;
Placed[Root] = Chosen;
};
- DenseSet EntryRoots;
- for (unsigned I = 0; I < Symbols.size(); ++I)
- if (Symbols[I].Entry && Live[I])
- EntryRoots.insert(Roots[I]);
- // Entry roots first, each closing its partition, then roots of more than
- // half the cap, in name order, so that small roots fill around them; the
- // others follow in name order.
+ // Entry roots first, each closing its partition; then roots of more than
+ // half the cap, in name order, so that the others fill around them; then
+ // the other roots of at least one unit, in name order.
for (unsigned Root : Order)
if (EntryRoots.contains(Root)) {
Place(Root);
Closed[Placed[Root]] = 1;
}
for (unsigned Root : Order)
- if (!EntryRoots.contains(Root) && RootBytes[Root] * 2 > Cap)
+ if (!EntryRoots.contains(Root) && RootBytes[Root] >= Unit &&
+ RootBytes[Root] * 2 > Cap)
Place(Root);
for (unsigned Root : Order)
- if (!EntryRoots.contains(Root) && RootBytes[Root] * 2 <= Cap)
+ if (!EntryRoots.contains(Root) && RootBytes[Root] >= Unit &&
+ RootBytes[Root] * 2 <= Cap)
Place(Root);
+ // Roots under one unit (runtime helpers, mostly) count in no load of the
+ // larger roots, so a helper that an edit adds or removes moves no larger
+ // root. Each goes to the first open partition from its name's whose load,
+ // the helpers placed there included, is below the cap, or else to its
+ // name's: the helpers fill the partitions with room, and once those are
+ // full they spread by name instead of gathering in one partition (which
+ // could then hold most of the module and have it optimized whole).
+ std::vector SmallLoad(Count, 0);
+ for (unsigned Root : Order)
+ if (!EntryRoots.contains(Root) && RootBytes[Root] < Unit) {
+ unsigned Home = stableHash(Symbols[Root].Name) % Count;
+ unsigned Chosen = FirstOpen(Home);
+ for (unsigned K = 0; K < Count; ++K) {
+ unsigned P = (Home + K) % Count;
+ if (!Closed[P] && Load[P] + SmallLoad[P] < Cap) {
+ Chosen = P;
+ break;
+ }
+ }
+ SmallLoad[Chosen] += RootBytes[Root];
+ Placed[Root] = Chosen;
+ }
for (unsigned I = 0; I < Symbols.size(); ++I)
if (Symbols[I].Kind == SymbolKind::Function && Live[I])
Symbols[I].Partition = Placed[Roots[I]];
@@ -787,8 +824,30 @@ std::optional partitionProgramText(StringRef Text,
Symbols[Root].Kind != SymbolKind::Function)
Root = I;
Roots[I] = Root;
+ }
+ // The x86-64-v3 copy of a --cpu-dispatch build's function (`.v3`),
+ // with the chain that ends at it, is placed with the chain of its baseline
+ // copy (`.base`), so that the helper can compare the two copies
+ // after optimization (cpu-copies.h).
+ DenseMap CopyRoots;
+ for (unsigned I = 0; I < Symbols.size(); ++I) {
+ StringRef Name = Symbols[I].Name;
+ if (Symbols[I].Kind != SymbolKind::Function || !Live[I] ||
+ Roots[I] != I || !Name.consume_back(".v3"))
+ continue;
+ auto Base = Index.find((Name + ".base").str());
+ if (Base != Index.end() && Live[Base->second] &&
+ Symbols[Base->second].Kind == SymbolKind::Function)
+ CopyRoots[I] = Roots[Base->second];
+ }
+ for (unsigned I = 0; I < Symbols.size(); ++I) {
+ if (Symbols[I].Kind == SymbolKind::Declaration || !Live[I])
+ continue;
+ auto Copy = CopyRoots.find(Roots[I]);
+ if (Copy != CopyRoots.end())
+ Roots[I] = Copy->second;
if (Symbols[I].Kind == SymbolKind::Function)
- Symbols[I].Partition = stableHash(Symbols[Root].Name) % Count;
+ Symbols[I].Partition = stableHash(Symbols[Roots[I]].Name) % Count;
}
placeGroups(Symbols, Roots, Count);
balanceRoots(Symbols, Roots, Live, Count);
diff --git a/native/llvm-codegen/src/profile-blocks.cpp b/native/llvm-codegen/src/profile-blocks.cpp
index 4c94577a3..6504f585b 100644
--- a/native/llvm-codegen/src/profile-blocks.cpp
+++ b/native/llvm-codegen/src/profile-blocks.cpp
@@ -424,6 +424,16 @@ void annotateFromBlocks(Function &F, const FunctionBlocks &Record,
markFrequency(F, EntryCount, MaxCount, Summary);
}
+void BlockRecorder::addNeverRan(const Function &F) {
+ std::string Text;
+ raw_string_ostream OS(Text);
+ OS << "f ";
+ writeEscaped(OS, F.getName());
+ OS << " 0 0\n";
+ std::lock_guard Guard(Lock);
+ Records.emplace(F.getName().str(), std::move(Text));
+}
+
void BlockRecorder::add(const Function &F) {
std::optional Entry = F.getEntryCount();
if (!Entry || Entry->isSynthetic())
diff --git a/native/llvm-codegen/src/profile-blocks.h b/native/llvm-codegen/src/profile-blocks.h
index 1db71b53e..9368bcbe2 100644
--- a/native/llvm-codegen/src/profile-blocks.h
+++ b/native/llvm-codegen/src/profile-blocks.h
@@ -71,6 +71,8 @@ class BlockRecorder {
public:
// Records F's block counts, which the profile annotation just gave it.
void add(const llvm::Function &F);
+ // Records F, which the profile has no record of, as never run.
+ void addNeverRan(const llvm::Function &F);
// Writes the file through a private sibling; returns an error message on
// failure.
std::optional write(llvm::StringRef Path);
diff --git a/package.json b/package.json
index d049a4ecb..5540010f2 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"test:ts7": "node scripts/test-ts7.mjs",
"test:checker-identity": "node scripts/checker-identity.mjs",
"generate:ts7-ast": "node scripts/generate-ts7-ast-schema.mjs",
+ "generate:ir-codec": "node scripts/generate-ir-codec.mjs",
"generate:ir-fields": "node scripts/generate-ir-fields.mjs",
"test:fetch-conformance": "vitest run tests/harness/fetch-conformance.test.ts",
"test:test262": "tsx tests/test262/run.ts",
diff --git a/packages/cli/test/native-install.test.ts b/packages/cli/test/native-install.test.ts
index 7a6d3ef16..369bc392b 100644
--- a/packages/cli/test/native-install.test.ts
+++ b/packages/cli/test/native-install.test.ts
@@ -4,6 +4,7 @@ import {
mkdtempSync,
mkdirSync,
readFileSync,
+ realpathSync,
renameSync,
rmSync,
statSync,
@@ -22,8 +23,10 @@ afterEach(() => {
});
function fixture() {
- const root = mkdtempSync(
- join(process.platform === "win32" ? tmpdir() : "/tmp", "scriptc-install-"),
+ // installNativeCli resolves the installation directory, and on macOS /tmp
+ // is a link to /private/tmp, so the fixture's paths start resolved.
+ const root = realpathSync(
+ mkdtempSync(join(process.platform === "win32" ? tmpdir() : "/tmp", "scriptc-install-")),
);
roots.push(root);
const directory = join(root, "installation");
diff --git a/packages/cli/test/native-link-info.test.ts b/packages/cli/test/native-link-info.test.ts
index b838b3155..162165f28 100644
--- a/packages/cli/test/native-link-info.test.ts
+++ b/packages/cli/test/native-link-info.test.ts
@@ -72,7 +72,7 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
minimum_os: "14.0",
},
program: { object, entry_symbol: "main" },
- runtime_abi: { version: 47, marker: "scr_runtime_abi_v47" },
+ runtime_abi: { version: 48, marker: "scr_runtime_abi_v48" },
runtime_pack: {
kind: "precompiled",
package: "@scriptc/runtime-darwin-arm64",
@@ -165,6 +165,6 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
() => null,
(failure: { stderr?: string }) => failure,
);
- expect(error?.stderr).toContain("scr_runtime_abi_v47");
+ expect(error?.stderr).toContain("scr_runtime_abi_v48");
});
});
diff --git a/packages/compiler/ambient/node-fallback/events.d.ts b/packages/compiler/ambient/node-fallback/events.d.ts
index dda1cd086..ca55ca0c8 100644
--- a/packages/compiler/ambient/node-fallback/events.d.ts
+++ b/packages/compiler/ambient/node-fallback/events.d.ts
@@ -5,7 +5,12 @@
* checks annotated listener parameters against it (unannotated non-empty
* parameter lists have no static types and fence at the registration). */
declare module "events" {
- class EventEmitter {
+ /* Generic like @types/node's class: the type argument is the listener
+ * MAP, a pure typing aid with no runtime layout (Node erases it too), so
+ * `class Sink extends EventEmitter