Commit Graph
3704 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 fa012f5da3 Name the corpus programs instead of numbering them
The numbers collided with other open changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:51:31 -07:00
Chris Tate 0b00ecfc23 Read x.constructor on EventEmitter and stream instances
`x.constructor` on a value typed as EventEmitter or a stream class
stopped at SC2020. The runtime now records each instance's Node class
name: fs streams as ReadStream and WriteStream, child process stdio as
Socket, worker stdio as ReadableWorkerStdio and WritableWorkerStdio, and
Hash/Hmac stream views as Hash and Hmac (zlib streams, the base classes
and program subclasses already did). The name is the one Node's leak
warning and argument-type errors print, so those messages now name the
same class as Node.

`x.constructor.name` reads that name (scr_emitter_class_name).
`x.constructor === C` for EventEmitter or a stream class tests the
instance's own class: C's interval, none of its direct subclasses', and
C's name, as fs, zlib, crypto, child process and worker streams share the
base classes' vtables. `x.constructor` as a value is the class object of
EventEmitter or the stream class the instance belongs to; for instances
of the other runtime classes and of program subclasses it reports SC1090
at run time.
2026-10-19 21:44:33 -05: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 4bbf31b61f Check the bench workloads' dynamic-tier symbols in CI (#1436) 2026-10-19 21:32:51 -05:00
Malte Ubl ea14ae9bae Merge remote-tracking branch 'origin/opt/native-undici' into opt/npm-polyfill-shims
# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-modules.ts
#	tests/harness/npm.test.ts
2026-10-19 19:20:10 -07:00
Chris Tate ab70d3d369 Keep thrown arrays' identity and inspect thrown values in test failures (#1432) 2026-10-19 21:12:33 -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 c17eec0261 Compile event-loop metrics, predicate-narrowed catch bindings and other wrapper-reachable gaps
Once generic wrappers compile (#1413), the code behind them is reachable.
This closes the general gaps found there:

- performance.eventLoopUtilization() and monitorEventLoopDelay()'s
  IntervalHistogram (enable, disable, reset, percentile and the min, max,
  mean, stddev, count and exceeds readings), under the fallback
  declarations and @types/node. A new runtime unit, scr_perf_loop.c, links
  only for programs that read them (the perfLoop feature): an unref'd
  sampling interval records into HdrHistogram-shaped buckets, and
  utilization measures active time as the main thread's CPU time since
  the loop started.
- Catch bindings narrowed by a user type predicate or an assertion read
  their members through the checked-dynamic value.
- `??` with a function default of a different signature takes the
  optional callback's own function type and adapts the default.
- Optional calls on interface-typed receivers dispatch like the plain
  protocol call.
- `any` parameters of function declarations and spelled function types
  are checked-dynamic slots, and a generic instantiated at `any` takes
  `unknown` there.
- `'key' in err` on Error receivers answers from the declaring subclasses.
- Object.entries over a generic record resolves
  `keyof T extends never ? unknown : T[keyof T]` through the binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:04:07 -07:00
Chris Tate bf2afee9f5 Split lower-exprs.ts into expression families (#1431) 2026-10-19 21:03:52 -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 12582757e8 Split lower-exprs.ts into expression families
lower-exprs.ts had grown to 17.1k lines, and its import block and the big
branches of lowerExprInner were edited by about half of all changes to
it, so concurrent changes conflicted there. Its declarations move, as is,
into files under frontend/lowering/expressions/: identifiers,
property-access, typeof, element-access, binary, relational, narrowing,
updates and array-literals. lowerExprInner's identifier, property-access
and typeof branches become functions of their own in those files, called
from the branch. Helpers move with their only users; helpers shared
across files stay in lower-exprs.ts, which re-exports every moved name it
exported, so other modules' imports are unchanged.

The move changes no behavior: the zero-cost gate compiles the benchmarks,
the self-hosted compiler and the corpus to identical IR, LLVM and
executables.
2026-10-19 20:59:02 -05:00
Chris Tate 2119198c55 Merge main 2026-10-19 20:53:44 -05:00
Malte UblandClaude Opus 5.5 6e67b1da09 Run the poll phase once per event-loop iteration
The net unit drained its poller every time the loop came back to the I/O
station, including the restarts that drain microtasks. I/O that became
ready during the iteration's own callbacks (a loopback connect started by
one of them) was delivered before the check phase, so setImmediate
callbacks queued earlier ran after it; Node delivers it in the next
iteration's poll phase. The poller now drains once per iteration (a full
batch keeps draining), reopening after the check phase. Back-to-back
undici requests now reuse connections like Node, so the fixtures drop
their setImmediate turn between requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:53:23 -07:00
Malte UblandClaude Opus 5.5 3701f26681 Lower Headers, Request and Response members that undici-types declares as properties
@types/node's fetch declarations (undici-types) spell the Headers methods
and the body mixin as function-typed properties (readonly append: (...) =>
void), where scriptc's own declarations use methods. The receiver call
lowering treated such a standard-library property as a stored callback:
Headers.forEach was refused at build time and append/get/set/... compiled
to a runtime 'is not a function'. Standard-library members now take the
shared receiver dispatch either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:53:23 -07:00
Chris Tate dfb0bed8c1 Write the limitations partials one sentence per line (#1369) 2026-10-19 20:39:49 -05:00
Chris Tate 912bf6e9e9 Fail the runtime build when two handle tags share a value (#1430) 2026-10-19 20:33:19 -05:00
Chris Tate 340f4d0c6c Compute with NaN-boxed jsval values in the dynamic tier (#1344) 2026-10-19 20:31:17 -05:00
Malte UblandClaude Opus 5.5 2b7c892af3 Record order baselines for the new cases and corpus program
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:29:54 -07:00
Malte UblandClaude Opus 5.5 3723e05417 Resolve bundled chunks that re-export a builtin to the builtin
A chunk whose whole body is `module.exports = require("<builtin>")`
(readable-stream's internal/streams/stream.js) is the builtin module:
its calls become that require, and no chunk module is emitted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:28:45 -07:00
Chris Tate b56d1c9ba7 Fail the runtime build when two handle tags share a value
ScrDynHandleTag listed its tags by name with explicit values, so two
changes that each took the next free value merged without a conflict and
produced two tags with one value; only dyn-handle-tags.test.ts noticed.
#1397 (CRYPTO_KEY) and #1259 (ASYNC_DISPOSABLE_STACK) both took 56.

SCR_DYNH_TAGS is now the one list of tags: X(NAME, value) rows, sorted
by name, generate the enum, a _Static_assert per tag that the value is
below SCR_DYNH_CAPACITY, and scr_dynh_tags_distinct, a never-called
switch with a case per tag. Two tags with one value are a duplicate case
value, so every runtime unit that includes scr_dyn.h fails to compile and
names both tags. The values, the names and the generated code are
unchanged. dyn-handle-tags.test.ts reads the rows.
2026-10-19 20:27:22 -05:00
Chris Tate 8d4737dfdb Merge main 2026-10-19 20:27:03 -05:00
Chris Tate 285ed51d93 Format the declaration reads changed in #1426 (#1428) 2026-10-19 20:22:34 -05:00
Malte UblandClaude Opus 5.5 5670bc182e Respell members of a function export object; name conditional exports
A CommonJS module whose export object is one of its own top-level
functions (semver 5/6's `exports = module.exports = SemVer`) reaches
its members through that function after the statement: member reads
and writes through module.exports (and, after the rebinding, every
`exports` reference) become members of the function, the expando form
both resolvers model, and the rebinding itself drops. Constructor
functions exported whole with members attached afterwards no longer
lose those members at runtime.

Replacing module.exports or rebinding exports inside a branch (an
environment-dependent export object, readable-stream 2.x) now names the
construct instead of the generic assignment fence, and a required
builtin module object is truthy in the LLVM backend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:21:52 -07:00
Chris Tate a1b3ab477b Format the declaration reads changed in #1426
oxfmt joins the .some(...) calls onto the declarations reads, so
format:check passes again.
2026-10-19 20:14:56 -05:00
Malte Ubl 8e41f7678a Merge remote-tracking branch 'origin/main' into opt/native-undici 2026-10-19 18:11:35 -07:00
Chris Tate 187886d2da Read symbol declarations directly in code added after the checker helpers were removed (#1426) 2026-10-19 20:11:00 -05:00
Malte Ubl f5f8c4821c Merge remote-tracking branch 'origin/main' into opt/native-undici
# Conflicts:
#	packages/compiler/src/frontend/lowering/builtins/timers.ts
#	packages/compiler/src/frontend/type-mapper.ts
2026-10-19 18:08:01 -07:00
Chris Tate 76dda23d8b Read symbol declarations directly in code added after the checker helpers were removed
#1399 removed CheckerFacade.declarationsOf and valueDeclarationOf. #1407 and
#1351 merged after it with calls to them, so main does not type-check. Read
symbol.declarations and symbol.valueDeclaration instead, as the rest of the
compiler does.
2026-10-19 20:07:39 -05:00
Chris Tate bb6270efc1 Fix silent divergences: constructor returns, arr && arr[0], ES5 class names, builtin function values, uninitialized let writes, module-export this, 1 ** Infinity (#1351) 2026-10-19 19:56:36 -05:00
Chris Tate e8ba8e404a Merge main 2026-10-19 19:56:12 -05:00
Chris Tate 950ec9ee85 Give prototype name stamps their own copy of the name
A construction through a plain function stamps the prototype with the
name of the function in its `constructor` property, for util.inspect.
The stamp pointed at the function's name. A bound function's name
("bound " and the target's name) is a string the bound function owns,
so after the `constructor` property was replaced and the bound function
released, inspecting an instance read freed memory. The stamp now holds
a copy of the name, freed with the stamp at exit.

The corpus program constructs through functions whose prototype's
`constructor` is a bound function (and a bound function of one), then
replaces the property and inspects the instances.
2026-10-19 19:51:44 -05:00
Chris Tate 4770b3ffa7 Fix pi's small compile gaps (#1348) 2026-10-19 19:50:07 -05:00
Chris Tate 2f7ac10926 Treat bindings the loop initializer writes as variant in typed-array loop versions
The bounds guard of a versioned typed-array loop runs before the loop's
initializer, and the stability scan covered the body, condition and
update only. An initializer that rebinds the indexed array
(`for (let i = (a = b, 0); i < n; i++) a[i]`) kept the bounds the guard
proved for the old array: the unchecked loop then read and wrote past a
shorter replacement. The scan now includes the initializer, so a binding
it writes is not invariant and the loop runs checked. Loops whose
initializer leaves the receiver alone version as before.
2026-10-19 19:45:33 -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
Chris Tate 2edc7a2c2a Read declarations from symbols and signatures (#1399) 2026-10-19 19:33:42 -05:00
Chris Tate 85d1c423d3 Read generic class narrowings through every class they can hold (#1419) 2026-10-19 19:32:55 -05:00
Malte UblandClaude Opus 5.5 1c3a405277 Expect the lazy-loader refusal for a namespace kept by a loader
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:49 -07:00
Malte UblandClaude Opus 5.5 13fa2c9505 Document built-in packages and unloaded optional dependencies
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:38 -07:00
Malte UblandClaude Opus 5.5 a6003ec011 Implement undici 5.x and 7.x as a built-in package
A TypeScript implementation over node:net, node:tls and node:events:
Dispatcher, Client (HTTP/1.1 with keep-alive), Pool, Agent,
buildConnector, request(), errors, the global dispatcher, fetch, Headers
and the diagnostics_channel publications, with per-version behavior.
Differential npm cases run the vendored real undici under Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:38 -07:00
Malte UblandClaude Opus 5.5 c59457b0dc Map npm packages to built-in implementations and mark optional dependencies unloaded
package-shims.ts registers implementation modules shipped with the
compiler per package and major version (chosen by the importer's
installed version) and reads the project's never-loaded packages from
"scriptc": { "packages": { "<name>": "unloaded" } }. Module edges go
through resolveProjectImport; the npm resolution, the npm-edge walk and
the --npm-static set skip such packages; the tsgo host reads a shimmed
package's index.d.ts as a re-export of its implementation; an unloaded
package's require() throws Node's not-installed MODULE_NOT_FOUND.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:38 -07:00
Malte UblandClaude Opus 5.5 1e38f02a3d Map @types/node 20's class-declared Timeout and Immediate handles
@types/node 20 and older declare NodeJS.Timeout and NodeJS.Immediate as
classes; 22+ (and scriptc's own declarations) declare interfaces. Accept
both as the native timer handle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:37 -07:00
Malte UblandClaude Opus 5.5 4993060d16 Convert string | URL unions to strings through the URL's href
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:37 -07:00
Malte UblandClaude Opus 5.5 b8dd7d2182 Throw MODULE_NOT_FOUND with its code from statement-level requires
A statement-level require() of a package nothing installed threw a plain
Error; the expression form already carried Node's MODULE_NOT_FOUND code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:36 -07:00
Chris Tate 44bd0a7b14 Keep forwarded-only tuple bindings per function
The lowerer kept the tuple bindings that are only forwarded in one map
for the whole program, keyed by local id. Local ids are unique within
one function only: two wrappers that both bind `arguments` (or a
`const args = arguments as any` alias) declare the same id, so a wrapper
whose tuple exists found the other wrapper's entry, and reading an
element of its `arguments` failed with "forwarded-only 'arguments' read
as a value". Each function's context now keeps its own map.

The corpus program wraps functions with a forwarding-only wrapper and
with one that also reads an element of `arguments`, directly and through
an alias, in both orders.
2026-10-19 19:24:17 -05:00
Chris Tate 4f7539103c Keep a lowered ES module namespace unobservable through calls that pass it as this
A call through a bundled ES module's namespace (`ns.fn()`, `(ns.fn)()`,
a tagged template) passes the namespace as `this`. Node's namespace is
the __toCommonJS object (`__esModule` first and non-enumerable,
non-configurable getters); the lowered object of getters differs. The
namespace lowers only when every such call targets a top-level function,
never reassigned, that reads no `this` (an arrow inside it included);
otherwise the package stays refused.
2026-10-19 19:10:11 -05:00
Chris Tate c18a4a790a Resolve chunk references by scope, keep shared functions out of chunk copies, and treat indirect calls as calls in cycles
Three fixes for the bundled-chunk splitter and the CommonJS cycle check:
- A reference counts as local only when a scope between it and the chunk
  factory (or the copied helper) declares its name. A nested function,
  block, loop or catch binding with the name of a top-level helper no
  longer hides the chunk's read of that helper. Before, the helper was not
  copied and the chunk failed at runtime on the unresolved reference.
- A top-level function is copied into a chunk only when the bundle calls
  it and uses it in no other way. Each chunk gets its own copy, so a
  function that is exported, compared, constructed, or given properties
  would lose its shared identity. Such a chunk now keeps the package
  refused.
- During a cycle's initialization, a read of a partner's exported
  function is safe only as a plain value read. A call through
  esbuild's `(0, binding.fn)()` spelling, `new`, a tagged template, a
  member of the function, or passing it to a call can run the function,
  so these uses keep SC1016 (Node throws when the function reads a binding
  in its temporal dead zone).
2026-10-19 19:07:43 -05:00
Chris Tate 31475715a0 Compare strings in UTF-16 code-unit order (#1424) 2026-10-19 19:04:23 -05:00
Malte UblandClaude Opus 5.5 02dba55958 Record order baselines for the new cases and corpus programs; format
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:55:55 -07:00