Commit Graph
6750 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 e4f72c65f5 Merge opt/native-undici (main 783870dec) into opt/native-undici-followups
Replaces the single-parent "Merge main" squash (6ab9a5423) with a two-parent merge of the same tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:50:23 -07:00
Malte UblandClaude Opus 5.5 7cc8179a22 Close a bridged child stdin on destroy and keep stream listener typing untyped
The fallback NodeJS stream interfaces declare Node's EventEmitter listener
signature for events beyond data/end, so checked JavaScript keeps its
contextual typing for process stream listeners. The child stdin fence
fixture becomes a differential test of the forms that now compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:31:27 -07:00
Chris Tate e1c27176a6 Throw checked-dynamic primitives as primitives so catch typeof tests see them (#1510) 2026-10-20 04:19:32 -05:00
Malte UblandClaude Opus 5.5 fa2dad9051 Merge opt/variadic-generic-wrappers into opt/override-variance-error-expando
Brings in #1433's review follow-up (now merged into #1413). In `key in
err`, own `stack` and Object.prototype keys answer true, keys built-in
errors keep outside their class fields stay fenced, and every other key
keeps this branch's answer: a declaring subclass or a runtime-set own
property.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:14:27 -07:00
Malte UblandClaude Opus 5.5 d447daf73b Merge main into opt/variadic-generic-wrappers
Brings main in through the already-resolved stack merge (f4c6441ce: main
merged into #1413 and #1433). Main split lower-exprs.ts into
expressions/*: the review follow-up's Error 'in' keys (own 'stack',
Object.prototype keys, BUILTIN_ERROR_SLOT_KEYS gating the declared-member
fold) move to expressions/relational.ts, and the perfLoop feature keeps
main's rule table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:13:26 -07:00
Malte UblandClaude Opus 5.5 d968975f34 Build dict-spreading literals with other members as checked-dynamic objects
An object literal that spreads an index-signature record but has
members outside the scalar join (an array of labels), an unknown-valued
spread (Record<string, unknown>), or a member a later spread may
overwrite with another type now maps to the checked-dynamic value: it is
built like a JavaScript object literal, so spreads copy their own keys
in order and later keyed writes land on the same object.

'c && { k: v }' spreads contribute k exactly when c is truthy even where
the checker's union of the two does not map (an AbortSignal member), and
a conditional spread onto an 'unknown' field leaves the checked-dynamic
undefined in its empty arm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:01:48 -07:00
Chris Tate 1ccda45f7d Decode an AST list's position only when it is read
Every read of an AST list field built an AstNodeList whose pos and end
were decoded from the wire first, though most readers only use `nodes`.
The list now keeps the wire file and the list's index, and pos and end
decode when they are read. The allocation per read stays the same size,
and a decoded file still holds no list objects.
2026-10-20 03:59:25 -05:00
Chris Tate e1e45f12b9 Give converted object literals their accessors
An object literal with get/set accessors converted to `any` or `unknown`
listed its reserved closure slots (`%get:x`) as data keys, so a dynamic
read of the accessor answered undefined without calling the getter, and
Object.keys and JSON.stringify listed the slot instead of the property.

The checked-dynamic copy now defines each accessor as one enumerable,
configurable property whose getter and setter are the slot closures, in
declaration order: the record shape records its accessor key order
(`accessorOrder`). Live views of such records build that copy, and
checking a copy back into the record reads the closures from the
accessor's getter and setter. Reads, writes, Object.keys,
Object.entries, JSON.stringify and property descriptors answer as in
Node.
2026-10-20 03:55:43 -05:00
Chris Tate 15ae146267 Keep builtin aliases off local bindings and unloaded packages, and validate Host values
- A builtin alias whose replacement starts with a global the file
  declares (`process`, `Array`) keeps the package's own module: the
  replacement would read the local binding.
- A polyfill package the project marks unloaded has no builtin alias, so
  its require throws MODULE_NOT_FOUND.
- The undici shim rejects a Host header value with characters outside
  the header value set (CR, LF and other controls) with `invalid <key>
  header`, as undici 5.19.1 and later and 7.x do, before the client
  writes it into the request head.
- Format lowerer.ts and type-mapper.ts.
2026-10-20 03:51:29 -05:00
Chris Tate 1e422cb7cb Merge opt/npm-esm-wrappers 2026-10-20 03:49:24 -05: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 d118fcbbbd Format the bundled-shim changes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:38:01 -07:00
Malte UblandClaude Opus 5.5 08fb421628 Compile NodeJS stream interfaces as general streams when other streams flow into them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:27:17 -07:00
Malte UblandClaude Opus 5.5 004034e4e9 Monomorphize generic overload calls and call generic methods through Pick views
- A call that resolves to an overload signature of a generic function or
  method with an implementation (`load<T>(k, map: Mapper<T>): Promise<T>;
  load(k): Promise<Reply>; load<T>(k, map?) {...}`) monomorphizes the
  implementation under the bindings the overload's resolved signature
  proves; a type parameter the overload never mentions is unknown, and
  parameters the overload omits must be optional in the implementation.
  It was refused.
- `view.m(...)` where `view` is a `Pick`/`Omit` view of a program class
  and `m` is a generic (or generically overloaded) method extracts the
  instance from the view's live receiver and calls the method statically,
  instead of a runtime refusal reading the method through the untyped
  view.
- Binding inference pairs the arms of `T | Promise<T>` with an
  instantiated `R | Promise<R>`, and `unknown | Promise<unknown>` maps to
  the checked-dynamic value the checker reduces it to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:26:57 -07:00
Malte Ubl 547166d2bd Merge branch 'opt/npm-polyfill-shims' into opt/npm-fs-extra-shim
# Conflicts:
#	packages/compiler/src/frontend/npm-static-bundled-esm.test.ts
2026-10-20 01:26:53 -07:00
Malte UblandClaude Opus 5.5 14f1fe97f0 Keep a hoisted class's next piece when the initializer ends with it
In minified output the class closes right where the initializer body
does (`…Counter=class{…}}})`), so the empty insertion that opens the next
run-once piece shared its position with the body's closing edit and was
dropped: the initializer then called a piece that did not exist. The
opener now rides on the class's own closing brace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:21:28 -07:00
Malte UblandClaude Opus 5.5 bebc0946bc Point bundled copies of shimmed packages at the shim; guard every shim
A bundle that embeds fs-extra (build-utils does) compiled the package's
own JavaScript, whose computed export table and graceful-fs internals the
static model cannot follow. A __commonJS chunk whose key names a shimmed
package's main file at a supported version now requires the shim
instead, through a forwarder declaration beside the bundle: the checker
follows it to the shim (a package's JavaScript cannot load TypeScript
sources directly), and module resolution answers the shim for its path.
The package's other chunks are no longer reached. Calls through such a
require of a TypeScript module lower when the export never reads `this`
(require() answers the module's namespace).

package-shim-guard.test.ts compiles every registered shim module under
both type worlds and fails on any lowering diagnostic or fence inside the
shims, reached or not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:21:06 -07:00
Malte UblandClaude Opus 5.5 29b7f8220d Keep a hoisted class's next piece when the initializer ends with it
In minified output the class closes right where the initializer body
does (`…Counter=class{…}}})`), so the empty insertion that opens the next
run-once piece shared its position with the body's closing edit and was
dropped: the initializer then called a piece that did not exist. The
opener now rides on the class's own closing brace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:20:56 -07:00
Chris Tate b8a955b23f Compile function export objects; name conditional export objects (#1434) 2026-10-20 03:16:27 -05:00
Chris Tate 603c6720f9 Keep function export rewrites from capturing names or keys
The pass respells `exports.x` and `module.exports.x` through the
exported function's name. A parameter, local, nested function or catch
binding of that name between a respelled read and the top level would
capture it, so the pass now applies only when the file binds the name
once, at its top-level declaration.

`{ exports }` after the rebinding respelled to `{ SemVer }`, which
renames the property; the shorthand now becomes `{ exports: SemVer }`.
2026-10-20 03:13:40 -05:00
Chris Tate 9d10ae169e Compile lazy barrels, star re-exports and npm aliases in bundled packages (#1427) 2026-10-20 03:12:25 -05:00
Chris Tate eb729399ad Merge branch 'opt/npm-lazy-loaders' into opt/npm-cjs-exports 2026-10-20 03:09:23 -05:00
Chris Tate 66d2010388 Remove lazy loaders together and keep their receiver rule
Lazy loaders that share a var statement each removed their declarators
on their own, and declarators of one statement removed separately leave
overlapping edits, of which applyEdits keeps the first. A later loader
in the statement (one using __toESM, say) stayed behind and the interop
pass refused the package. Every removed declarator of the pass (the
interop requires, the loaders and the unused export tables) now goes
into one set, removed once.

A call through a loader (`load().fn()`) respelled the callee as the
export's live read, which drops the namespace receiver. Loader calls now
take the rule namespace calls already use: the callee must be a
top-level function that never reads `this`; other calls keep the package
refused.
2026-10-20 03:08:09 -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 c70d9d7e28 Refuse returns a generic callback's inferred result would drop members of
A callback passed to a generic call without a return annotation can
return values the checker unifies into a narrower result type ('return
items' beside 'return items.map((i) => ({ ...i, extra }))' infers
Item[]). Converting the extended objects into that type copied them
and dropped the added members, which Node keeps and a later wider view
reads back. Such a return is now refused by name, pointing at the
annotation that keeps the members.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:53:19 -07:00
Chris Tate ea611bec02 Convert object literals with accessors to unknown again
#1490 also took records with accessor slots out of the dyn-convertible
domain, which refused passing such a literal where `unknown` is
expected. Native APIs read these values through their accessor slots:
fileURLToPath, fileURLToPathBuffer and pathToFileURL call the `windows`
getter of an options object, so tests/corpus/node24-file-url-options.ts
stopped compiling.

Records with accessor slots convert to checked-dynamic values again.
JSON.stringify of their typed values stays refused, since the typed
serializer cannot invoke the getter.
2026-10-20 02:52:38 -05:00
Malte UblandClaude Opus 5.5 b74224f31c Throw checked-dynamic primitives as primitives so catch typeof tests see them
A value thrown from `unknown` storage (`throw values[i]`) that holds a
number, string or boolean now throws as that primitive, like a statically
typed `throw 5`. Before, it threw as a checked-dynamic reference, so
`typeof e === "number"` (and "string"/"boolean") on the catch
binding answered false and the narrowed reads were unreachable. A
primitive has no identity to keep, and reading the binding as `unknown`
converts it back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:50:32 -07:00
Chris Tate eda2c3104e Merge main 2026-10-20 02:49:54 -05:00
Malte UblandClaude Opus 5.5 a64d7f8ce1 Run JavaScript promise executors passed by name, and read catch (e: any) bindings dynamically
- `new Promise(run)` where `run` is a JavaScript function value whose
  parameters are untyped (the retry-helper shape `function run(resolve,
  reject) {...}; return new Promise(run)`) gets an adapter executor: it
  receives the native resolve/reject pair and calls the original with
  both boxed as checked-dynamic function values. This was a deferred
  runtime refusal inside such packages.
- A catch binding annotated `any` reads the caught value through its
  checked-dynamic face, as an unannotated `unknown` one does: member
  reads, expando writes, `delete` and `in` act on the thrown object, and
  a rethrow keeps its identity. It was refused (SC1063).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:49:51 -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 35e566855f Accept mock.fn() without an original (#1417) 2026-10-20 02:42:59 -05:00
Chris Tate c8b22e3435 Merge main 2026-10-20 02:40:17 -05:00
Malte Ubl 7e6da3a488 Merge remote-tracking branch 'origin/opt/npm-polyfill-shims' into opt/npm-fs-extra-shim 2026-10-20 00:40:16 -07:00
Malte UblandClaude Opus 5.5 37551e41b4 Compile any crossings: destructuring, namespace patterns, any parameters and async callbacks
- Destructuring an `any` value in TypeScript (a function typed `any`, an
  awaited `any`, `JSON.parse`, an `any` table read) now goes through the
  checked-dynamic value, as JavaScript sources and #1407's JS-declared
  results already did. Object sources get V8's RequireObjectCoercible
  TypeError for the spellings the assignment form reproduces.
- A method call or member read on a checker-`any` receiver whose value is
  a fresh typed call result (`out.split("\n").map(...)`, `.pop()`,
  `.length`) boxes that temporary and dispatches on the runtime kind,
  only after every other lowering declined.
- `const { a, b: c } = ns` and `= await import("./m")` over a program
  module bind each export the way `ns.a` reads it.
- A defaulted parameter of a program-source function, arrow or function
  expression that the checker types `any` (a default under an
  `(...args: any[]) => any` wrapper slot, where the contextual `any`
  wins) is a dyn ABI slot in the callable's type.
- An async lambda whose signature answered a bare dyn result (a library
  slot `A | Promise<A>` with A = any) produces Promise<unknown>.
- `761-any-objects` compiles and moves back to the corpus; the
  retired-engine 2083/2104 snapshots shrink to their remaining fences.
- JavaScript `Promise.resolve(value)` (in a JavaScript file) over a dyn value holding a promise
  answers that promise instead of wrapping it, so a TypeScript async
  callback's result is adopted by JavaScript retry helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:30:58 -07:00
Malte UblandClaude Opus 5.5 47ecfdfa8c Merge opt/trace-reachable-blockers (with main) into opt/override-variance-error-expando
Brings main into the stack (thrown-value fixes, the lower-exprs split,
the checker helper removal). This branch's lower-exprs.ts hunks move to
expressions/relational.ts (`in` on errors), expressions/property-access.ts
(stdlib members of a structurally narrowed catch binding) and
expressions/narrowing.ts (assertions on an `any` catch binding); the
`this` blame, the `any` catch-binding read and the setPrototypeOf
feature test stay in lower-exprs.ts. The Error member read keeps both
this branch's intersection members and #1433's subclass-owned `code`.
Corpus 6407 and 6408 renumber to 6412 and 6413 (main has its own 6407
and 6408), and 6414 adds the thrown-value round trip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:29:46 -07:00
Chris Tate 95837b91c5 Check static members a cycle class inherits from its base classes 2026-10-20 02:26:01 -05:00
Malte UblandClaude Opus 5.5 2553818b0c Compile the round-2 index-signature and union-of-records shapes
- An object literal that spreads an index-signature record (the
  metric-tags idiom '{ ...tags, bucket, status }') maps to the pure
  index-signature record joining the spread slots and member types: the
  checker drops the signature, but the object holds every spread key.
  Declared members read and write as keys of it.
- A conditional spread into an index-signature record adds no key when
  it does not apply.
- '{ ...unknownValue }' into an index-signature record copies the
  value's own keys through the checked extraction; null, undefined,
  numbers and booleans spread nothing.
- 'return Promise.reject(e)' in an async function is typed by the
  function's own result.
- A union of record layouts flowing into one record slot converts each
  arm; fields the checker types 'undefined' alone lift into
  undefined-armed slots.
- '(x as { m: T }).m' on a union of records reads the arms that declare
  m and answers undefined for the others.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:19:07 -07: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 f31dbf80f1 Compile event-loop metrics, predicate-narrowed catch bindings and other wrapper-reachable gaps (#1433) 2026-10-20 02:16:25 -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
Malte UblandClaude Opus 5.5 f4c6441cee Merge main (through opt/variadic-generic-wrappers) into opt/trace-reachable-blockers
Main replaced the runtime-feature scan with a rule table, the executable
unit list with a unit table and the module emission with emit-module.ts:
perfLoop becomes a rule (perf.idleTime, perf.loop*, perf.eld*), a unit
(scr_perf_loop.c) and emit-module's loop-start mark (readsLoopMetrics is
now public). The feature lists take both perfLoop and main's webcrypto.
This branch's lower-exprs.ts hunks move to expressions/property-access.ts
(histogram readings), expressions/binary.ts (`??` with a function
default) and expressions/relational.ts (`in` over class members); the
caughtRead fallback stays in lower-exprs.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:10:52 -07:00
Malte UblandClaude Opus 5.5 77437eae3c Merge main into opt/variadic-generic-wrappers
Main split lower-exprs.ts into expressions/*; this branch's hunks move to
expressions/identifiers.ts (typed 'arguments') and
expressions/property-access.ts (static fn.name, forwarded tuple length,
'this'-parameter arity). Main removed the checker's declarationsOf,
valueDeclarationOf and signatureDeclaration helpers; the branch's uses
read symbol.declarations, symbol.valueDeclaration and
signature.declaration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:10:27 -07:00
Chris Tate b6ef49d39d Promisify callback-last functions that return a value
The promisified executor typed its call of the function as void, so a
function with a non-void return type failed to compile with SC9001. The
call now has the function's return type, and its value is discarded, as
in Node.
2026-10-20 02:09:48 -05:00
Chris Tate 069545a126 Address review: monitorEventLoopDelay spellings and validation, Error 'in' keys, event-loop metrics in libraries
- monitorEventLoopDelay is lowered from the builtin call dispatch, by module and member, so aliased named imports, namespace members and destructured requires all reach it. Shorthand and possibly undefined resolutions take Node's default.
- The runtime validates the resolution as Node does (an integer of at least 1) and throws a catchable RangeError with ERR_OUT_OF_RANGE.
- `'stack' in err` and Object.prototype's keys are true for every Error. The declared-member fold applies only to keys some program class declares and that built-in errors don't carry in runtime slots; other keys stay fenced.
- Library mode's async_free gate refuses the event-loop metrics, which library links leave out. They join the perf_hooks fence family.
2026-10-20 02:07:46 -05:00
Chris Tate 9febfe8ca3 Place a quoted name field among an error's fields
The position of an Error subclass's own `name` among its fields was found
from identifier keys only, so a quoted `"name"` field, other quoted
fields, and `this["name"] = ...` in a constructor were not seen, and
`name` was listed last. Field keys and constructor assignments now read
any statically spelled key: an identifier, a quoted string, a number in
its canonical spelling, or a computed string literal.
2026-10-20 02:07:18 -05:00
Chris Tate c2ae71a6fa Bound full-batch poller drains to 48 per poll phase
A full batch of 64 events polled again without returning to the event
loop. When synchronous socket callbacks kept 64 events ready on every
drain, timers and setImmediate callbacks never ran. The poll phase now
drains at most 48 full batches, the count libuv's uv__io_poll uses, and
the budget carries across the reopen that lets microtasks run, so the
remaining events wait for the next loop iteration.
2026-10-20 02:07:00 -05:00
Chris Tate bdf1bb9734 Pass rest-parameter functions into nullable and optional fixed-parameter slots
Structural conversion lifts a function into a union's function arm only
when cleanFuncAdaptable accepts the pair, and it refused every rest
signature. A typed-rest function assigned to `((m: string) => void) |
null`, an optional parameter of that type, or a `| undefined` binding
therefore failed with SC2003. cleanFuncAdaptable now accepts the pairs
the rest packing adapter serves, so these slots use the same adapter as
a plain fixed-parameter slot.

6445 covers the nullable, optional and undefined slots, and 6445 and
6446 get their corpus costs.
2026-10-20 02:06:21 -05:00
Chris Tate f6f4ad1b00 Give errors held as any their own stack accessor (#1450) 2026-10-20 02:05:17 -05:00
Chris Tate 45431aec32 Choose comptime union arms by discriminant and refuse recursive records
A comptime record value whose union has a discriminant takes the arm its
discriminant field names, so a value that fits two arms with overlapping
shapes gets that arm's tag and layout instead of the first arm it fits.
comptimeBakeable reports a record type reachable from its own fields as
not bakeable instead of recursing without end, so such a comptime result
reports SC1090, and the message names unions and that limit.
2026-10-20 02:01:48 -05:00