Commit Graph
2491 Commits
Author SHA1 Message Date
Chris Tate 13c5fc6748 pi wave 5: node process APIs, stdlib singletons, misc lowering (#2548) 2026-10-30 01:00:51 -05:00
Chris Tate 8e07b89461 Keep typed records unboxed in object-mode streams (#2601) 2026-10-30 00:41:54 -05:00
Chris Tate 8a5d2e14a2 Match Node's socket encodings, rejected writes and event loop liveness (#2599) 2026-10-30 00:09:13 -05:00
Chris Tate c36c5d99ce Support non-Error 'error' payloads and checked-dynamic listeners in listeners() and meta events (#2586) 2026-10-30 00:04:33 -05:00
Chris Tate e36a442468 Throw for primitive Reflect.has targets, record isRaw, and match StringDecoder chunks
Reflect.has over a target held as any, unknown or a union answered a
presence result for a primitive. It now checks the target at run time
after both arguments are evaluated and throws Node's TypeError
("Reflect.has called on non-object"). The stdlib-fence snapshot no
longer lists Reflect.has, which lowers.

process.stdin.isRaw read a flag that only the dynamic setRawMode path
set, so a direct process.stdin.setRawMode(true) on a TTY left it false.
The flag now lives with scr_process_stdin_set_raw_mode, which every
path calls, readline's raw mode included.

StringDecoder.end(chunk) with a dynamic chunk that is undefined threw
ERR_INVALID_ARG_TYPE. It now only flushes, as Node's end skips the
write for undefined. A dynamic ArrayBuffer or SharedArrayBuffer chunk
decoded its contents; write and end now throw Node's
ERR_INVALID_ARG_TYPE for it, as it is not an ArrayBufferView.
2026-10-29 23:59:44 -05:00
Chris Tate bcda45ab7f Merge main 2026-10-29 23:58:03 -05:00
Chris Tate 2353a04540 Keep typed records unboxed in object-mode streams 2026-10-29 23:46:36 -05:00
Chris Tate e6cea05db9 Ship ICU data without the categories no Intl form reads (#2598) 2026-10-29 23:29:51 -05:00
Chris Tate f67678b422 Pipe decoded socket strings and give lane jobs their own tmpdirs
After socket.setEncoding(enc), socket.pipe(destination) wrote the raw
bytes it read to the destination. It now writes the decoded strings, as
Node's pipe writes each string chunk: the destination receives their
UTF-8 bytes, and a partial character the decoder holds at EOF is written
before the destination ends.

tests/node-parallel/run.mjs runs each job's files with its own
TEST_THREAD_ID, as Node's test runner does, so test/common/tmpdir gives
concurrent files different directories instead of one shared .tmp.0.
2026-10-29 23:16:13 -05:00
Chris Tate 2cc51b1427 Reject import() of unresolvable specifiers with Node's ERR_MODULE_NOT_FOUND (#2563) 2026-10-29 22:35:34 -05:00
Chris Tate d80b34d92d Match Node's socket encodings, rejected writes and event loop liveness 2026-10-29 22:18:16 -05:00
Chris Tate 902e8b4396 Ship ICU data without the categories no Intl form reads
The runtime pack shipped ICU's full icudt78l.dat (33.1 MB), and every
Intl executable gets a copy beside it. scr_intl.c answers its Intl forms
with ICU's break iterators, number and date formatters, calendars and
locale IDs, which never read the charset converters or the collation,
language, region, time zone name, unit, spell-out and transliterator
trees: 21.5 MB of the file.

- The pack build writes icudt78l.dat without those items
  (ICU_DATA_DROPPED in icu-inputs.mjs). filterIcuData rewrites the data
  file's table of contents to the remaining items, and the result is
  checked against a pinned sha256 and size (11,653,984 B).
- tests/harness/icu-data-filter.test.ts runs every @requires-icu corpus
  program under 47 locales and 3 time zones with the pack's data, with
  the full data and in Node, and fails when the outputs differ, so an
  Intl form that reads a dropped category fails there.
- intl-word-segmenter-dictionaries.ts segments Chinese, Japanese, Thai,
  Lao, Khmer and Burmese text, which ICU breaks with the dictionaries in
  brkitr/.
2026-10-29 22:17:47 -05:00
Chris Tate 6a05a2588f Merge main 2026-10-29 21:25:26 -05:00
Chris Tate 2a1f7ac212 Give class methods and function values jsval entries for untyped calls (#2543) 2026-10-29 21:17:19 -05:00
Chris Tate 919151d339 Lower partial record assertions to record views (#2555) 2026-10-29 20:56:21 -05:00
Chris Tate d4c6aee135 Merge main 2026-10-29 20:04:10 -05:00
Chris Tate 794ede51db Read the callee before the arguments in calls through untyped receivers (#2510) 2026-10-29 19:53:04 -05:00
Chris Tate 48dd898db6 Support non-Error 'error' payloads and checked-dynamic listeners in listeners() and meta events 2026-10-29 19:16:06 -05:00
Chris Tate e62348d849 Copy record sources field by field in Object.assign onto a live record 2026-10-29 16:46:35 -05:00
Chris Tate 7b27410698 Intl over ICU 78.3: Segmenter (grapheme, word) and default-locale toLocaleString (linux-x64-gnu) (#2504) 2026-10-29 16:42:38 -05:00
Chris Tate 78dbd98b1a Merge main 2026-10-29 16:39:31 -05:00
Chris Tate fb9499c846 Keep timer deadlines on macOS and re-arm intervals as Node does
The event loop slept to its next timer with nanosleep. macOS applies the
timer slack of the thread's QoS class to nanosleep: a 5 ms sleep takes
about 38 ms at utility QoS and 180 ms at background QoS, where launchd
services such as CI jobs run. Timers then fired out of order against
Node, whose libuv loop waits with a kevent timeout that keeps its
deadline at every QoS. On macOS the loop now sleeps with a kevent timeout
on a kqueue with no events, shared by every thread's loop.

Intervals also re-arm from the time their callback started, as Node's
listOnTimeout does, instead of from the clock after the callback: a
callback that takes 30 ms of a 50 ms interval keeps the ticks 50 ms
apart, and one slower than the interval makes the next tick due on the
next turn. interval-slow-callback.ts checks both against Node.
2026-10-29 16:11:17 -05:00
Chris Tate 3cd2f97c7c Ignore SIGPIPE and SIGXFSZ in executables, as Node does (#2578) 2026-10-29 16:05:14 -05:00
Chris Tate 21b919cad7 Use SSE2 and string-instruction memory routines in x86_64 musl executables (#2576) 2026-10-29 16:02:07 -05:00
Chris Tate e3a42c52a8 Ignore SIGPIPE and SIGXFSZ in executables, as Node does
Node sets SIGPIPE and SIGXFSZ to SIG_IGN at startup, so a write to a
closed pipe or past RLIMIT_FSIZE fails with EPIPE or EFBIG, and libuv's
spawns start children with every signal at its default action. The
runtime instead blocked both signals in the writing thread. On Linux the
signal a write raises goes to that thread, but on macOS it goes to the
process, so a write to a closed pipe from the fs pool's threads ended the
program with SIGPIPE.

- scr_init ignores SIGPIPE and SIGXFSZ. A process.on listener replaces the
  disposition while it is registered, as before.
- scr_fs_write_once writes directly in executables, and the fs pool's
  threads no longer block the two signals. Library builds keep the
  per-thread blocking, since their host owns the dispositions.
- scr_spawn_command starts children with SIGPIPE and SIGXFSZ at their
  default actions (POSIX_SPAWN_SETSIGDEF), for spawnSync, execFileSync
  and spawn alike.

write-signals-ignored.ts and write-signals-listener.mjs check the ignored
signals, a listener, and each spawn path's child against Node.
2026-10-29 16:00:39 -05:00
Chris Tate 4e67c5ce2c Host stdio: typed process.stdout.write slot, write-callback errors, drain, run-time-typed chunks (#2546) 2026-10-29 15:59:21 -05:00
Chris Tate 7f10ed6d90 Use SSE2 and string-instruction memory routines in x86_64 musl executables
The musl lane links zig's libc, whose memset, memcmp, bcmp and strlen are
byte loops, and whose memcpy and memmove copy without string
instructions. An x86_64 musl executable now defines these six routines
(scr_musl_mem.h): SSE2 for short and medium lengths, and rep stosb or rep
movsb from 2 KiB for memset, memcpy and forward memmove. zig's
definitions are weak, so these replace them. Library artifacts keep the
routines of the program they link into.

Adds a test that checks every length up to 600 and longer ones, every
alignment in a 32-byte window, overlapping moves in both directions,
memcmp's sign at each position, and strlen up to the end of a readable
page, optimized and under ASan and UBSan.
2026-10-29 15:58:15 -05:00
Chris Tate 3b1a2c41b6 Merge main 2026-10-29 15:55:05 -05:00
Chris Tate b507472888 Take from a Readable's buffered queue by moving its head index (#2573) 2026-10-29 15:50:34 -05:00
Chris Tate f1825611fb Collect garbage cycles across the scriptc and V8 heaps (NI-8) (#1908) 2026-10-29 15:33:24 -05:00
Chris Tate 87c8d14510 Test262: String/Number/Boolean wrapper objects in JavaScript sources (#2550) 2026-10-29 15:24:04 -05:00
Chris Tate d972a6bc13 Defer island modules whose recorded load is deterministic (NI-7b/7d/7e/7f) (#1906) 2026-10-29 15:18:28 -05:00
Chris Tate 145f8fcd5e fs.watch names the changed entry; fs.watchFile polls in its own gated unit (#2507) 2026-10-29 15:13:12 -05:00
Chris Tate 03b26a7e8e Stdio write hook: console lines pass (line, errorHandler), byte chunks pass a Buffer (#2511) 2026-10-29 15:12:31 -05:00
Chris Tate dfa940a82d Return freed memory sooner: chunked shared writes, a bounded string spare, idle trims (#2518) 2026-10-29 15:12:10 -05:00
Chris Tate 4458e11a75 Read /proc/self/maps once per glibc thread heap (#2527) 2026-10-29 15:11:09 -05:00
Chris Tate 16f3c16752 Match Node's node:path behavior from Node 24's path tests (#2495) 2026-10-29 15:10:50 -05:00
Chris Tate 7eda6fa880 Trim the per-chunk work of compiled object-mode stream callbacks (#2523) 2026-10-29 15:09:24 -05:00
Chris Tate 4db725e622 Don't offset a null pointer when an array has no dense storage (#2559) 2026-10-29 15:07:27 -05:00
Chris Tate a9e2cb994f Deliver the lines a chunk split after a line listener closes the interface (#2533) 2026-10-29 15:07:08 -05:00
Chris Tate a391eef92a Read and write cached regular files on the loop thread in fs streams (#2540) 2026-10-29 15:06:47 -05:00
Chris Tate 7d283cbda3 Read at the exact bigint position in fs.readSync (#2535) 2026-10-29 15:05:39 -05:00
Chris Tate 5dc4bfd6d8 Read builtins into untyped TypeScript slots as Node's values (#2556) 2026-10-29 15:05:19 -05:00
Chris Tate d19fad3999 Match Node's emitWarning, kill, chdir and userInfo behavior (#2532) 2026-10-29 15:04:59 -05:00
Chris Tate e6f6cd4b98 Release the web view of an abandoned fetch body while its transfer runs (#2570) 2026-10-29 15:04:15 -05:00
Chris Tate 42e57e4ef3 Deliver a line to a single line listener without the listener snapshot (#2531) 2026-10-29 15:03:53 -05:00
Chris Tate 9632906b4d Take from a Readable's buffered queue by moving its head index
Taking the head entry of a Readable's buffer moved every remaining entry
down one slot, so draining a queue of n buffered chunks cost O(n²). The
buffer now keeps the index of its first entry: a take advances it, a push
at the tail moves the entries down only when at least half the slots are
free at the front (and grows otherwise), and unshift takes a freed front
slot when there is one. The drop, trace and boxing loops walk the entries
from that index.

The stream-buffered-drain runtime workload buffers 200,000 objects and
100,000 Buffers before draining them; corpus 8533 covers reads across
entries, unshift into a freed slot and onto a part-read head, setEncoding
after takes, and object-mode takes, unshifts and pushes.
2026-10-29 14:59:10 -05:00
Chris Tate d67f378a50 Release the web view of an abandoned fetch body while its transfer runs
A Response body's stream gets a web ReadableStream view when a member the
fetch stream lacks is read (for example `body.tee`). The view's source holds
the stream, and the stream released the view only when that source was its
last holder. While the transfer runs it holds the stream too, so a dropped
body with a view kept a reference count of 2: the unobserved-response
discard never ran, the paused socket was never resumed, and a compressed
response dropped before HTTP EOF kept the process alive.

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

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

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

The wrapper corpus program covers the prototypes' values, a getter read
once per call and console.log of a 40-level shared graph. The refusal
test covers a wrapper 100 levels deep, a wrapper under a shared graph and
util.inspect of a cyclic value.
2026-10-29 13:31:39 -05:00