A Symbol.asyncIterator method that returns a compiled generator is iterated
instead of refused. Async yield* awaits a synchronous delegate's final value,
and return() results that are not objects throw V8's TypeError, which names
primitive results. The coverage fixture drops its async yield* example, which
now compiles.
`globalThis as { [KEY]?: State }` is the usual way for several modules
to share process-wide state. globalThis lowers to a checked-dynamic
value, and asserting a checked-dynamic value to a record or array type
took the checked conversion, which builds a new native value. A write
through the asserted value went to that copy, so other modules never
saw it; string-keyed and index-signature views threw "expected object".
- An assertion of the global object (globalThis, global, also through
`as unknown as`) keeps the checked-dynamic value when the conversion
would copy, so member reads and writes reach the global object.
- File-scope and local bindings of such an assertion hold the global
object (collectGlobals, lowerVarDecl), so an exported
`const state = globalThis as ...` works across modules.
- The divergence scan no longer reports these assertions as SC6001
width copies.
- Runtime: one global-object box per thread (scr_global_object), which
globalThis, global and globalThis.globalThis all answer, and direct
`globalThis[symbol]` accesses use that box's symbol-keyed properties,
so every spelling of the global object sees one property.
Other checked-dynamic values keep the checked conversion: a native
value held as unknown unwraps by identity there, and a checked-dynamic
tree is copied at the documented boundary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
new WebAssembly.Memory, Table and Global construct the runtime objects an
instance exports, with Node 24's descriptor order, defaults, conversions
and messages (V8's EnforceUint32 errors, the v128 and shared-memory
errors; a valid shared memory is refused with a TypeError until threads),
so either kind can be imported. Imports of memories, tables and globals
now check limits, types and mutability with V8's LinkError messages, and
exports keep V8's identity (imported objects re-export as themselves, one
object per memory and table). The classes called without new throw Node's
TypeError.
WebAssembly.Module.imports, exports and customSections read the
descriptor (ABI v2). Custom sections are carried only by programs that
call customSections. Every function a module references reaches
JavaScript as a callable function: the translator emits one call thunk
per signature, so table.get and funcref globals return the module's
unexported functions too, and exported functions can be stored in tables
and globals from any instance. Discovery also finds .so WebAssembly
shared libraries at resolved paths.
Fixes to W3/W4 found on the way: externref Table.grow fills with
undefined; Memory.grow/Table index errors and funcref messages match V8;
an invalid funcref global import is a LinkError; global.valueOf() on a
WebAssembly.Global-typed receiver answers the value; explicit-bounds
memories keep the blocks grow replaced, so old ArrayBuffers never
dangle.
This unblocks quickjs-wasi's extension loader: quickjs-wasi with its url,
encoding, headers and structured-clone extensions runs through its own
glue identically to Node, and pi codemode's static coverage goes from 83%
to 84% with no WebAssembly blocker left.
Evidence: differential corpus programs for the API (webassembly-jsapi),
a 261-test port of WPT wasm/jsapi (webassembly-wpt-jsapi), quickjs-wasi's
dist/extensions.js run unmodified against dylink-shaped modules
(webassembly-dylink), and the 2.0 spec suite's modules that import the
spectest memory and table (new webassembly-spec-imports group plus the
memory and table groups); all match Node in the plain and sanitized
lanes. The zero-cost gate leaves every non-WebAssembly program
byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/coverage-fixtures/mixed.ts used `const { length: firstItem } = items`
as its SC1031 example. Object patterns of positions and length over arrays
compile now, so coverage counted 13/13 static. The example now destructures
a union source (array | string), which keeps the SC1031 object-pattern fence
at the same line and column; only the message's source kind changes, from
array-typed to union-typed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each record shape's enumeration table now follows the key order its object
literals, spreads, rest bindings and record copies create, and appends
optional keys assigned after construction. Shapes whose values are built
in more than one order keep their declaration order and SC6002.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch lowers Number.prototype.toPrecision statically, so the
tier-demotions and dynamic-mix coverage fixtures lost the operation they
used as "no static lowering". Use n.toLocaleString() instead: the host's
default locale is something a compiled binary cannot carry, so it stays
refused (SC2020) and the fixtures keep the same blocker and demotion
request on the same lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`scriptc coverage` now lists every WebAssembly module whose bytes the
build can see, with its size, whether it validates, its function, import
and export counts, and the WebAssembly proposals it uses. Nothing is
translated yet; builds are unchanged.
- packages/compiler/src/wasm/binary.ts decodes and validates a module in
plain TypeScript (the native compiler compiles it too) at the feature
level scriptc means to translate: the MVP plus sign-extension,
non-trapping float-to-int, bulk memory, multi-value, mutable globals,
reference types and extended constant expressions. Where WebAssembly
3.0 and V8 relaxed a 2.0 rule (global initializers reading defined
globals, memory indices), it follows V8, the behavior oracle. Modules
that use SIMD, threads, exception handling, tail calls, function
references or GC, memory64 or multiple memories still decode, and are
reported as needing that proposal instead of as invalid.
- packages/compiler/src/wasm/discover.ts finds modules from four sources:
paths the compiler resolves from the source text (require.resolve,
createRequire(...).resolve, import.meta.resolve,
new URL(..., import.meta.url), __dirname joins), inline base64 and byte-array
literals, the .wasm files of reached packages whose code mentions
WebAssembly, and a `scriptc.wasm` list in package.json or the new
coverage-only `--wasm <file>` flag. Modules are reported once, by
SHA-256, with every source that found them; module files are input
probes of the coverage verdict cache. The JSON envelope
(--print=diagnostics) carries the same records under `wasmModules`.
Evidence: packages/compiler/test/wasm-spec.test.ts runs the decoder over
the 3,017 distinct binary modules of the WebAssembly 2.0 core test suite
(without SIMD) and the extended-const proposal's tests, generated by
gen-wasm-spec-cases.mjs with wabt's wast2json, and requires V8's verdict
for each; 8,000 seeded mutations of those modules must also agree with
V8, as must undici's inline llhttp modules (scalar valid, SIMD reported)
and es-module-lexer's. src/wasm/binary.test.ts checks each feature and
each error class against V8. On the survey's real modules the decoder
agrees with V8 for quickjs-wasi (637 KB, 36 ms) and photon (1.9 MB,
121 ms). tests/coverage-fixtures/wasm pins the four discovery sources.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Date.parse and new Date(string) answered NaN natively for every form
outside ISO 8601 with an explicit offset and the X509 certificate shape,
so RFC 1123 HTTP dates ("Mon, 01 Jan 2024 00:00:00 GMT") never parsed and
the fresh/express/send conditional GET never matched. The runtime now
falls back to a port of V8's DateParser (the ES5 date-time format, then
the Safari-compatible legacy grammar: weekday and month names, RFC 2822
offsets, zone abbreviations, AM/PM, parenthesized comments, slashes) for
exactly the strings the bounded grammars reject, so the timestamps they
accept cost nothing more. Zone-less dates are local time, converted with
the spec's UTC(t) over the host zone (DST gaps read with the offset before
the transition, overlaps answer the earlier instant).
With native parsing matching Node for every input, the SC6005 literal
warning has nothing left to report and is retired rather than extended to
non-literal inputs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stage S2 of the two-tier design. IrFunction gains a tier (absent means
static); lowering assigns it from function bodies, placing functions that
use the dyn vocabulary in the dynamic tier, and the validator enforces
invariant I1: a static function performs no dynamic operation, only
boundary operations (dynFrom, dynCheck, kind tests) and moves. Serialized
IR keeps the field out in this stage and loaders derive it again, so every
IR, LLVM and executable artifact is unchanged.
Every committed refusal and runtime fence becomes a recorded demotion
request attributed to the binding, field, function instance, class or
module the dynamic tier would take over, while the compiler still refuses
and fences as before. `scriptc coverage` reports dynamic-tier statements
(statementsDynamic), `--explain-dynamic[=<name>]` prints the demotion
chains, and `--tier=auto|static-only` is wired through build, run and
coverage for stage S6; both modes compile identically today.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SC6001 only looked at call arguments and typed initializers, and only at
strict field subsets: a target that adds optional properties, the empty
record type, `r as T`, a later `v = r` into a typed binding, and records
stored as elements of a typed array literal copied silently.
The scan now reports a copy whenever the property sets differ, covers
those sites, and reports a class instance that reaches a plain record
type which does not keep its identity (the remaining boundary: interfaces
of only generic methods). Every corpus program still reports no
divergence.
- Lower Number.prototype.toFixed through the compiler IR and native backends.
- Match JavaScript rounding, range errors, and numeric edge cases in the runtime.
- Add differential coverage and update surface metadata and diagnostics.
- mixed.ts and dynamic-mix.ts compare unknown against number so the SC1040 blocker line survives the same-kind == lowering; snapshots re-record
- corpus 2428's example strings drop the brand word; order-parity baseline re-records for the content change
- workspace-linked untyped dists register before the tsc gate and take the node_modules-JS island treatment (errors suppressed, out of userFiles)
- bare '.' and '..' specifiers resolve as relative directory imports (corpus 2386, coverage fixture dot-parent, SEMANTICS 362)
- TS default imports of supported builtins pass under the project's adopted interop knobs (corpus 2387)
- benign-cycle admission accepts d.ts-rooted callable arguments — promisify(fs.readFile) at a cycle member's top level (SEMANTICS 363)
- --npm-static auto probes runtime JS from the importing file's realm, not the entry's (nested-realm fixture)