Commit Graph
4679 Commits
Author SHA1 Message Date
Chris Tate 892758e86b Test catch bindings against class bindings (#1968) 2026-10-23 03:26:31 -05:00
Chris Tate 8d6bb60847 Test caught values against the class a class binding holds
A class binding may hold a subclass of the class it was declared with.
When the declared class has subclasses, a caught payload of that class is
now tested against the class the binding holds, as for other operands, so
a base-class instance no longer passes a test against a subclass.
2026-10-23 03:20:31 -05:00
Chris Tate e7bced94fd Let LLVM merge the comparisons of a three-way string comparator 2026-10-23 03:20:04 -05:00
Malte UblandClaude Opus 5.5 2f903f0255 Read native handle methods as function values
Methods of Web objects held in untyped code (Request, Response, Headers,
ReadableStream, its reader, AbortSignal) answered member calls only by
static name, so a property read such as hono's `raw[key]()` or
`typeof req.json` saw undefined. A handle class now lists the methods its
invoke answers; a read returns one shared function per method that calls
invoke on its `this` and raises Node's ERR_INVALID_THIS TypeError (or a
rejected promise for the async operations) on another receiver. The in
operator sees the methods and the modeled properties.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 01:13:24 -07:00
Chris Tate 9c9fb6531a Replay class objects with their base names, and restore the inspector after a refusal
- The emitter journal that forked workers merge (#1919) records each class
  object entry whole, and the entries carry baseNameSym beside nameSym.
  The replay cast named only nameSym, which the native compiler refuses
  (SC1090), so the self-hosted LLVM emitter did not build.
- An inspection refusal (the 'not supported yet' Error of insp.dyn)
  propagates out of the emitted inspect helpers before they close their
  frames and before util.inspect's layout options are restored. The open
  frames, their indentation and the custom layout then shaped every later
  console.log and util.inspect. A top-level render through helpers that
  can reach a checked-dynamic view, a generator or a class instance now
  runs in a lifted wrapper that takes insp.mark first and calls
  insp.unwind in a finally. A render under breakLength, compact or sorted
  restores the default layout in a finally the same way.
- A non-enumerable accessor that %o shows, and any accessor of a
  checked-dynamic object, renders as [Getter], [Setter] or
  [Getter/Setter], as Node prints it, instead of its value slot.
- Under %o, an array's non-enumerable own properties follow its named
  ones in brackets (`[ 1, 2, [length]: 2, foo: 'x', [hid]: 3 ]`), as Node
  prints them; corpus 6357 covers [length] ahead of named properties.
2026-10-23 03:07:29 -05:00
Chris Tate b9275c2693 buffer: fromArrayBuffer ranges, the deprecated constructor, Buffer as a value 2026-10-23 03:05:19 -05:00
Chris Tate 6e9023ed1f Merge main 2026-10-23 03:04:14 -05:00
Chris Tate ac40773362 Excuse date.fromTime in the attestation parity test
date.fromTime makes a Date from the time value a Date local holds and
reads no ambient state. The local's initializer is the Date expression
the fences witness.
2026-10-23 02:54:19 -05:00
Chris Tate 88d15a8ea7 Call own accessors over an Error's name and message from typed code (#1957) 2026-10-23 02:48:44 -05:00
Chris Tate 4d83db26bd Index strings through checker-any receivers (#1965) 2026-10-23 02:44:28 -05:00
Chris Tate a474ff7e8d Check for Error accessors after definitions over object-typed targets
The reflection gate turned on only for an untyped or Error-shaped
defineProperty target, so a helper taking `target: object` (or `{}`,
`Object`, a weak type, or a type parameter) installed a message or name
accessor that typed Error reads then skipped, returning the old slot
value. The gate now turns on for any target an Error may flow into: an
untyped or generic type, a type Error is assignable to, or an
Error-shaped type, alone or in a union or intersection.
2026-10-23 02:43:57 -05:00
Chris Tate b948d7946b Run match and search with untyped patterns (#1966) 2026-10-23 02:43:38 -05:00
Chris Tate 6318d35679 Iterate untyped Headers values (#1969) 2026-10-23 02:42:23 -05:00
Malte UblandClaude Opus 5.5 b1214f7607 Test catch bindings against class bindings
e instanceof E where e is a catch binding and E a binding holding a class expression (var E = class extends Error {}) refused with "'instanceof' right-hand sides other than classes declared in the program". The binding holds exactly that class, so the test is the class's caught test (hono's SmartRouter fallback on UnsupportedPathError).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:37:51 -07:00
Malte UblandClaude Opus 5.5 e269262d73 Iterate untyped Headers values
entries(), keys() and values() on a checked-dynamic Headers value threw 'is not a function'. They now return live native iterators over the sorted header list, re-read on every step with set-cookie values one by one, as Node's Headers iterator does (hono's Context copies response headers this way).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:37:51 -07:00
Malte UblandClaude Opus 5.5 fdcee191da Give rebuilt property-bag stores their prototype
Worklist passes that transform every function (the class-construction dispatch rewriting dyn.classBasePrototype) rebuild statement objects, so a recorded lazy bag store could be a detached copy. When the class gained prototype data later, only the copy was upgraded and the live store kept creating a bare {} bag: inherited methods read as undefined through the property bag (hono's SmartRouter.match once zod was in the program). Upgrades now also reach the stores as they stand in the generated bodies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:37:50 -07:00
Malte UblandClaude Opus 5.5 be6e63735a Index strings through checker-any receivers
An implicit-any package function specialized for a string argument lowered base?.[0] to the element-access refusal: the checker types the receiver any, so only the lowered string reached the string-index path. Hono's mergePath failed at startup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:37:49 -07:00
Malte UblandClaude Opus 5.5 b8d442e3ae Run match and search with untyped patterns
s.match(p) and s.search(p) with an untyped pattern now choose at run time, as Node does: a RegExp is used as is (with its lastIndex state), any other value builds new RegExp(value). The checked-dynamic receiver form previously threw 'method calls like path.match on a string are not supported yet' (hono's RegExpRouter matcher).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:37:49 -07:00
Malte UblandClaude Opus 5.5 342cf3a063 Copy class-view property bags entry by entry
Materializing the checked view of a JavaScript class instance copies its
property bag through scr_dyn_copy_property_descriptors, which built a key
string and a descriptor object per property and defined each through
scr_dyn_define_property (about 2k instructions per property). Plain objects
on both sides now copy each entry directly with the attributes, value or
accessor pair and own-key order the definition gives; properties the target
already has, non-extensible targets and other receivers keep the general
path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:29:22 -07:00
Chris Tate 358d2df536 Keep package results that pass their argument through out of the declaration boundary
A package function declared to return its own type parameter, which also
types one of its parameters (<T extends object>(obj: T) => T), hands back
the argument or something standing in for it, and the package may track
that object's identity. Effect's Equal.byReference returns a proxy it
keeps in a WeakSet. The declaration-typed boundary cast such a result to
the argument's type and built a separate native record, so
Equal.equals(a, b) on two by-reference objects compared them by value and
printed true where Node prints false. The result's type comes from the
caller's argument, not from data the package declares, so the projection
no longer casts these calls.

The package re-export coverage test now expects three dynamic statements:
since the boundary, readRows's declared number result keeps its type, so
count - 1 in 7004-package-declared-type-reexports is native arithmetic.
2026-10-23 02:23:33 -05:00
Chris Tate 76e57eac7f Merge main 2026-10-23 02:00:06 -05:00
Chris Tateandblurname 2f88f35281 Freeze records that other references still hold
Object.freeze of a record that another reference may still hold stopped at
compile time with SC2020 ("possibly-aliased value"), and freezing a record
held in an untyped value threw SC2020 at run time. Node freezes the object
itself: every later write through any reference throws a TypeError in
strict mode code and leaves the object unchanged in sloppy-mode code.

- A record a program may freeze keeps a frozen bit in its reference count
  word. A shape is freezable when a typed Object.freeze names it, or, in a
  program that freezes untyped values, when a value of it can be held as a
  live untyped reference. Only those shapes change: their release ignores
  the bit, and every write to them, keyed writes and deletes included,
  checks it first.
- Object.freeze of a record held in an untyped value sets the bit and
  freezes the record's checked view, so Object.isFrozen, Object.isSealed
  and Object.isExtensible through that value answer for the frozen record.
  A typed Object.isFrozen of a record reads the bit.
- Programs that never freeze a record compile to the same IR and LLVM.

Corpus 7404 covers typed and untyped freezes, a recursive freeze through
unknown, keyed writes and deletes, optional-field deletes, Object.assign,
tuples, and frozen records that are released or collected as cycles, in
strict mode code. 7405 covers the same writes in sloppy-mode code. Both
match Node.

Refs #1374

Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>
2026-10-23 01:56:34 -05:00
Chris Tate 2232c716a8 Compile instanceof against constructors held as values (#1909) 2026-10-23 01:56:13 -05:00
Chris Tate 6fba766e58 Merge main 2026-10-23 01:52:29 -05:00
Chris Tate f5cecccf5c Reflect the prototype of function-local classes without prototype members (#1958) 2026-10-23 01:46:06 -05:00
Chris Tate 1d68edc0f6 Give function-local class prototypes their own constructor
The prototype object of a function-local class, created once per evaluation
of the class, now starts with its own non-enumerable `constructor`, the
class value of that evaluation, as every class prototype does. Reflecting
the prototype of a function-local class without prototype members exposed
an object without it: `proto.constructor` answered the base class, and
`Object.hasOwn(proto, "constructor")` and its property descriptor did not
match Node.
2026-10-23 01:39:26 -05:00
Malte UblandClaude Opus 5.5 14ba361985 Call own accessors over an Error's name and message from typed code
zod 4 defines its error message with Object.defineProperty(inst, "message",
{ get, set }). The typed-ref commit of an Error subclass read the accessor
as a data value and threw "expected string". The commit and the slot sync
now leave the slot alone and mark the property absent; typed reads and
writes of name and message call the accessor when the program reflects on
an untyped or Error-shaped target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 23:34:56 -07:00
Chris Tate b6d6eb60ae Replace a scheduled _construct hook, and keep runtime option values in order
Node's constructNT reads _construct on the construction tick, so an
assignment after the constructor and before that tick replaces a scheduled
construct hook. The assignment was dropped, so the original hook ran. It
now lowers to stream.replaceConstruct, which swaps the hook while its tick
has not run and has no effect after it or on a stream without a hook.

Runtime decodeStrings and defaultEncoding values are read into locals
before the constructor evaluates the other option members, while Node
evaluates the members in order. A runtime value after a member whose value
has effects (a call or an assignment) now refuses instead of reading a
value that member could still change.
2026-10-23 01:34:19 -05:00
Chris Tate 2ebb89b0ef Store number arrays of proven int32s in 4-byte slots across direct calls
An int32 array candidate can now be a module global or a parameter, not
only a local. A number[] parameter qualifies when its function is reached
by direct calls alone (not as a value, a method or a constructor, and not
through a result-slot entry the backend calls in its place), it is never
written, and every call passes a candidate at its position; a local,
global or parameter may then be passed there as well. A global qualifies
when every function reads it only in candidate positions and every write
stores a fresh array. moduleInt32ArrayCandidates finds them by dropping,
pass after pass, the ones a function or call disqualifies.

The candidates one array can reach through calls form a group
(int32ArrayGroups): the int32 slot analysis keeps or drops a group as one,
since a store through any member writes the same array. A module with a
module namespace object keeps its globals in doubles, as member reads can
reach any global by name.
2026-10-23 01:24:41 -05:00
Chris Tate 7dfc96728d Merge main 2026-10-23 01:19:49 -05:00
Chris Tate 15e3d8f9aa Merge main 2026-10-23 01:13:27 -05:00
Chris Tate 80f0bfada2 Resolve a forward local class per instance in hoisted functions (#1959) 2026-10-23 01:07:52 -05:00
Chris Tate 291aad0f8b Merge main 2026-10-23 01:07:14 -05:00
Chris Tate d942c27c2f Merge main 2026-10-23 01:01:03 -05:00
Chris Tate f3a903607d Merge main 2026-10-23 00:54:10 -05:00
Chris Tate 468e0592bd Enumerate each record in the key order its object literal wrote (#1943) 2026-10-23 00:51:23 -05:00
Chris Tate 341868ea9e Build fresh literals a JavaScript callee may reshape as plain objects (#1951) 2026-10-23 00:46:30 -05:00
Chris Tate e8d1c2f47b Keep arrays stored through a typed global view native (#1952) 2026-10-23 00:45:28 -05:00
Malte UblandClaude Opus 5.5 2f882e1e1e Reflect the prototype of function-local classes without prototype members
Object.getPrototypeOf on an instance of a function-local class refused
unless its prototype members were installed at run time. A class that
declares no prototype methods or accessors (zod 4's
class Definition extends Parent {}) has nothing to install, so its shared
prototype view is returned, as is a bare Local.prototype value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:44:07 -07:00
Malte UblandClaude Opus 5.5 20d0a6c7eb Resolve a forward local class per instance in hoisted functions
A JavaScript package function lowered once per argument-type tuple
collects its local class per instance. A function hoisted above the class
statement in a later instance resolved the class an earlier instance
collected (zod 4's $constructor: "expected class constructor"). It now
takes the forward-class TDZ box unless the collected class is this
instance's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:44:07 -07:00
Chris Tate 5c5d0f5a0f Merge main 2026-10-23 00:42:41 -05:00
Chris Tate 638750751f Merge main 2026-10-23 00:38:51 -05:00
Chris Tate 108df00b29 Keep wrapped array literals native in typed global views
A fresh array literal wrapped in parentheses or `satisfies` on the right
side of a write to a member of a typed view of the global object
(`g.cache ??= ([])`, `g.cache ||= [] satisfies number[]`) is now built as
the declared native array too, so typed reads return the same array.
globalViewSlot walks up through those wrappers to the write's right operand
and answers it, and the slot type comes from that operand's contextual type.
2026-10-23 00:37:49 -05:00
Chris Tate 6e71385a1a Type user code from npm package declarations at a checked, identity-preserving boundary (#1942) 2026-10-23 00:33:02 -05:00
Chris Tate cba3fa6a05 Keep projected files valid for bare async arrows and unbraced bodies, and adopt tuple elements
- A bare arrow parameter after `async` (`async x => ...`) gets parentheses
  with its projected type, as `x => ...` does. Without them the rewritten
  file failed its check and lost its projection (SC6102).
- A cast that starts an unbraced loop or `if` body gets no `;` guard. Only
  a statement in a statement list can continue the previous line, and in
  a body the `;` became the body, which moved the statement after the
  loop.
- Adoption walks a tuple's elements through the live element capsules of
  the tuple's view, so references package code holds to them see the
  native values.
- scr_runtime.h declares scr_dyn_decl_adopt (in scr_dyn.h), which the
  backend emits, so llvm-runtime-abi.test.ts finds its prototype.
- npm.test.ts gives zod3-adoption and zod3-boundary the long per-case
  timeout of the other zod 3 cases; their sanitized builds compile zod's
  types module too.
2026-10-23 00:25:59 -05:00
Chris Tate f8fdb3b3f5 Call observed class methods natively while their slot is unchanged (#1950) 2026-10-23 00:09:35 -05:00
Chris Tate 4a17b3f461 Keep the dynamic call for observed methods called with a spread argument 2026-10-23 00:05:57 -05:00
Chris Tate a9b551a091 Fold navigator member reads under optional chains (#1954) 2026-10-22 23:59:26 -05:00
Chris Tate 0314d9db3e Dispatch checked-dynamic method calls on method ids 2026-10-22 23:52:25 -05:00
Malte UblandClaude Opus 5.5 390ac6f326 Call observed class methods natively while their slot is unchanged
Any computed write with a key TypeScript cannot know (zod's
`curr[el] = ...` in ZodError.js, for one) puts every class method name
in the method census. Every typed call of such a method then went
through the observable prototype slot: a dynamic call with boxed
arguments, followed by a checked conversion of the result (for a
`Product[]` result, a deep walk of every element). Bundling untyped
JavaScript made unrelated typed code several times slower per call.

The prototype data now keeps each observed method's original value in a
global. A typed, non-virtual call of a TypeScript method still reads the
slot first, as before, and calls the method natively when the slot holds
that original value. A replacement, on the prototype or as an own
property, still takes the dynamic call. Virtual calls and methods
declared in unchecked JavaScript keep the dynamic call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:52:15 -07:00