Commit Graph
275 Commits
Author SHA1 Message Date
Chris Tate 5a091f050f Run the zlib one-shot forms on the stream engine (#1139) 2026-10-16 19:11:18 -05:00
Chris Tate fe86c85902 Support socket.address(), resetAndDestroy() and dropMaxConnection in net
socket.address(), socket.resetAndDestroy() and server.dropMaxConnection
were refused at compile time, and address() and resetAndDestroy() also
through untyped socket handles. They now match Node:

- address() answers { address, family, port } while the socket has a
  local address, and {} while it connects or once it is destroyed. The
  declared AddressInfo | {} result is a checked-dynamic value, so a type
  assertion to AddressInfo narrows it.
- resetAndDestroy() closes the connection with an RST (SO_LINGER with a
  zero timeout), so the peer reads ECONNRESET, and 'close' reports
  hadError false. A connecting socket resets once it connects: the reset
  is a once('connect') listener, so it runs at that place among the
  'connect' listeners. A destroyed socket is left alone, a TLS socket
  throws ERR_INVALID_HANDLE_TYPE, and the call answers the socket.
- dropMaxConnection is a stored value (undefined until set), since Node
  reads it only in cluster mode.

Corpus 5657 (typed) and 5658 (untyped handles and the TLS throw) and the
node-types fixture net-reset-address.ts cover each member.
2026-10-16 10:01:22 -05:00
Chris Tate ed5ab6796b Run the zlib one-shot forms on the stream engine
deflateSync, inflateSync, gzipSync, gunzipSync, deflateRawSync,
inflateRawSync and unzipSync, and their callback forms, accept Node's
options and run Node's processChunkSync over the zlib stream engine.

- Options (windowBits, level, memLevel, strategy, dictionary, chunkSize,
  finishFlush, maxOutputLength, rejectGarbageAfterEnd) are validated at
  run time with Node's ladder, shared with the stream classes; info is
  refused at run time.
- Errors carry Node's errno and code; maxOutputLength reports
  ERR_BUFFER_TOO_LARGE and rejectGarbageAfterEnd
  ERR_TRAILING_JUNK_AFTER_STREAM_END. The default-options decoders also
  read every gzip member.
- Untyped JavaScript inputs may be strings, TypedArrays, DataViews or
  ArrayBuffers; other values throw Node's ERR_INVALID_ARG_TYPE after the
  options are checked.
- Results are Buffers, as in Node: they convert to their text.

Corpus programs 5378, 5379 and 5415 cover the sync, callback and untyped
input forms.
2026-10-16 09:18:32 -05:00
Chris Tate daa5cc256f Lower util.promisify over the fs callback functions
promisify(fs.X) over a named or namespace import of node:fs lowers as
the fs/promises member of the same name for access, appendFile, chmod,
chown, copyFile, lchown, link, lstat, lutimes, mkdir, mkdtemp, readFile,
readdir, readlink, realpath, rename, rm, rmdir, stat, statfs, symlink,
truncate, unlink, utimes and writeFile. For these, Node's promisified
callback form settles with the callback's single result and rejects
with the same errors as the fs/promises call. Calls through the bound
function accept the forms the fs/promises lowering accepts and refuse
the others by name.

fs.open (an fd number where fs/promises.open gives a FileHandle),
fs.exists (a custom promisify) and fs.read/fs.write (multi-value
callbacks) remain refused.
2026-10-16 09:14:35 -05:00
Chris Tate 21c0e17178 Merge main 2026-10-16 08:17:52 -05:00
Chris Tate 6da5ad6eb9 Lower events.once over EventEmitter instances (#1061) 2026-10-16 08:01:35 -05:00
Chris Tate ae9e629acb Lower util.promisify(child_process.exec) (#1118) 2026-10-16 07:54:07 -05:00
Chris Tate 4adc3e6f2a Support the dgram socket options and disconnect()
dgram.Socket's setBroadcast, setTTL, setMulticastTTL,
setMulticastLoopback, setMulticastInterface, addMembership,
dropMembership, get/setRecvBufferSize, get/setSendBufferSize,
getSendQueueSize, getSendQueueCount and disconnect were refused at
compile time. They now lower to scr_dgram.c and match Node:

- The TTL setters and setMulticastLoopback return their argument. The
  value checks run first: a TTL outside 1-255 (0-255 for multicast,
  after ToInt32) throws "setTTL EINVAL" with code, errno and syscall.
- A socket that has not been bound has no descriptor yet, so the
  options throw EBADF, and the buffer size methods throw Node's
  ERR_SOCKET_BUFFER_SIZE SystemError.
- After close(), the members that read Node's null handle throw its
  TypeError ("Cannot read properties of null (reading 'setTTL')"), and
  setMulticastInterface and the membership calls throw "Not running".
- Buffer sizes must be uint32 values (ERR_SOCKET_BAD_BUFFER_SIZE), 0
  reads the current size, and sizes past the int32 range fail as EINVAL.
- IPv6 interfaces and groups go to the IPv6 socket options, so a udp4
  socket answers ENOPROTOOPT as in Node.
- disconnect() throws ERR_SOCKET_DGRAM_NOT_CONNECTED until 'connect' was
  emitted, and dissolves the association with an AF_UNSPEC connect.

addMembership and dropMembership before bind() throw SC2020, where
Node binds the socket to an ephemeral port first; the limitations page
documents it, along with the missing info object on the buffer size
SystemError. Corpus 5655 and 5656 and the node-types fixture
dgram-options.ts cover each method.
2026-10-16 07:34:12 -05:00
Chris Tate 60623086a3 Lower util.promisify(child_process.exec)
A const binding of promisify(exec) now lowers like promisify(execFile):
each call runs the command line with /bin/sh -c, as execSync does, and
settles with { stdout, stderr }. A non-zero exit, a missing command (the
shell's 127) or a timeout rejects with Node's "Command failed: <command>\n
<stderr>" message. The options are the execFile slice: encoding utf8,
cwd, env (inline spread included), timeout, and maxBuffer accepted.

Three gaps closed for both targets along the way:

- the { stdout, stderr } result maps as a record when it is held in a
  variable, passed or returned, instead of only when destructured at the
  await (it is an anonymous literal in @types/node and in the fallback);
- a namespace member is a recognized target (promisify(cp.exec));
- util.promisify(...) through a namespace or default import is
  recognized like the named import.
2026-10-16 07:32:06 -05:00
Chris Tate 90a6e66422 Merge main 2026-10-16 07:31:21 -05:00
Chris Tate 92032a1b11 Shrink Map and Set headers and small entry arrays
A Map or Set header held six adapter pointers, the union key table and
size_t counts: 168 bytes, 176 in its size class. The adapters and union
key kinds now live in an immutable per-type ScrMapOps descriptor that the
compiler emits once per collection type, a traced byte replaces the two
trace-pointer tests, and the counts and capacities are 32-bit. The header
is 88 bytes.

The entries array starts with one slot and grows to four, then doubles,
and maps of up to 8 entries scan their cached hashes instead of keeping a
bucket table. Number-key maps still move to the direct index from four
entries.

One million maps held at once (RSS per map): empty 188 -> 100 bytes,
1 entry 286 -> 125, 4 entries 286 -> 198, 8 entries 450 -> 295.
2026-10-16 07:27:17 -05:00
Chris Tate 0d59abe143 Pool small Buffers as Node does
Buffer.from of strings, arrays and Uint8Arrays, Buffer.allocUnsafe and
Buffer.concat now return views into a per-thread pool slab of
Buffer.poolSize bytes (64 KiB by default) when the size is below
Buffer.poolSize >>> 1, following Node's allocate, fromStringFast and
fromArrayLike: 8-byte aligned offsets, a new slab when a request does not
fit, and the four-bytes-per-unit reservation for strings. Buffer.poolSize
can be read and set, and Buffer.allocUnsafeSlow returns an unpooled Buffer.
A slab lives while any of its views does. Its ArrayBuffer cannot be
transferred, and structuredClone of a typed array view copies the whole
ArrayBuffer and keeps the view's offset. Buffer.alloc, allocUnsafe and
allocUnsafeSlow validate sizes with Node's ERR_OUT_OF_RANGE error.

The first pooled allocation finds the startup slab, which has the
default 64 KiB size. A request it cannot hold gets a slab of the current
Buffer.poolSize, as in Node, and the room is checked after every new
slab, so a Buffer.poolSize raised before the first pooled allocation
cannot place a reservation past the end of a 64 KiB slab. A reservation
larger than a fresh slab traps instead of writing past it.

Corpus 5531 (typed) and 5532 (untyped) raise Buffer.poolSize before the
first pooled allocation and allocate just under the new threshold
through each pooled path, each case in a fresh child process, and also
shrink Buffer.poolSize and change it between allocations. The
node-types fenced fixture no longer lists Buffer.poolSize, which now
compiles.
2026-10-16 01:39:10 -05:00
Chris Tate 42954a3266 Add mock.fn and t.mock.fn with Node's calls log and implementation controls
node:test's mock.fn(original[, implementation]) and t.mock.fn compile.
A Mock<F> is the function-with-properties record { %call, mock }: the
callable is a wrapper closure that shares the mock context with the mock
field. The context records each call's arguments, result, error and this,
and keeps the current implementation, the original and the one-time
implementations by call index. fn.mock supports, as in Node 24.22:

- calls, read as a copy of the log;
- callCount();
- mockImplementation(fn);
- mockImplementationOnce(fn[, onCall]), for the next call by default, a
  later registration for the same index replacing the earlier one;
- resetCalls();
- restore(), which brings back the original.

A throwing implementation records the error and rethrows it. Functions
with fixed parameters or a single rest array parameter are supported; a
union return type needs an undefined arm. mock.fn() without an original,
mock.method, the tracker's reset and restoreAll, and t.mock.method are
refused with SC2020.

The node:test fixture mock-fn compares the behavior with Node, and
tests/diagnostics/mock-fences.ts covers the refusals.
2026-10-16 00:22:22 -05:00
Chris Tate 681f5ceee7 Add TestContext hooks, plan and fullName, and Node's todo and only rules
node:test changes, matching Node 24.22:

- t.before, t.after, t.beforeEach and t.afterEach lower. t.before runs
  when it is called; a throw fails the test and the subtests that follow,
  which skip their body and beforeEach hooks and run their afterEach hooks.
  t.beforeEach and t.afterEach wrap every descendant subtest. t.after runs
  after the test's afterEach chain, also when the body throws, and a throw
  fails the test.
- t.plan(count) counts the test's t.assert calls and subtests. A mismatch
  fails the test with Node's "plan expected N assertions but received M"
  string, unless an assertion failed first.
- t.fullName joins the names from the outermost suite or test with " > ".
- Tests and suites under a todo suite or test are todo, with a bare TODO
  directive, unless they are skipped or carry their own todo message. A
  todo suite reports its directive.
- describe.only, suite.only and { only: true } on a suite mark it. At each
  level, when a child is marked or contains a marked test or suite, only
  those children run.
- The failing-tests section lists failures in completion order, so
  subtests come before the test that ran them, and prints thrown strings
  quoted, as Node inspects them.

The fixtures ctx-hooks, ctx-plan, todo-suites, suite-variants,
only-nested and thrown-values compare the reporter output and exit code
with Node.
2026-10-15 23:16:04 -05:00
Chris Tate 81ed97c5a7 Lower timers/promises resolve values and setInterval under @types/node (#1058) 2026-10-15 21:33:25 -05:00
Chris Tate 439a4fee93 Run the event loop for timers/promises calls
A timers/promises timer that nothing awaited did not keep the process
alive: the loop only ran when the program used the global timers, async
functions or generators. Node keeps a pending ref'd timer's process open,
and the timer heap's exit teardown releases an unref'd one only when the
loop runs, so a pending { ref: false } timer stayed live at exit. Any
timers/promises call now runs the loop.

The node-types fixture no longer attaches .then to the never-firing
unref'd timer, whose suspended continuation the sanitizer's RC audit
reports at exit. Corpus 5620 no longer orders a 1ms timeout against an
immediate, which Node itself does not order from the main module.
2026-10-15 20:42:37 -05:00
Chris Tate b00d65b8be Merge main 2026-10-15 19:57:06 -05:00
Chris Tate 816c664058 Lower events.once over EventEmitter instances
events.once(emitter, name) was refused. The emitter dispatches statically
emitted events to typed listeners through invokers built per argument
tuple, so the lowering takes the event's tuple from the program-wide event
table and builds a capture-free function that boxes that tuple into the
any[] Node fulfills with, and one for the 'error' tuple.
scr_events_once registers both as one-shot typed listeners behind emitted
adapters (emitterOnceAdapter). The first to fire settles the promise
(fulfilled with the arguments, or rejected with the error) and removes
the other, as in Node; awaiting 'error' itself registers only the one
listener. The shared state holds the emitter and the listener closures
weakly, so a never-fired once creates no cycle.

Options, computed or meta event names, EventTarget and stream receivers,
and events emitted with computed names or checked-dynamic listeners stay
refused. The fallback declarations type the result Promise<unknown[]>;
under @types/node (Promise<any[]>) TypeScript code narrows it to
unknown[].
2026-10-15 18:26:31 -05:00
Chris Tate 54dc43ed14 Lower timers/promises resolve values, the ref option and setInterval under @types/node
timers/promises setTimeout(delay, value) and setImmediate(value) were
refused: only the void forms lowered. The value now travels as an
already-resolved promise of its own type (the Promise.resolve lowering),
and scr_tp_set_timeout_value / scr_tp_set_immediate_value copy its
settlement into the result when the timer or immediate fires, so the
result keeps the value's type. An options object literal takes `ref`;
`ref: false` unrefs the timer, so it no longer keeps the process alive.
AbortSignal options and other option forms stay refused, with their own
messages.

@types/node types setInterval as NodeJS.AsyncIterator<T>, which maps to
no IR type, so a for await loop over setInterval(delay, value) was refused
in projects with @types/node. The interval's async generator type now
comes from the iterator's type argument, and a for await loop whose head
is that call consumes it directly.

The fallback declarations type the new forms.
2026-10-15 18:18:45 -05:00
Chris Tate f7362fc386 Match Node's http keep-alive, header and framing behavior
- server.close() closes idle keep-alive connections, and every open
  connection closes after keepAliveTimeout + keepAliveTimeoutBuffer, so a
  closed server whose clients keep their connections no longer stays up.
  The Keep-Alive response header gives keepAliveTimeout in whole seconds
  and is omitted when it is 0.
- A client request through an Agent with neither keepAlive nor a finite
  maxSockets sends Connection: close.
- "socket hang up" and "aborted" errors carry code ECONNRESET, a client
  head over the header limit fails with HPE_HEADER_OVERFLOW, and TLS
  verification failures carry their openssl code.
- Outgoing header values keep the type they were set with: getHeader and
  getHeaders answer numbers and arrays, appendHeader turns an existing
  value into an array, and setHeader(name, string[]) writes one line per
  element.
- Repeated incoming header fields join with ", " ("; " for cookie),
  Node's single-value fields keep the first value, and set-cookie reads
  as an array under @types/node, on server requests and client responses.
  Fetch reads response headers line by line.
- Expect: 100-continue is answered with 100 Continue before 'request';
  other expectations answer 417.
- HEAD requests and 1xx/204/304 responses send no body or length header,
  and HTTP/1.0 requests without a set length get an unframed body and
  Connection: close.
- The 16 KiB maxHeaderSize applies to the request target plus header
  names and values (431 on the server).
- https.Agent TLS options are refused by name instead of being dropped,
  and server.address() lowers for @types/node's
  string | AddressInfo | null result.

Corpus 5555-5563 and the node-types http-headers-address fixture cover
each case.
2026-10-15 13:06:33 -05:00
Chris Tate 59cdda3930 Merge main 2026-10-15 09:20:01 -05:00
Chris Tate 0b468e40a1 Add fs.globSync, fs.glob and fs/promises glob
The three glob entry points run a port of Node 24's
lib/internal/fs/glob.js walk in scr_glob.c: the same LIFO queue of
(path, patterns) items over the sorted directory listings, the same
lstat, stat, readdir, realpath and seen-pattern caches, the same result
set in insertion order, and the same exclusion checks, so matches come
out in Node's order. Patterns compile with the existing minimatch port.
glob_match is split into a compile step and a test step so the walk can
reuse each compiled pattern and test it segment by segment.
nocaseMagicOnly is now an option, because exclude patterns turn it off.

Patterns (a string or string array) and options (cwd as a path or
file: URL, exclude as patterns or a function, followSymlinks,
withFileTypes) are validated in Node's order with Node's errors,
including the internal assertion Node hits for a non-string cwd with
exclude patterns. withFileTypes results are Dirent rows: name,
parentPath and the type probes. parentPath follows Node's rule for each
form: the joined path for globSync, the resolved path for fs/promises
glob.

fs.glob checks the callback and validates synchronously, then delivers
string[] or the walk's error through the filesystem callback queue.
fs/promises glob lowers as the source of a for await loop. The walk runs
when the loop starts, and the matches found before an error bind before
the error is thrown, as Node's async iterator yields them first. The
older glob(3)-based fs.glob branch is removed.

Corpus 5448-5452 and a node-types fixture check the behavior against
Node.
2026-10-15 07:59:19 -05:00
Chris Tate 416311f2c5 Append to strings in place for fields, concat chains and character codes
The runtime appends to a string in place when the left operand is
uniquely owned, but three common forms never got there:

- `obj.field += s` on a plain class or record field kept a second
  reference to the old value, so every append copied the whole string.
  The compound assignment now reads a stored string field into a mutable
  local, stores "" into the field (no code runs in between: both operands
  are already strings), runs the self-append `old = old + rhs` and stores
  the result.
- `s = s + a + b` copied per append: the self-append recognized only
  `s = s + x`. A concat chain whose leftmost operand is the target now
  takes the remaining operands as the suffix, in the same order.
- `s += String.fromCharCode(c)` built a one-character string first.
  scr_str_concat_move_char_code appends the character, in place for ASCII.

Self-appends also borrow the suffix instead of retaining it, and a
discarded compound assignment no longer retains and releases its result.
2026-10-15 06:17:38 -05:00
Chris Tate a08fbd2746 Merge main into createCipheriv support 2026-10-15 03:12:04 -05:00
Chris Tate 5336966743 Merge main into createCipheriv support
Keeps the cipher support together with the hash, random and
timingSafeEqual surface added on main.
2026-10-15 02:22:02 -05:00
Chris Tate 731462be5c Strip VT control characters in one pass
util.stripVTControlCharacters ran Node's ANSI pattern through the
regex replace loop, which tried the OSC branch's lazy scan at every
introducer. Text with many unterminated OSC introducers took quadratic
time (about 200 s for 100,000 of them).

The function now scans once and gives the same result as Node 24.22's
pattern: the OSC branch ends at the first terminator after ESC ], and
that position is cached across introducers; the CSI branch resolves its
first match without backtracking. A removal that brings lone surrogates
together keeps canonical storage.

Corpus programs 5470-5474 cover OSC and CSI forms, seeded random input,
long unterminated runs and surrogates, and strip-vt-linear.test.ts checks
that 400,000 unterminated introducers finish quickly.
2026-10-14 23:43:52 -05:00
Chris Tate 4f6a972df6 Support every Node 24 digest in createHash, createHmac, hash, pbkdf2 and hkdf (#1006) 2026-10-14 23:15:20 -05:00
Chris Tate 23b6cb8819 Support every Node 24 digest in createHash, createHmac, hash, pbkdf2 and hkdf
createHash, createHmac, crypto.hash, pbkdf2, pbkdf2Sync, hkdf and hkdfSync
accept every digest crypto.getHashes() lists in Node 24: SHA-512/224,
SHA-512/256, SHA-256/192, SHA3-224 to SHA3-512, Keccak-224 to Keccak-512,
SHAKE128, SHAKE256, KECCAK-KMAC-128, KECCAK-KMAC-256, RIPEMD-160,
BLAKE2b-512, BLAKE2s-256, SM3 and MD5-SHA1. The new runtime unit
scr_hash.c implements Keccak, RIPEMD-160, BLAKE2b, BLAKE2s and SM3, and
is registered in both runtime pack matrices and the C-compile path.

Names resolve as Node 24.22 resolves them: case-insensitively, with
OpenSSL's provider aliases and object identifiers, ending at the first
NUL, and with trailing spaces allowed except on the RSA signature
aliases. "dss1" and "DSS1" are SHA-1, and createHash and crypto.hash
accept "dss1" in any case. The compiler no longer refuses literal digest
names outside SHA-1/SHA-2 and MD5; unknown names throw Node's errors at
run time.

SHAKE and KMAC digests use their default output lengths. createHmac
rejects them with Node's OpenSSL error, pbkdf2 with "xof digests not
allowed", and hkdf with "Deriving bits failed". pbkdf2 with a key length
of 0 now fails with "Deriving bits failed" as in Node 24.22, and the
callback forms pass these failures to the callback.

PBKDF2 computes the keyed HMAC states once instead of on every
iteration, and digest contexts are cleared with memset and a compiler
barrier instead of a byte loop. The Keccak permutation, the RIPEMD-160
and BLAKE2 compression functions and the SM3 rounds are unrolled with
constant indices.
2026-10-14 22:15:06 -05:00
Chris Tate bd438a6d5e Add Blob and File
Blob and File (globals and node:buffer exports) compile to native
handles in the new scr_blob.c, following Node 24.22's
lib/internal/blob.js and file.js. A Blob is a window over a shared,
immutable byte store plus the byte length of each part. Sources (strings,
typed arrays, DataView, ArrayBuffer, Buffer, other Blobs, Set and Map)
convert as a Web IDL sequence, with each non-buffer part converted to a
string, before the options are read, and are copied when the Blob is
built. The constructors throw Node's Web IDL TypeErrors and its
ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE and ERR_MISSING_ARGS errors,
and read and convert their options in Node's order. The type is
lowercased, or cleared when it holds a character outside U+0020..U+007E.
endings: "native" rewrites CRLF, CR and LF to the platform's line break.
File takes its name with lone surrogates replaced and lastModified as a
number (Date.now() when absent).

slice() converts its offsets as a [Clamp] long long (rounded half to
even) and keeps the parts Node's data queue keeps. text(), bytes() and
arrayBuffer() settle on the same microtask turn as in Node, which depends
on the part count. util.inspect prints Node's `Blob { size, type }` and
`File { ..., name, lastModified }` forms, with `[Blob]` past the depth
limit. String() and toString() give the class tag, JSON.stringify gives
{}, and structuredClone returns a new Blob or File over the same bytes.
instanceof works for both spellings. stream(), textStream() and the
Object.prototype members other than toString() fail at compile time.
Non-array iterables other than Set and Map throw SC2020.

Three handle-table additions support this: a structuredClone hook, a
JSON `{}` flag, and a bracketed past-depth inspect flag. scr_blob.c is an
optional unit selected by dynAsync, because the dynamic dispatch path
boxes its reads as dyn promises through scr_async_dyn.c.

Corpus 5285-5290 and a node-types fixture check the behavior against
Node.
2026-10-14 21:17:46 -05:00
Chris Tate 7dfe4e8bea Merge main and update test fixtures for the cipher link feature
The native link feature records in the self-hosting tests list cipher,
and the library profile test uses crypto.createSign for its remediation
example now that createCipheriv is supported.
2026-10-14 20:31:47 -05:00
Chris Tate f7390b4f05 Match Node 24.22 HTTP write coalescing, fetch connection headers and crypto.constants
OutgoingMessage now corks the message for a tick like Node 24.22, so
chunked writes in one tick (and a corked end(chunk)) go out as one chunk,
for server responses and client requests. Fetch accepts a connection
header that is a comma list of tokens, as Undici 7.29.1 does. The
crypto.constants declaration matches the Node 24.22 table, with a pin test.
2026-10-14 18:12:09 -05:00
Chris Tate c43ff734fa Merge main 2026-10-14 17:00:49 -05:00
Chris Tate c9fe456a96 Support crypto.createCipheriv and createDecipheriv 2026-10-14 15:14:14 -05:00
Chris Tate 63012c09b1 Add readline/promises, stream.addAbortSignal and the stream/promises options 2026-10-14 14:10:43 -05:00
Chris Tate f7a9887709 Add process.memoryUsage, process identity members, and performance marks and measures (#977) 2026-10-14 13:57:47 -05:00
Chris Tate f3ace69760 Store scalar-union fields inline in records and objects 2026-10-14 13:02:38 -05:00
Chris Tate f951af1e8f Merge main into process and perf_hooks 2026-10-14 12:46:19 -05:00
Chris Tate 47223bdfaa Use plain wording in docs, comments and test names (#981) 2026-10-14 12:25:43 -05:00
Chris Tate 3f8e0bcb13 Use plain wording in docs, comments and test names 2026-10-14 11:43:44 -05:00
Chris Tate 5ef650b5e7 Merge main into the legacy url API 2026-10-14 11:30:47 -05:00
Chris Tate 86f77c88e9 Add process.memoryUsage, process identity members, and performance marks and measures 2026-10-14 09:58:18 -05:00
Chris Tate bd9a994060 Add the legacy url API and url.domainToASCII/domainToUnicode 2026-10-14 08:10:02 -05:00
Chris Tate 3008f2ff1a Support util.inherits, util.deprecate, util.callbackify and util.debuglog 2026-10-14 08:08:06 -05:00
Chris Tate e932714bfa Shrink the cycle-collector object header to 8 bytes 2026-10-14 04:53:07 -05:00
Chris Tate e646b347de Complete node:os and add path.parse and path.format 2026-10-14 00:25:07 -05:00
Chris Tate 7e6294ca0f Make scriptc compilation native-only 2026-10-12 12:25:30 -05:00
Malte Ubl f4eafed2b5 Number collector types in the self-hosting layout fixture
Emitted ScrCycType descriptors now carry collector statistics indices from ShapeHost.nextCycKind, counted from FIRST_EMITTED_CYC_KIND. The llvm-layouts.ts host lacked the hook, so its class descriptors and vtables stamped kind 0 and the native layout emission diverged from the production emitter's.
2026-10-11 09:39:58 -07:00
Malte Ubl 4fd8d5d3b5 Give the self-hosting layout fixture the cycle header's buffered offset
The 16-byte collector header replaced ClassHost.cycleColorOffset with cycleBufferedOffset (6 bytes behind every object on every target); llvm-layouts.ts still set the removed property and lacked the required one.
2026-10-11 09:38:27 -07:00
Malte Ubl 63c45c99ee Tell LeakSanitizer that published objects are immortal
Publication (@scriptc/threads publish() and the zero-copy delivery of
deeply frozen plain-data graphs) makes objects immortal: nothing frees
them, and the RC audit already stops counting them. LeakSanitizer did not
know, so a program that drops its last reference to a published graph
reported every such object as a direct leak whenever leak detection was
on (SCRIPTC_SAN=1 runs on Linux without ASAN_OPTIONS=detect_leaks=0, such
as the documented local fallback). Both publication paths now mark each
object they make immortal with the weak __lsan_ignore_object hook in ASan
builds; ignored chunks are leak-scan roots, so the verdict on the rest of
the heap is unchanged. Other builds compile the hook away.

The sanitized lanes run with detect_leaks=0, so a harness test pins the
contract with LeakSanitizer on (Linux, SCRIPTC_SAN=1 lane).
2026-10-11 06:39:41 -07:00
Malte Ubl 20b7da29e7 Stream serialized IR and LLVM instead of building one string
The self-hosted compiler's compact IR is ~515-528M characters, a few
million below V8's maximum string length (2^29 - 24). serializeModule
joined it into one string, deserializeModule parsed one string, and the
native bootstrap comparison read cli.ir.json and cli.ll as utf8 strings,
so any compiler growth crashed --emit=ir and the bootstrap tests with
"RangeError: Invalid string length".

- serializeModuleParts returns the compact encoding as ordered parts
  (header, one part per function, remainder). serializeModule(mod, true)
  is their join, so the on-disk format is byte-identical.
- deserializeModuleBytes splits a UTF-8 Buffer at the root object's
  "functions" array with a byte-level scanner and parses the header and
  each function separately. Malformed input falls back to the
  whole-document parse for its exact error.
- The Node API writes --emit=ir, --emit-ir sidecars and --emit=llvm
  through writeTextParts (the former writeLlvmSource batching writer).
- The native bootstrap harness decodes IR per function and compares
  LLVM as Buffers, removing ABI-check lines at the byte level.
- The self-hosting serialization fixture gains a "bytes" mode so the
  scriptc-compiled serializer exercises both new entry points.

Self-hosted compiler IR (514.6 MB, macOS arm64, Node 24.12):
  read  old 5.85 s / 4.26 GB max RSS -> new 5.84 s / 1.36 GB
  write old 2.05-2.53 s / 4.48 GB    -> new 2.30-2.37 s / 2.10 GB
  2x module (1.03 GB): old serialize and read throw; new round-trips.
The remaining per-string bound is the largest function (16.3M chars,
~33x headroom) and the header (2.3M chars).
2026-10-10 20:39:35 -07:00