Commit Graph
290 Commits
Author SHA1 Message Date
Chris Tate 783870dec3 Compile TLSSocket.isSessionReused, keyed short-circuit assignment and Object.assign into class instances (#1460) 2026-10-19 23:10:10 -05:00
Chris Tate 6c742a7264 Compile class hierarchies across the CommonJS modules of npm packages (#1412) 2026-10-19 22:53:32 -05:00
Malte UblandClaude Opus 5.5 762718a254 Compile TLSSocket.isSessionReused, keyed short-circuit assignment and Object.assign into class instances
- TLSSocket.isSessionReused() is declared in the fallback types and lowers
  to false: the TLS client never offers a cached session (no session
  resumption is a documented divergence), so no handshake resumes one.
- `o[k] ??= v`, `||=` and `&&=` through a pure index-signature record with
  a string key use the keyed read and write `o[k]` and `o[k] = v` lower to.
- Object.assign(instance, record) writes the record's fields into the
  class instance's declared fields (same or width-compatible types;
  optional source fields only when present) and answers the instance.
- Object.assign({}, instance) whose result the checker types as the class
  is refused at compile time: the copy is a plain object, so the checked
  cast into the class threw at run time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:26:47 -07:00
Chris Tate 2c6744e97d Merge main 2026-10-19 22:02:31 -05:00
Chris Tate d04b7a5cf2 Read ThreadSlots values with the layout they are stored with 2026-10-19 21:59:15 -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 4770b3ffa7 Fix pi's small compile gaps (#1348) 2026-10-19 19:50:07 -05:00
Chris Tate 79ecacfb5d Merge main 2026-10-19 17:08:37 -05:00
Chris Tate c82b6d8188 Merge main 2026-10-19 17:06:58 -05:00
Chris Tate c7329845b1 Merge main 2026-10-19 16:48:01 -05:00
Malte UblandClaude Opus 5.5 d204ef71d6 Keep the private-brand diagnostics on shapes that still refuse
Function-local classes whose private names are used only on `this` now
compile, so the two diagnostics fixtures test a brand check on another
object instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:09:32 -07:00
Chris Tate 1f07166fbf Merge main 2026-10-19 08:56:48 -05:00
Chris Tate 178646baf0 Merge main 2026-10-19 08:31:43 -05:00
Chris Tate 3d4f181cb7 Merge main 2026-10-19 08:15:47 -05:00
Malte UblandClaude Opus 5.5 2b8cd22423 Add WebCrypto HMAC: crypto.subtle importKey, sign, verify and digest
crypto.subtle.importKey('raw', ...) for HMAC keys, HMAC sign and verify,
and the SHA-1/SHA-2 digests compile through the crypto global, with
CryptoKey values as native handles. scr_webcrypto.c is only the WebCrypto
layer (Web IDL conversion, algorithm normalization, the CryptoKey handle,
promise wrapping and Node's TypeErrors and DOMExceptions) over node:crypto's
digest core and deferred promise queue. It links only into programs that
call these methods (the new webcrypto link feature). Other algorithms, the
jwk format and the other SubtleCrypto methods are SC2020 refusals.

The real jose@6.2.12 HS256 path (SignJWT, jwtVerify) now runs natively. It
also needed four compiler fixes: object patterns of positions and length
over arrays, the Uint8Array base64/hex feature probes, a compile-time
String.fromCharCode.apply, and structuredClone of a parameter whose default
is {}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 05:34:26 -07:00
Chris Tate 309fa897ec Use EventEmitter, the stream classes and the error classes as values
`function make(C: typeof EventEmitter) { return new C(); }`,
`const kinds: ErrorConstructor[] = [TypeError, RangeError]` and
`(typeof Readable)[]` stopped at SC2020 (the type has no mapping) or
SC1090 (builtin classes as values). These classes are now class values:
the name in a TypeScript file (or a const alias of it) is the class
object, `typeof EventEmitter`, `typeof Readable` and ErrorConstructor-
style types map to class values, and parameters, arrays, Map values and
object fields hold them.

Construction through a value calls %C.construct, which builds the
instance with the runtime constructor the direct `new` uses (emitter.new,
a stream's newDyn without options, error.newOptions for Error and its
six standard subclasses). instanceof, typeof, .name and === answer as in
Node. A Duplex value widens into a `typeof Readable` slot, and stream
values widen into `typeof EventEmitter` slots, as the constructor ABIs
match; a union of such class values (`const kinds = [Readable,
Writable]`) is a value of their nearest common ancestor, and `new K()`
through it answers the checker's union of instances. `instanceof`
through a value that may be Writable keeps the Duplex rule at run time.

EventEmitter and stream values constructed with options, runtime class
values stored as untyped values or printed, statics read through a value,
and DOMException, AggregateError, AssertionError and SuppressedError
values report SC1090. The existing static lowerings
(`EventEmitter.setMaxListeners`, `Error.stackTraceLimit`, the events
module) are unchanged. JavaScript files keep the identity token.
2026-10-19 04:48:10 -05:00
Chris Tate 1b27822d64 Merge main 2026-10-19 00:47:00 -05:00
Chris Tate e6ea0b0e22 Merge main 2026-10-18 23:57:51 -05:00
Chris Tate b61bf84f0f Merge main 2026-10-18 23:39:20 -05:00
Chris Tate dc6a638e63 Lower string and typed array iterators, Iterator.from and helper return()
String iterators (s[Symbol.iterator]()) and typed array values(),
keys() and entries() compile as iterator objects: generators over the
code points and over the indices. The iterator helpers apply to them,
and for...of, spreads and Array.from consume them.

Iterator.from over an array, string, typed array, Set, Map or iterator
object answers the value's own iterator, as Node does for iterators
that already inherit from Iterator.prototype. Objects with their own
next method keep the Iterator fence. As the root of a helper chain,
Iterator.from(x) walks x itself: an array by index, a Set or Map live,
a generator directly.

h.return!() and h.return?.() compile when h is a helper call or a const
initialized with one: helper objects always have return(), so both
spellings are the call. Other iterator objects keep the fence, since the
array, string, Map and Set iterators have no return().

flatMap callbacks may return Sets (their values) and Maps (their
entries), walked live; a string result throws Node's TypeError and
closes the source.
2026-10-18 22:10:17 -05:00
Chris Tate 402558dc71 Merge main 2026-10-18 21:45:00 -05:00
Malte Ubl 7f4a150902 Merge remote-tracking branch 'origin/main' into opt/builtins-npm2
# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-calls.ts
2026-10-18 19:34:12 -07:00
Chris Tate d4d407bb07 Fix npm crashes, internal errors and false TDZ errors from the compatibility research (#1357) 2026-10-18 21:05:11 -05:00
Chris Tate 684f73d186 Add ThreadSlots, per-thread values on shared objects, to @scriptc/experimental (#1271) 2026-10-18 20:39:11 -05:00
Chris Tate 4e92ec16d8 Compile derived constructors whose super() call leads a comma statement (#1354) 2026-10-18 20:25:58 -05:00
Chris Tate 705c197a71 Format Dates with Node's local time zone names
Date.prototype.toString, toDateString and toTimeString, String(date),
template literals and string concatenation print local time with the GMT
offset and the zone's long name, as Node does. The runtime identifies the
host zone from TZ or /etc/localtime, again after each write to
process.env.TZ, and looks its name up in a table generated from Node's
ICU data. Dates held in untyped values convert to strings with toString()
and to numbers with their time value.
2026-10-18 20:20:05 -05:00
Malte UblandClaude Opus 5.5 18033dc42d Compile derived constructors whose super() call leads a comma statement
Minifiers join a derived constructor's statements into one comma
expression (`super(message, options), this.name = ..., ...;`), which jose
6.2.12 ships in its error classes. The statement lowering already runs
each operand of a statement-level comma as its own statement, so super()
is accepted when only parentheses and comma operators separate it from
its expression statement. Every other expression position keeps the
SC1090 refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:57:35 -07:00
Chris Tate 45d4105351 Compile maybe-promise unions, unknown[] rest parameters, and function-valued Maps and Sets (#1347) 2026-10-18 19:55:09 -05:00
Malte Ubl cdf731e4da Merge remote-tracking branch 'origin/main' into opt/npm-runtime-bugs
# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-modules.ts
#	packages/compiler/src/frontend/lowering/lower-stmts.ts
#	packages/compiler/src/frontend/program.ts
2026-10-18 17:50:35 -07:00
Malte Ubl 63d04d3a70 Merge remote-tracking branch 'origin/main' into opt/silent-bugs
# Conflicts:
#	tests/harness/errors.test.ts
2026-10-18 17:32:39 -07:00
Malte UblandClaude Opus 5.5 a1576d7c5f Keep the deferred-class-receiver fixture on a rejected field type
The fixture's class was rejected through a WeakMap-typed field, which
this branch makes legal. A MapIterator-typed field is still refused, so
the class stays rejected and the test still checks that a class reached
only through a receiver probe reports its own diagnostic exactly once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:30:54 -07:00
Chris Tate 008eb0712c Merge main 2026-10-18 19:13:48 -05:00
Chris Tate 6fd2dedb15 Merge main 2026-10-18 19:04:59 -05:00
Chris Tate 6ab49f33b2 Stringify class instances with JSON.stringify (#1338) 2026-10-18 18:59:22 -05:00
Chris Tate 8d94e5d993 Refuse RegExp and Promise class fields in JSON.stringify
A Date field reads through the class's checked-dynamic property view and
writes its toJSON text, so json-class-fields.ts compiled and matched Node
while the diagnostics test expected a refusal. The Date cases move to
6000-json-stringify-class-instances.ts.

RegExp and Promise fields read through the view but the runtime JSON
traversal has no writer for them: a RegExp field threw at run time where
Node writes {}, and a Promise field failed to link. Both are now refused
at compile time naming the field, also inside a field's arrays, records
and unions, and the fixture covers them.
2026-10-18 18:34:09 -05:00
Chris Tate cf11fbaaeb WebAssembly W0–W2: free handle tags, a cross-thread clone hook, the WebAssembly types and refusals, and a decoder that reports every module a program can load (#1339) 2026-10-18 18:12:42 -05:00
Malte UblandClaude Opus 5.5 b8ac5e3f6b Merge origin/main into opt/builtins-npm2
fs.constants as a value now builds its object from the fs group fill that
node:constants added (scr_fs_constants_object, defined after
scr_node_constants so the shared static keeps its place); the fallback
declarations gain the UV_FS_O_* Windows-only flags. Compatibility files
regenerated with pnpm manifest and pnpm node-compat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:03:19 -07:00
Malte UblandClaude Opus 5.5 f4a8511dca Compile maybe-promise unions, unknown[] rest parameters, and function-valued Maps and Sets
A union may now hold one promise beside data members (`T | Promise<T>`,
`Promise<R | void> | R | void`). The union tag is the `instanceof Promise`
test, and every operation keeps Node's microtask timing: `await` awaits the
promise arm or takes the one non-thenable turn, an async `return` (block
bodies, concise arrows, then handlers) adopts the promise arm after the
function's own finally blocks, `Promise.resolve` returns the promise arm as
is, and `typeof (u as any).then` reads the tag. Data arms that declare
`then`, two promise arms, and function arms beside a promise stay refused
with diagnostics. Inferred JavaScript literals that mix promises and values
keep the checked-dynamic array.

Rest parameters typed `unknown[]` or `any[]` in TypeScript use the
checked-dynamic variadic ABI that JavaScript rest parameters already use,
including spread forwarding (`(...args) => handler(...args)`).

Functions are now Map values, Map keys and Set elements, compared by
closure identity.

Two pre-existing bugs in the same area are fixed: an async lambda in a
`Promise<void> | void` slot got a `Promise<union>` ABI and threw when called
through the slot, and `return cb()` of such a callback in an async function
dropped the promise instead of adopting it.

The retired-engine 2251 and 2790 diagnostics fixtures (tagged templates
with `any[]` rest tags) now compile and match Node, so they move to the
corpus as 6248 and 6249.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:58:57 -07:00
Malte UblandClaude Opus 5.5 ba17241220 Run String and Number methods on dynamic values and add substr, locale case and toPrecision
string-width's `character.codePointAt(0)`, camelcase's
`string.toLocaleLowerCase(locale)`, toml's `input.substr(...)` and
pretty-bytes' `number.toPrecision(3)` threw "x.y is not a function": the
dynamic method dispatch had no answer for these names on strings and
numbers.

A checked-dynamic receiver calling a String or Number method the runtime
dispatch cannot answer (codePointAt, substr, toLocaleLowerCase,
toLocaleUpperCase, isWellFormed, toWellFormed, trimLeft, trimRight,
toPrecision, toExponential(digits)) now evaluates the receiver and
arguments once and dispatches on the runtime kind: a string or number runs
the typed lowering, every other kind keeps the runtime dispatch's answer.
Names other dyn kinds share (at, concat, slice) and the Annex B HTML names
chalk-style builders define (bold, link, ...) keep their existing
dispatch.

Typed receivers gain substr, the locale case pair, the HTML methods,
toPrecision and toExponential(fractionDigits), all previously compile-time
refusals. toPrecision/toExponential digits come from exact big-integer
arithmetic over the double (ties up on the exact binary value). Locale
case mapping follows V8: the first requested locale decides, Turkic
dotted/dotless I is implemented, and Lithuanian, Greek uppercasing,
objects and malformed tags throw a 'not supported yet' Error.

A checker-untyped JS binding holding `string | undefined` boxes into the
dyn receiver path for String methods (string-width's baseVisible result).
tests/diagnostics/retired-engine-1112-number-methods.ts compiles now and
moves back to tests/corpus/1112-number-methods.ts.

New runtime functions are reached only from the new lowerings; the
existing runtime objects' sections are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:55:12 -07:00
Chris Tate b32f4e31f3 Merge main 2026-10-18 17:47:55 -05:00
Chris Tate 2f40bcb8d4 Lower iterator helpers on generators and Map and Set iterators
map, filter, take, drop, flatMap, toArray, forEach, reduce, some, every
and find compiled only as one chain from arr.values() or arr.entries()
that ended in a consuming method. Every other form was refused, and in
JavaScript the refusal was thrown at run time.

IteratorObject now maps to the generator type. A consuming method over
a chain of stages fuses into one loop over the source: a generator
driven like for-of, a Map or Set walked live, or an array by index. A
stage on its own builds a helper object: a generator over its source
that supports next(), for...of, spreads, Array.from and yield*. A
for...of whose head is a chain of stages without flatMap runs the
stages inside the loop.

The source is closed where Node closes it: when take reaches its count,
when some, every or find has its answer, when a callback or the loop
body throws, when the loop exits early, and before the RangeError of an
invalid take or drop budget. It is not closed when it is exhausted or
its own next() throws. return() on a helper object before its first
next() closes the source through a new runtime hook,
scr_gen_set_unstarted_return.

When closing an iterator throws while another error propagates, the
first error reaches the caller, as in Node. This applies to for...of
over a generator too, where the error from the close used to replace
the body's error. The try statement takes a dropFinallyErrors flag,
whose cleanup handler calls the new scr_exc_keep_first.

throw() lowers only on values typed as generators. Iterator helpers and
the built-in iterators have no throw method, and a value typed as an
iterator may hold one.

Callbacks in JavaScript bind their unannotated parameters to the
element type, and flatMap callbacks may return generators. A generator
whose record yields share checked storage with its return channel has
each value converted to the declared element type as it is pulled.
2026-10-18 17:46:31 -05:00
Chris Tate b119b4cf03 Stringify class instances through the runtime JSON traversal
JSON.stringify of a class instance, or of a record, array or union that
holds one, boxes the value and serializes it with the runtime JSON
traversal. The output matches Node: own enumerable properties in
creation order, properties added after construction, toJSON on the
instance or a base class, and no methods, accessors, #private fields or
static fields. A class whose declared field cannot be read through the
checked-dynamic property view, such as a Date field, is refused at
compile time with SC1090 naming the field.

Boxed Map and Set values now stringify as {}, as Node does, instead of
throwing.
2026-10-18 17:32:17 -05:00
Malte UblandClaude Opus 5.5 c7f3fa6564 Type-check programs that mention WebAssembly and refuse its API precisely
Packages whose declarations name WebAssembly types (quickjs-wasi,
photon-node, and therefore pi) failed scriptc's type check with SC0001
"Cannot find namespace 'WebAssembly'", because scriptc's ES-only type
world never loads lib.dom, so `scriptc coverage` could not analyze them.

- scriptc.d.ts declares lib.dom's WebAssembly namespace and BufferSource
  (admitting every ArrayBufferView, so Node's Buffer types fit).
- The type mapper gives every type declared in the namespace, and unions
  over one, the checked-dynamic representation, so declarations that only
  mention them compile.
- Every reached use of the API value refuses at its site with a named
  SC2020 diagnostic: `WebAssembly.<member>` reads and calls,
  `new WebAssembly.<Class>`, `instanceof WebAssembly.<Class>`, the bare
  namespace, `globalThis.WebAssembly`, and the global object viewed
  through a cast to a type with a WebAssembly member (pi codemode's
  spelling). TypeScript sources fail the build; JavaScript sources get a
  located runtime fence.
- `typeof WebAssembly` and `typeof WebAssembly.<member>` need no value and
  fold to Node's answers ("object", "function"), including inside
  `typeof x === "..."` tests, so feature probes in JavaScript glue behave
  as in Node. Before, a JavaScript probe answered "string" (the identity
  token) and silently took the non-wasm branch.
- The surface manifest projects the refusal (stdlib.webassembly), and the
  compatibility ledger's `WebAssembly` global row moves from
  not-implemented to refused; no other row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:19:38 -07:00
Chris Tate 4c27ab8bd7 Compile classes that extend a generic class factory's result
`class Counter extends base(42) {}` with `function base<T>(initial: T) {
return class { value: T = initial; }; }` failed with SC1090 "extending
computed expressions": the call's result type is the factory's local class
instantiated with T = number, which only maps inside the factory's own
generic instance.

A call of a generic function whose result is one of its local classes (or
an instance of one) now keys the instance on its type bindings and maps the
result inside that instance, where the local class takes the instance's
name and bindings. The class value then works as a base (`extends
base(42)`), as a `const` binding, through `new`, and for method calls and
field reads on its instances. Each instantiation has its own class, so
`base(1) === base(1)` is false and `instanceof` tells them apart, as in
Node. Calls inside another generic instance bind through the declaration;
calls elsewhere also record the instantiated checker type, so later uses of
it name the same class.

Fixes #1241
Co-authored-by: BaoLei <50226652+blurname@users.noreply.github.com>
2026-10-18 17:17:22 -05:00
Chris Tate 356d968528 Merge main 2026-10-18 17:07:58 -05:00
Chris Tate 3b0ffb24e4 Compile overloaded methods on interfaces and object types (#1333) 2026-10-18 17:01:44 -05:00
Malte UblandClaude Opus 5.5 11c97074e2 Answer the object a constructor returns from new
A class constructor's `return expr` was evaluated and dropped, so
`new C()` answered the constructed instance even where Node answers the
returned object (`if (C.instance) return C.instance;` built a second
instance).

A return that yields an instance of the class itself stores it in a
per-class result slot, read and cleared by one helper at every
construction site (direct new, class values, the checked dynamic
construction dispatchers, record-to-class coercion). A return of any
other object refuses with SC1090 (compile time in TypeScript, a
deferred fence in JavaScript); a JavaScript value typed only at run time
is tested when it returns. super() into a class that can return an
existing instance refuses, since Node would rebind the subclass's this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 14:42:18 -07:00
Malte UblandClaude Opus 5.5 f4daefa6be Compile overloaded methods on interfaces and object types
An interface or type-literal member with several call signatures (the `on("event", handler)` registry shape, arity overloads, type overloads sharing one implementation) failed its whole record shape: the type mapper maps only single-signature function types, so the member mapped to nothing and every type mentioning the interface failed with it.

JavaScript stores one function under such a member; overloads only constrain call sites. The member now keeps a record slot holding that function. Its type joins the overloads per parameter position and result: equal types stay, scalar mixes form their union, and other mixes travel as checked `unknown` values when every joined type crosses that boundary both ways (differing callbacks become checked function values that any implementation's callback type adapts from). Positions some overloads omit are optional, and an overload's own type parameters erase to `unknown`. Rest parameters and a void result beside a value result keep the member fenced, now with a diagnostic naming the overload and parameter that block it.

Calls convert their arguments into the slot and the result back to the resolved overload's type, on record and union receivers, through optional chains, and on call results. Object-literal methods find their member slot through the literal's contextual type, so an implementation with optional parameters fills an arity overload set (and a single-signature member's slot). Value reads of the member keep SC2007. Library, declaration-file and class overload declarations are unchanged.

The zero-cost gate passes against the parent commit with all 2379 programs identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 14:40:40 -07:00
Malte UblandClaude Opus 5.5 beddc5f94a Keep typescript-go's tuple-literal panic with the statements that ask
`s.match(re) || []` contextually types the empty array by the tuple-like
RegExpMatchArray, so typescript-go builds an empty tuple target and
clones it as an array literal. The clone keeps ObjectFlagsTuple on
TypeReference data, and the API's newTypeResponse panics (interface
conversion to *TupleType) whenever it serializes the clone, for example
as a member of `RegExpMatchArray | []`. Whole-program analyses asked for
such union members first, so every program containing the pattern
(moment.js, or a two-line TypeScript file) failed at 1:1 with SC0004.

A union whose members typescript-go panics to serialize is now
unmappable at the top of mapType, the any-array probe answers false for
it, and the panic is remembered per type instead of re-requested.
Queries that still need the members keep the source-anchored SC0004 on
their own statement (a runtime fence in JavaScript). Other checker
panics are unchanged. The panic itself is an upstream bug in
internal/api/proto.go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 13:51:44 -07:00
Chris Tate ea7bcd9846 Resolve builtin classes through const aliases (#1327) 2026-10-18 15:42:15 -05:00