Commit Graph
651 Commits
Author SHA1 Message Date
Chris Tate 8823556a84 Let constructor functions inherit from EventEmitter and the stream classes (#1512) 2026-10-20 11:32:22 -05:00
Chris Tate e9fb01603f Compile NodeJS.ReadableStream and NodeJS.WritableStream as general streams when a program mixes stream producers (#1518) 2026-10-20 11:24:09 -05:00
Chris Tate 25780d9231 Merge main 2026-10-20 11:03:09 -05:00
Chris Tate 8eb5ad28e5 Keep WebAssembly references and memory storage alive while they are in use; merge main
Externrefs a guest holds are borrowed pointers. An import's result was
kept only until the outermost call returned, which was before the export
call converted its results to JavaScript, so a result created by an
import was freed before conversion. A table or global slot released the
reference it replaced at once, although the guest may still hold it in a
local after reading the slot. The kept references are now released after
the outermost caller has converted its results (scr_wasm_leave), and a
table or global store during a call keeps the reference it replaces until
then.

Checked memories (explicit bounds checks: sanitized builds, Windows)
grew with realloc, which can move the storage that memory.buffer and its
views alias. They now reserve the address space for their largest size
(mmap on POSIX hosts, VirtualAlloc on Windows) and commit pages as they
grow, so the base never moves. Hosts without address-space reservation
grow in place only until a buffer has been handed out; after that,
growth that would move the storage fails.

Tests: webassembly-externref-lifetimes.mjs (import results through an
export, table and global references replaced by the guest and by
JavaScript), webassembly-memory-growth.test.ts (views written after
growth from JavaScript and from the guest), and translate.test.ts cases
for memargs naming memory 0 explicitly and for local.tee operand types,
which b3bcbaaf7 made consistent with validation. Under SCRIPTC_SAN=1 the
two runtime tests report heap-use-after-free without these changes.

Merges main; internal/compatibility/generated/node-v24-backlog.json is
regenerated with pnpm node-compat.
2026-10-20 10:58:34 -05:00
Chris Tate 89f8dbe9e0 Merge main 2026-10-20 10:23:43 -05:00
Chris Tate da476df676 Merge main 2026-10-20 10:11:51 -05:00
Chris Tate 575f6491f4 Fix silent wrong output: object identity, Object.prototype values, Date parsing, stored builtins (#1336) 2026-10-20 09:18:37 -05:00
Chris Tate 56de47defc Check programs with tsc-ts by default (#1353) 2026-10-20 09:12:30 -05:00
Chris Tate 7d555c4cf3 Merge main 2026-10-20 09:04:02 -05:00
Chris Tate 55f4eaca5b Merge main 2026-10-20 09:02:52 -05:00
Chris Tate 7cd7b483bf Merge main 2026-10-20 09:02:06 -05:00
Chris Tate 9e54b4a198 Merge main 2026-10-20 09:00:02 -05:00
Chris Tate ce5e4294ad Merge main 2026-10-20 08:49:25 -05:00
Malte UblandClaude Opus 5.5 b9aaaa9d8c Answer server.address() with the socket path for Unix-socket servers
Node answers a Unix-socket server's address() with its path. Programs
that call listen() with a string argument now get a Unix-aware
address(): typed `string | AddressInfo | null` (the @types/node
declaration), it answers the path; typed as the AddressInfo record alone,
which cannot hold the path, it throws for such a server, as does the
composed address().port read. Every other program keeps the record-only
lowering, so their output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 04:45:43 -07:00
Malte UblandClaude Opus 5.5 447e9bf000 Compile Transform option callbacks and loosely typed child-process listeners
@types/node types the transform/flush completion callback's data as any
and a child stream 'data' chunk as any, and ChildProcess accepts
(code: number, signal: string) exit and close listeners through its
catch-all overload. None of these compiled: the any-typed completion
callback crashed the LLVM emitter, and the child listeners were refused.

The completion callback's data parameter is typed Buffer | string |
undefined while the option callback lowers. Child stream 'data' listeners
with an unknown or any chunk box it as a checked-dynamic value, taking
strings when a setEncoding statement on the same stream precedes the
listener and Buffers when the function never calls setEncoding on it;
other placements are refused. Exit and close listener parameters declared
number or string bind as checked-dynamic values, so the body sees the
null Node passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 04:16:06 -07:00
Malte UblandClaude Opus 5.5 95b2f81716 Pipe into untyped destinations by unwrapping boxed native streams
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 03:24:08 -07:00
Malte UblandClaude Opus 5.5 40b80116e4 Merge origin/main into opt/http-socket-path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 03:22:28 -07:00
Malte UblandClaude Opus 5.5 cb26c95f61 Read http.request options at run time, speak HTTP over Unix sockets, and fill the boot path's net and fetch gaps
http.request, https.request and get now accept options the compiler
cannot see as an object literal: a RequestOptions variable, a literal
that spreads one, or a literal that sets socketPath. The runtime reads
the record in the ClientRequest constructor's order with Node's errors;
documented options it does not implement throw the runtime fence when
they carry a value. https adds rejectUnauthorized, ca and servername.

socketPath dials a Unix domain socket in the net unit; the agent keys
its sockets by the path as Node's getName does, and the Host header
defaults to localhost. server.listen(path[, cb]) binds a Unix socket and
removes the socket file when the server closes; a numeric string is
still a port. Errors name the path ("connect ENOENT /path", "listen
EADDRINUSE: address already in use /path"). Windows reports ENOTSUP.

A TypeScript object literal with no contextual type that spreads a
checked-dynamic value maps to the checked dynamic, so
`const options = { ...location, path, timeout }` compiles.

A socket's 'lookup' event reaches its listeners with (err, address,
family, host) for each address a host name resolves to, or once with
the lookup error, before 'connect'. Only programs that listen for
'lookup' install the lookup records; elsewhere the hook sites are a NULL
check.

server.close((err) => ...) runs the callback with undefined at 'close',
or with ERR_SERVER_NOT_RUNNING when the server was not listening; a
server that never listened now emits 'close'. IncomingMessage 'data'
listeners may take string chunks after setEncoding, and res.end()
accepts a string-or-undefined chunk.

fetch's BodyInit and HeadersInit unions, as RequestInit-derived option
types spell them under @types/node, map to the checked dynamic, and a
URLSearchParams body is sent form-encoded, as by Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:57:13 -07:00
Malte UblandClaude Opus 5.5 915c0f63c5 Request the general stream representation where a stdio handle meets the stream surface
child.stdout.pipe(), stream events and writes beyond write(string) on a
NodeJS stream interface slot, and pipes into a child's stdin now lower
the program with the general representation instead of fencing. The
fallback ChildStdin is a NodeJS.WritableStream, and the coverage
remainder relowers its unreached code the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:40:28 -07:00
Malte UblandClaude Opus 5.5 1fb1d9c829 Let constructor functions inherit from EventEmitter and the stream classes
Bundled npm code (glob, yazl, from2, multistream) subclasses Node's
classes with constructor functions: util.inherits(Ctor, EventEmitter) and
EventEmitter.call(this). util.inherits received an identity token and
threw ERR_INVALID_ARG_TYPE, EventEmitter.call(this) and
EventEmitter.prototype were refused, and `this instanceof Ctor` against
a function was refused.

A new runtime unit (scr_dyn_builtin_class.c, linked by the new
dynBuiltinClass feature) provides EventEmitter, the legacy Stream and
the stream classes as untyped constructor functions with Node's
prototype chain. EventEmitter's prototype ports lib/events.js over the
object's own _events, _eventsCount and _maxListeners properties; Stream
adds the legacy pipe. util.inherits, Base.call/apply(this, ...),
Base.prototype, instanceof on untyped values and JavaScript value reads
of these classes use them. Constructing over a stream class throws
SC2020 where it runs. instanceof against a compiled constructor function
compares its prototype with the value's prototype chain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:49:36 -07:00
Chris Tate c8b22e3435 Merge main 2026-10-20 02:40:17 -05:00
Chris Tate d5d9a1185a Compile process.send presence, process.exitCode reads, wider stream hooks, void race entries and promisify of callbacks (#1429) 2026-10-20 02:18:40 -05:00
Chris Tate 391b9ac00e Merge main 2026-10-20 02:15:13 -05:00
Chris Tate d3a567f830 Pass rest-parameter functions into slots with fixed parameters (#1480) 2026-10-20 02:13:34 -05:00
Chris Tate f77298ea82 Merge main 2026-10-20 01:49:45 -05:00
Malte Ubl 25e40afa83 Merge remote-tracking branch 'origin/main' into opt/bc-node-surface 2026-10-19 23:16:41 -07:00
Chris Tate 98a7d3b33d Merge main 2026-10-20 00:08:29 -05:00
Malte UblandClaude Opus 5.5 73bd0b53c3 Pass rest-parameter functions into slots with fixed parameters
A typed rest-parameter function passed where a function with fixed parameters is expected (`log(...m: string[])` into `(m: string) => void`) was refused. The function adapter now packs the slot's remaining arguments into the rest array, and the value check admits the function wherever that adapter fits. A nested function declaration with a typed rest parameter becomes a value with its own typed-rest signature, converted at each use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:23:54 -07:00
Malte UblandClaude Opus 5.5 72eab2aa0e Document process.exitCode reads on the limitations page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:06:01 -07:00
Malte UblandClaude Opus 5.5 fa64a7cf5a Merge main into opt/bc-node-surface
Main split lower-exprs.ts into expression families; the typeof and __dirname changes move to expressions/typeof.ts and expressions/identifiers.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:42:15 -07:00
Chris Tate 431a8d6874 Merge main 2026-10-19 21:39:39 -05:00
Chris Tate 3ae4172f4d Keep thrown arrays' identity and inspect thrown values in test failures
A thrown array reached its catch binding as a type-erased native array:
`console.log(e)` printed `{}` where Node prints the array, `String(e)`
gave "[object Object]", `Array.isArray(e)` was false and a typed view
such as `(e as number[]).length` threw a TypeError. Thrown records
already crossed the catch boundary as checked-dynamic values that keep
their identity; arrays, and unions over records and arrays, now take the
same conversion, so the catch binding is the thrown object.

node:test printed a failing test's thrown non-Error value with String():
`throw { code: 42 }` reported `[object Object]` where Node prints the
value as util.inspect renders it. The failing-tests section now inspects
thrown numbers, booleans and checked-dynamic values (records, arrays,
null and undefined); a thrown string is quoted as before. A thrown class
instance that is not an Error still prints `[object Object]` (the
limitations page says so).

Adds corpus 6341, extends the thrown-values node:test fixture, and
records 6341's cost.
2026-10-19 21:06:20 -05:00
Malte UblandClaude Opus 5.5 20ab1c0531 Merge main into opt/bc-node-surface
Resolve the conflicts with main's process.features value, the WebCrypto dynamic types, the NODE_COMPAT_VERSION import and the symbol.declarations API. Renumber this branch's corpus programs to 6420-6429, write the new limitations partial one sentence per line, and regenerate the compatibility outputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:02:41 -07:00
Chris Tate 2119198c55 Merge main 2026-10-19 20:53:44 -05:00
Chris Tate e8ba8e404a Merge main 2026-10-19 19:56:12 -05:00
Chris Tate adb879f678 Compile the real jose root entry: string durations, chained builders and navigator (#1407) 2026-10-19 19:45:13 -05:00
Chris Tate d438cf685f Merge main 2026-10-19 19:43:23 -05:00
Malte UblandClaude Opus 5.5 d9d3dc2517 Keep __dirname and __filename in TypeScript files that load as ES modules
A .ts or .tsx file with import syntax is an ES module under Node's strip-types rule, which has no __dirname or __filename. TypeScript projects are commonly shipped as CommonJS bundles where both exist, and @types/node declares them for every file. scriptc now compiles them in such files to the module's source path, the same compile-time constants as in CommonJS and the same strings as import.meta.dirname and import.meta.filename. JavaScript ES modules keep the fence. The limitations page documents the difference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:46:46 -07:00
Chris Tate 53af3dc930 Compare strings in UTF-16 code-unit order
Relational string operators (`<`, `<=`, `>`, `>=`) compared the UTF-8
bytes, which is code-point order. Node compares UTF-16 code units, so the
results differed for characters above U+FFFF against U+E000..U+FFFF and
for lone surrogates against surrogate pairs. The default `sort()`
comparator, `util.inspect` with `sorted: true` and dynamic comparisons
already used UTF-16 order; the default comparator of a dynamic array's
`sort()` did not.

Typed comparisons and the dynamic array sort now use `scr_str_cmp_u16`,
and the IR's `strCmp` node no longer has a `utf16` flag. The two orders
agree unless both of the first differing bytes are at least 0xED, so:

- the inline wrapper decides differing first words, or last words of
  equal lengths up to 16 bytes, in byte order when neither word has a
  byte of at least 0xED (a SWAR test). Other differing words, and the
  first bytes of shorter strings, decide by their first differing byte:
  in byte order when one of the two is below 0xED, and by plane when both
  are lead bytes from 0xEE to 0xF4 (a supplementary character orders
  below U+E000..U+FFFF);
- otherwise a proven-ASCII string on either side compares with `memcmp`
  (`scr_str_cmp`), and other pairs call `scr_str_cmp_u16_bytes`. It finds
  the first differing byte with words up to byte 32, then with 32-byte
  SSE2 or NEON blocks where the target has them. The call is marked as
  only reading its arguments, so repeated comparisons of one pair merge.
- `scr_str_cmp_u16` itself takes the same `memcmp` path for proven-ASCII
  strings.

The native compiler sorts record fields with `<` and checks the order
with `sort()`, so it reported SC9001 for 5888-inspect-sorted.ts, whose
object has the keys "\u{1F600}" and "\uffff". 5888 runs in the
native-frontend self-host check. Adds corpus 6023.
2026-10-19 18:10:54 -05:00
Chris Tate 305832a12d Merge main 2026-10-19 17:32:14 -05:00
Chris Tate 736085fbf1 Merge main 2026-10-19 17:19:52 -05:00
Chris Tate a31b1fe23e Merge main 2026-10-19 17:17:48 -05:00
Chris Tate 0df30d4a56 Choose record key order from the orders values are built in (#1398) 2026-10-19 17:15:29 -05:00
Chris Tate 37f54ff157 Merge main 2026-10-19 17:14:32 -05:00
Chris Tate c82b6d8188 Merge main 2026-10-19 17:06:58 -05:00
Chris Tate 0e02fc28ca Accept mock.fn() without an original
node:test changes, matching Node 24.22:

- mock.fn() and t.mock.fn() without an original return undefined and
  record the arguments of each call, whatever the callers pass. The
  original and the initial implementation are a function that returns
  undefined, so restore() brings that function back.
- mock.fn<F>() with an explicit function type records F's parameters
  when F's return type is void or includes undefined. A return type
  without undefined, such as mock.fn<() => number>(), throws SC2020.

mock.fn()'s default type, (...args: any[]) => undefined, maps to the
checked-dynamic rest ABI used for program-declared (...args: unknown[])
functions: the calls log records the argument array, and the mock's
wrapper spreads it into the implementation.

A function whose fixed parameters are all unknown or any, or that has
none, now converts to a checked-dynamic rest signature without fixed
parameters, such as (...args: unknown[]) => void: each call passes the
arguments in order, undefined for the ones the caller left out. This is
how () => {} reaches mockImplementation and mockImplementationOnce, and
it applies to any such slot in TypeScript programs, which threw SC1090
before.

The mock-fn-default fixture and corpus program 5816 compare with Node,
plain and with SCRIPTC_SAN=1.
2026-10-19 16:59:05 -05:00
Chris Tate 65034c6d14 Merge main 2026-10-19 16:53:40 -05:00
Chris Tate cf87b359e7 Merge main 2026-10-19 16:50:17 -05:00
Malte UblandClaude Opus 5.5 4e0f71a7e5 Document the new package selection; record the 2563 order baseline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:21:57 -07:00
Malte UblandClaude Opus 5.5 cf198952e2 Compile the real jose root entry: string durations, chained builders, navigator
jose 6.2.12 now compiles natively through its root entry (`import { jwtVerify,
errors } from "jose"`), from TypeScript and JavaScript, with chained SignJWT
builders and string durations:

- A discarded `a && b;` / `a || b;` statement whose value form fails on its own
  result type (jose's secs(): `(!m || m[4] && m[1]) && invalid()`) lowers as
  `if (a) b;` / `if (!a) b;`. The value form still lowers first, so statements
  that compiled before are unchanged.
- In JavaScript, a number-keyed read on a `T[] | null | undefined` value outside
  an optional chain (`m[2]` after a statement ruled `m` non-null) reads the
  array arm like any element read: a group that did not participate is
  undefined instead of a trap.
- A JavaScript fluent method whose returns all end in `this` (including comma
  sequences) narrows a used call result back to the receiver's subclass. A
  receiver lowered at an ancestor of the class a call needs is bridged with a
  verified downcast instead of an upcast the validator rejected (the
  "upcast JWTClaimsBuilder does not extend SignJWT" internal error).
- `typeof navigator` is "object" and `navigator.userAgent` is "Node.js/24", as
  in Node 24, for the free name in JavaScript and under @types/node. Both fold
  to literals; other members refuse by name.
- TypeScript object destructuring of an `any` result of a JavaScript function
  (jose's jwtVerify) reads its members like the member reads that already
  compiled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:30:48 -07:00