Commit Graph
17 Commits
Author SHA1 Message Date
Chris Tate e646764023 Merge main 2026-10-23 19:31:34 -05:00
Malte UblandClaude Opus 5.5 14ba361985 Call own accessors over an Error's name and message from typed code
zod 4 defines its error message with Object.defineProperty(inst, "message",
{ get, set }). The typed-ref commit of an Error subclass read the accessor
as a data value and threw "expected string". The commit and the slot sync
now leave the slot alone and mark the property absent; typed reads and
writes of name and message call the accessor when the program reflects on
an untyped or Error-shaped target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 23:34:56 -07:00
Chris Tate 84cf0ec19f Inspect dynamic classes, functions and %o arguments as Node does
- The inspector throws the 'not supported yet' Error for checked-dynamic
  values it does not render (a promise, a URL, a RegExp reached through
  `unknown`). Its calls were not marked as throwing, so
  `console.log("x", p)` printed "x " and the program went on; records and
  arrays with such a field printed `{ a:  }` and `[  ]`. insp.dyn and
  insp.dynS are now may-throw library calls, so the Error propagates from
  the call that inspects the value, before anything is written.
- A class reached through `unknown` prints `[class C]`,
  `[class C extends B]` or `[class (anonymous)]` instead of
  `[Function: C]`. Functions and classes reached as dynamic values print
  their enumerable own properties (`[Function: f] { x: 1 }`,
  `[class S extends A] { count: 3 }`), cycles through them as
  `[Circular *N]`, and `[Function]` past the depth limit. Class objects
  carry their base class's name. A compiled class's static fields are
  module globals that the class object does not carry, so inspecting a
  class that declares static fields through `unknown` throws the
  'not supported yet' Error.
- util.format's %o renders a checked-dynamic argument with showHidden: an
  array's `[length]` entry and an object's non-enumerable own properties,
  string- and symbol-keyed, in brackets. An array with more than 100
  output entries keeps the last one out of the grid, as Node's
  groupArrayElements does. %o of a function, an Error, a typed array or a
  native handle other than Map, Set and Date throws the
  'not supported yet' Error.

ScrClassObj has two new fields, so RUNTIME_ABI_VERSION is now 27.
2026-10-22 10:54:02 -05:00
Chris Tate da0f6b642b Merge main 2026-10-20 09:52:32 -05:00
Chris Tate fc9a8650af Keep an Error's code slot in step with dynamic writes and own properties
Writes and deletes of `code` through an error's checked-dynamic node (a
`catch (e: any)` binding, a reflective write) changed only the node, so
typed `err.code` reads kept the old value. The slot sync that already
covered name, message, stack and cause now covers code: a string takes
the slot, any other value or a delete clears it. A write, delete or
Object.assign through an Error subclass's typed view now syncs the
slots the subclass inherits from Error after the class commit, which
writes only declared fields.

`'code' in err` read the code slot, so an own `code` set to undefined
answered false. It now asks the runtime: the slot holds a string, or the
error has an own `code` property on its node.
2026-10-20 03:05:32 -05:00
Malte UblandClaude Opus 5.5 7d1d440b7b Adapt method overrides across ABI differences, write Error codes, and close wrapper-reachable gaps
Overrides whose parameters widen and whose results convert into the
inherited ones keep the inherited slot, which converts around the
override's own body; Error values take code writes, `in` tests and
Object.assign; and a few gaps behind generic wrappers close:

