- `C.name` on a class of an import cycle reads the name from the class
object, so the reference gets the class's initialization check: an
early read throws Node's ReferenceError instead of returning the name.
Other classes keep the compile-time name.
- The static field storage of a flag without a class is a shared empty
set, so the self-hosted frontend lowers cycle-initialization.ts.
Only pipe and unpipe destinations now take the constructor-function
stream probe (stream.dynNativePipe). Member calls on a checked-dynamic
stream slot use the boxed-native probe again, so a constructor-function
stream's own pause(), resume() and on() still run
(5820-node-stream-dyn-constructor-overrides).
6471 started its second file read on the first stream's 'end', so that
stream's 'close' could land after the second stream's data. The second
read now starts on 'close'.
- bind/call/apply on builtin function values held as checked-dynamic
functions (String.fromCharCode, JSON.stringify and the forms below).
- Function values for Buffer.byteLength (Node's runtime encoding ladder),
Buffer.compare, Buffer.alloc (size check, then fill), JSON.parse,
fs.realpathSync and fs.realpathSync.native (Node's options handling).
- A JavaScript string method read as a value answers String.prototype's
function (smol-toml's `!!(''.isWellFormed)` probe).
The runtime entries live in a new unit, scr_builtin_values.c, linked only
by programs that read these values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`const { [name]: v } = source` was refused unless the key folded to one
property name. The key now evaluates once, in element order, and the
element reads:
- the live property of a checked-dynamic value or a JavaScript record
(its checked view), undefined when absent, so a default applies;
- the entry of a pure index-signature record, undefined when absent;
- the declared field a literal-union key names, when every name the key
allows is a declared field stored with one type.
Nested patterns and parameters share the path (`const { signals: {
[name]: n } } = os.constants`). Rest elements beside runtime keys,
class-instance and string sources keep their refusals.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`export default { parse, stringify, DateType, ParseError }` failed at
module initialization: the literal's width check against the default
binding's type found class members missing.
- A class name in a shorthand property (`{ ParseError }`) now evaluates
to the class object, exactly like the long form `{ ParseError:
ParseError }`; it had no lowering, so the JavaScript literal dropped
the member.
- A class the program could not lower (one extending Date) is still
dropped from the literal, its diagnostic deferred to the reads that
reach it. The default binding of a JavaScript object literal now
registers its type without such members, so the module initializes
and the named exports work.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `%dyn.class.call` dispatch answers a promise for an untyped async method whose signature keeps the promise's payload. An async generator method also has the async flag, but its signature already returns the generator object, so the call now keeps that type. Before, a call from untyped code (`schema.issues(values)` over an `async *issues` method) failed IR validation with "type promise != return generator".
- thunkAsyncSlot keeps an async generator method's slot type the same way.
- Corpus 6419 adds an async generator method that the package's own untyped helper iterates with `next()`.
- A JavaScript binding written with a value whose representation differs
from its initializer's (buffer-crc32's Int32Array table) stores a
checked-dynamic value.
- A top-level JavaScript function declaration its module reassigns
(yazl's self-redefining bufferFrom) lives in a mutable module global,
assigned when the module starts; reads, calls and writes use it.
- Buffer.from with a non-literal encoding follows Node's fromString rules
at run time, and copies arrays crossing by reference; array and byte
sources ignore the extra argument.
- `new Buffer(...)` in dependency sources, where Node never warns, is
Node's construction.
- Fixed-width read/write methods on Buffers held as checked-dynamic
values dispatch through a separate runtime unit (scr_bytes_dyn.c),
linked only by programs that call them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A method whose result is either a status record or a promise of one
(a parse step that hands off to a static async merge helper in async
mode) has a union result such as `Promise<A | B> | A | B`. The dynamic
conversion accepted promise arms in a union only when every other arm
was JSON-safe, so records holding dynamic, Map or Set members were
rejected even though the composite converter boxes them. Such methods
had no checked-dynamic form: no prototype data slot, and reads through
untyped code answered undefined ("this._parse is not a function" in a
validation library's object and record schemas).
The union conversion now also accepts composite-boxable arms; the
union converter already boxes each arm through its own converter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A checked cast such as `JSON.parse(text) as Partial<T>` makes every member
an `X | undefined` union. When a present object failed X, the error named
only the union's own path ("expected object | undefined at $.order, got
object"), not the missing or mistyped member inside it.
When no arm of a union matches and the value is the one container kind
exactly one arm can take (an object for the union's only record arm, an
array for its only array arm, beside unit and scalar arms), the failure
block now runs that arm's builder, which throws at the deepest failing
path with its reason ("expected string at $.order.owner.id, got
number"). Other values keep the union's message.
Only the failure block of union dynCheck builders changes; the matching
path is untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A function value assigned to a slot whose signature is structurally
compatible but differently shaped used to compile to an adapter that
threw "the checker's loose function compatibility admitted the
assignment, but the call has no exact lowering" when called. The adapter
now converts:
- a Promise result whose payload widens into the slot's payload
(`() => Promise<Message>` into `() => Promise<Message | undefined>`,
or records with extra, optional and nested members): the promise keeps
its identity, and its payload converts when it settles;
- a record result, and a record argument, that the other side's shape
is a structural width of: the record width conversion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
&, |, ^, <<, >> and >>> accept boolean operands, converted as ToNumber
does (true 1, false 0), as JavaScript dependencies write them
(async-sema's `isFn(pauseFn) ^ isFn(resumeFn)`). Hoisted JavaScript vars
that may also hold undefined or null convert those to 0, which is what
ToInt32 makes of NaN and 0. Number operands lower exactly as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With checked-dynamic stream slots, stream.dynNative answered only boxed
runtime streams, so a pipe into a constructor-function stream took the
JavaScript listener protocol, and src.unpipe(this) in its 'pipe' listener
had no runtime pipe to remove (6564-constructor-function-stream-late-hooks).
In programs that link the constructor-function stream unit,
stream.dynNative now answers such a stream's runtime stream, as
stream.unboxDyn does there. A stream that overrides write() or end()
still pipes through its JavaScript members.
In JavaScript, `ReleaseEmitter = EventEmitter` over `const EventEmitter =
require("events")` was refused with SC1090 ("a runtime-provided class value
as an untyped value"), and `class Channel extends require("events")` failed
on the require call as a value. Bundled npm code does both, for example a
small semaphore that aliases EventEmitter and constructs its release channel
through the alias.
- The events module binding and the EventEmitter or stream class itself
turn untyped as the runtime's untyped constructor that constructor
functions already use (scr_dyn_builtin_class.c). `new` through it builds
an emitter object with Node's listener API, and `===` against the class
compares identity.
- `x instanceof EventEmitter` on an untyped value also recognizes a boxed
compiled emitter (an instance of a program class extending EventEmitter),
which the prototype test alone answered false for. The same holds through
an untyped binding narrowed to the class.
- `class X extends require("events")` extends the runtime emitter, as
through a require binding.
- A typed array of the class built from such values (`[EventEmitter]`) is
refused with SC1090 rather than failing its class check at run time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A call that selects an overload signature of a generic function now
gives its instance the same result type as an implementation-signature
call: when the body can return an indexed read that misses, the result
includes undefined, as in JavaScript. Before, such a call failed at the
missing read instead of returning undefined.
- A dns.lookup callback whose options are known only at run time binds its parameters as checked-dynamic values. Rest parameters now pack the remaining arguments, optional and defaulted parameters stay omittable so a default applies when Node passes fewer arguments, and an async callback returns its promise instead of being refused with SC2001.
- Corpus 6453 covers an async arrow, an async function expression with a default, a default `family` with `all: true`, and a rest parameter.
Indexed reads of a non-ASCII string (charCodeAt, length) become loads
once the runtime holds its UTF-16 units, but emitted code read the
published scan receiver only where runtime state is ordinary global
storage, so worker executables called into the runtime for every read.
On ELF targets their thread-locals live in the executable's static TLS
block, so the receiver is now declared thread-local there and read with
one thread-pointer-relative load. The unit array was also limited to
receivers of 2^20 units; source texts of several megabytes, which
scanners read in full, now get one too.
Adds the unicode-lexer runtime benchmark workload: a lexer over a
2.7M-unit module whose comments and strings are in several languages.
An error rendered through its checked-dynamic node (a runtime error class,
or an error held as a subclass's `cause`) prints a non-enumerable `cause`
as `[cause]: value` and an AggregateError's `errors` as `[errors]: [...]`,
after its enumerable properties, as Node's formatError does. Before, a
subclass's cause that was an Error constructed with a `cause` option
printed without it: `{ cause: [Error: inner] }` where Node prints
`{ cause: [Error: inner] { [cause]: 'deep' } }`.
An arrow whose expression body is `s.add(n)` lowered the add as a discarded
statement, so `(n) => s.add(n)`, `(n): Set<number> => s.add(n)` and
`xs.map((n) => s.add(n))` were refused with "'void' values where 'Set<number>'
is expected". The body now counts as discarded only when the arrow's lowered
result is void and its contextual signature returns void, as in
`xs.forEach((x) => s.add(x))`.
node scripts/test-ts7.mjs fails when a corpus program has no recorded preflight/order baseline. Re-recording every baseline adds only this program's entry.
A Pick or Omit view of a program class also accepts other program
classes that have the picked members. When such a view flows into
another structural type, the classes behind that type now include those
classes, so writes to their instances keep the second type a live view
instead of a copy.
Widening an array or union of object literals gives each literal an
optional `undefined` member for every property only its siblings
declare. A record shape took its key order from the checker's member
list, where those members sit wherever the checker put them: TypeScript
7.0.2 by the literal its checker widened first, TypeScript 7.1 and
tsc-ts by the sibling that declares them. tsgo and tsc-ts then gave
webassembly-wpt-jsapi.mjs different LLVM IR. The members are never own
keys of the literal's value, so they now follow the declared members,
by name.
Pushes to main now run every lane except macos, so a merged commit no
longer queues the macOS native build, the darwin test slices and the
macOS LLVM artifact jobs. The nightly sweep and manual runs still run
every lane. Pull requests keep running the macOS lane when
scripts/ci-lanes.mjs selects it.
- The iOS runtime pack builds move from mobile_runtime_packs (linux lane)
to mobile_runtime_packs_ios (macos lane), since they run on macOS
runners. The Android pack stays in the linux lane.
- The macos_full dispatch input is replaced by macos_suite (full or
darwin, default full). Manual runs run the complete macOS suite unless
macos_suite is darwin.
- ci-lanes.mjs also selects the macos lane for paths naming kqueue, the
runtime's platform header, and every file of the darwin suite in
scripts/ci-test-plan.mjs (adding tests/harness/differential.test.ts
and the darwin runtime pack's archive test).
- The required test check counts a skipped macOS job as passing when the
macos lane is off, as it already did for pull requests.
The frontend calls tokenStartOfNode, getTextOfNode, getSourceFileOfNode and
forEachChild instead of AstNode's position and traversal methods. Node lists
answer hasTrailingComma(), and a declaration's modifiers are a modifier list
with their flags.
Symbol, Type, Signature, IndexInfo, TypePredicate, Program, TypeChecker and
Diagnostic have tsc-ts/api's members and types. Compiler code reads symbol
names with symbolName(symbol).
tsc-ts goes to 0.0.5. api-conformance.ts asserts that AstNode, AstNodeList,
SourceFile and each node interface are assignable to tsc-ts/api's interface of
the same name, and names the members where the wire format differs.
A key forwarded through functions with several call sites made the search
visit every call path. Each search now visits a local once, and a local
keeps its answer: true once a search reached a key that can be "stack"
from it, false once a search that found none passed it.
The fixture's launch() helper calls run().then, which used to be the
first statement these programs fenced at (SC1090). It compiles since
#1503, so node24-web-stream-pipe-abort and
node24-web-stream-readable-lifecycle now stop at 'new ReadableStream'
and node24-web-stream-tee at 'ReadableStream.tee', which have no
lowering (SC2020).
A top-level var statement that mixes require() declarators with other
bindings, the shape bundlers emit (`var EventEmitter = require("events"),
Base = class extends EventEmitter {}`), registered a module global for each
require declarator, because the whole-statement skip only applied when every
declarator was a require. The statement lowering treats each require
declarator as alias plumbing and never assigns it, so a read that resolved
to that global saw null: `new EventEmitter` in a class constructor called
through a null class object and crashed with SIGSEGV. A builtin require in a
nested block (`if (c) { var fs = require("fs"); }`) had the same mismatch.
Collection now skips storage for each such declarator, with the same
registrations a whole require statement makes, so reads resolve to the
module as they do for a statement of requires only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A constructor-function stream now reads `_final` and `_flush` when its
writable side finishes, and `_transform` for each chunk, as Node does. A hook
assigned after `Writable.call(this)` or `Transform.call(this)` runs, and an
override of a PassThrough's `_transform` replaces the pass-through. Without
`_final` or `_flush` the finish continues as Node's does.
A runtime stream passed to a constructor-function stream's listeners, such
as the source of a 'pipe' event, is the view whose pipe and unpipe accept
constructor-function streams, so `src.unpipe(this)` in a 'pipe' listener
unpipes instead of throwing.
The backend can emit scr_wasm_memory_construct, scr_wasm_table_construct,
scr_wasm_global_construct, scr_wasm_construct_without_new,
scr_wasm_module_imports, scr_wasm_module_exports,
scr_wasm_module_custom_sections and scr_wasm_value_of (#1410), and
scr_stream_dyn_native, scr_stream_pipe_dyn and scr_stream_unpipe_dyn
(#1524). Their prototypes were in scr_wasm.c or nowhere, so the runtime
ABI guard failed. They are now in scr_wasm.h and scr_stream.h, and
scr_wasm.c no longer repeats the header's declarations.
The speculative-hoisting test relied on `v instanceof Num` in a
JavaScript function failing, which constructor-function instanceof now
compiles (#1512). Its hoisted function reads the prototype through
Reflect.getPrototypeOf instead, which still fences.