- Method overrides: each inherited argument converts into the
  override's parameter (equal, wider, or a checked extraction from an
  `any` slot) and the override's result converts into the inherited one
  (narrower, a convertible union, a widening Promise payload, anything
  convertible over an `any` base). The slot %C.m keeps the inherited
  ABI and forwards to %C.m$own; TypeScript callers typed with the
  subclass call $own directly when nothing overrides below. Async
  overrides of sync and async bases, and JavaScript overrides of
  builtin or TypeScript bases (zod's ZodError.toString) are included;
  a toString slot converts its result with ToString.
- `err.code = value` on Error values in TypeScript: strings set the
  runtime code slot (and the error's checked-dynamic node, if any); other
  values clear it and keep the dynamic own property.
- `'key' in err` with any receiver: declared members answer true;
  otherwise a class test over the declaring subclasses, a runtime lookup
  of the error's own properties, or an Object.prototype member.
- `Object.assign(err, ...sources)` onto an Error target copies onto the
  error, re-reads name, message, stack, cause and code into the typed
  slots, and returns the error.
- `Error & { ... }` intersections whose extra members have checkable
  types are Error values; those members read and write the error's own
  properties, each read checked at its declared type.
- Catch bindings typed `any` read as checked-dynamic values (member
  reads and writes, assertions); a multi-statement type guard with an
  `unknown` parameter takes the caught value as an ordinary argument;
  Error members of a binding narrowed to an Error-extending interface
  read through the same value.
- Lambda parameters typed `any` by their default are checked-dynamic
  slots; forwarded-only rest tuples with optional positions forward
  their slots; `value as never` from an untyped value into a typed
  parameter takes the checked extraction to the parameter's type.
- Lexical `this` stops at generic instance frames, so an instance no
  longer sees the receiver of the caller that triggered it. The fence
  for `this` captured through a plain function now points at the
  `this` keyword.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:28:43 -07:00
Chris Tate 82ee707ce5 Support request signals, destroy(err), servername, Expect events and maxHeaderSize in http (#1076) 2026-10-17 11:41:29 -05:00
Chris Tate 5c39821726 Merge main 2026-10-17 02:30:43 -05:00
Chris Tate 6d4843ea99 Dispose Disposable-typed values and expose SuppressedError's error and suppressed
`using` and `await using` now accept object literals and other values typed
Disposable, AsyncDisposable or unknown. The declaration reads the value's
[Symbol.dispose] method ([Symbol.asyncDispose] first for `await using`) and
throws Node's TypeErrors for values that are not objects and for missing or
non-callable methods; null and undefined dispose nothing. Scope exit calls the
method with the value as `this`, and `await using` awaits the result of an
async method but not of the sync fallback. A class instance reached through
such a value throws an SC2020 error, because its dynamic view has no methods.

SuppressedError is its own error class: a failing disposal over a pending
error throws one whose `error` is the disposal error and whose `suppressed` is
the pending error, both non-enumerable as in Node. `new SuppressedError(error,
suppressed, message?)` and `instanceof SuppressedError` compile, and
`extends SuppressedError` is refused.
2026-10-16 22:36:18 -05:00
Chris Tate db94b2d257 Add fs.cpSync, fs.cp and fs/promises cp
fs.cpSync, fs.cp and fs/promises cp compile with every option:
dereference, errorOnExist, filter, force, mode, preserveTimestamps,
recursive and verbatimSymlinks. They follow Node's two implementations.
cpSync follows cp-sync.js over the fs binding: copy errors are plain
Errors with a code, std::error_code failures throw an ErrnoException,
and links inside a copied directory point at their canonical targets.
fs.cp and fs/promises cp follow cp.js, whose copy errors are
SystemErrors with code, info, errno, syscall and path.

Options and paths validate in Node's order with Node's errors. filter
runs for the top-level paths and each entry in Node's order; an async
filter must return a promise that is already settled, and a pending one
throws SC2020. The callback and promise forms complete the copy before
the callback runs or the promise settles. Windows builds throw SC2020.

The copy lives in the new scr_fs_cp.c. Directory walks keep their frames
on the heap, so deep trees also copy on a fiber's stack. File contents
copy with sendfile on Linux, and COPYFILE_FICLONE uses FICLONE
where it exists. ScrError gains the SystemError context and the
ErrnoException key order, with matching fields in the compiler's Error
layout. The stored fs.cp callback uses the same implementation.

Corpus 5567-5571 cover the sync, callback and promise forms, errors,
symlinks and 1500-level trees.
2026-10-16 21:06:22 -05:00
Chris Tate dea734d63a Support request signals, destroy(err), servername, Expect events and maxHeaderSize in http
- http.request accepts `signal`: an abort, or a signal already aborted at
  construction, destroys the request with an AbortError (code ABORT_ERR,
  the signal's reason as cause). The abort listener is removed when the
  request closes, and a value that is not an AbortSignal throws
  ERR_INVALID_ARG_TYPE.
- req.destroy(err) reports err as the request's 'error'. Error listeners
  receive the Error object through scr_event_error_payload, and
  AbortError construction is shared through scr_error_abort.
- https.request accepts `servername`, sent as SNI and used for the
  certificate name check.
- server.on('checkContinue' | 'checkExpectation') take Expect requests
  instead of 'request', and the client emits 'continue' on a 100
  response.
- createServer and http.request accept maxHeaderSize, validated with
  Node's ERR_OUT_OF_RANGE messages.

Corpus 5564 and the extended 5559, 5561 and 5562 cover each API.
2026-10-15 21:43:27 -05:00
Chris Tate 0ebc146405 Merge main 2026-10-15 21:07:04 -05:00
Chris Tate feeba0a166 Add assert.AssertionError with Node's properties and constructor (#1022) 2026-10-15 20:18:29 -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 1121fe20c2 Merge main 2026-10-15 08:18:16 -05:00
Chris Tate 3cd856b61b Merge main 2026-10-15 03:59:47 -05:00
Chris Tate 4d54f6c4d8 Split scr_runtime.h into per-module runtime headers
scr_runtime.h now includes 48 module headers (scr_core.h, scr_rc.h,
scr_string.h, scr_dyn.h, scr_http.h, ...) in dependency order and holds no
declarations. Each module header holds whole sections of the former file,
includes the headers whose names it uses, and compiles on its own.

Declarations, macros, struct layouts and inline bodies are unchanged. Five
blocks move to join their module: the class-hierarchy vtable header (into
scr_error.h, after the string core), the node:timers/promises pair (into
scr_async.h), the JSON.parse entry points and the JSON output buffer (into
scr_json.h, after the dyn declarations) and the EventTarget block (after
scr_json.h). No moved block uses or declares a name that a block it moves
past uses or declares. The string methods keep their position after the
class objects in scr_string_methods.h, because their struct ScrDyn
parameters rely on the file-scope struct ScrDyn declaration there.

The runtime ABI scan reads scr_runtime.h with its module headers expanded
in include order. A runtime test compiles every module header on its own.
scripts/runtime-header-hunks.mjs retargets a diff of the single-file
header onto the module headers.
2026-10-15 02:06:23 -05:00