Commit Graph
4307 Commits
Author SHA1 Message Date
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
Chris Tate 80f0bfada2 Resolve a forward local class per instance in hoisted functions (#1959) 2026-10-23 01:07:52 -05:00
Chris Tate 387419720e Support String.prototype.at on strings held in untyped values (#1948) 2026-10-23 01:03:32 -05:00
Chris Tate d942c27c2f Merge main 2026-10-23 01:01:03 -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 0418911874 Build the native compiler again after the two-counter while loop pass (#1956) 2026-10-23 00:50:32 -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 c50fb1275e Build the native compiler again after the two-counter while loop pass
The native compiler spreads only known record shapes, from values whose
type no longer includes undefined (SC1090). runPairedWhileLoopsAsFor
copied a counter's local read through a non-null assertion, and renamed
counters by spreading nodes narrowed to several kinds. It now tests the
local against undefined and renames one node kind at a time. The pass
rewrites the same loops to the same IR.
2026-10-23 00:46:09 -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 2edbe9186a Declare flock on macOS in the native compiler host (#1955) 2026-10-23 00:14:08 -05:00
Chris Tate 257d4dc5cb Declare flock on macOS in the native compiler host
host.c defines _POSIX_C_SOURCE, under which Darwin's <sys/file.h> hides
flock and LOCK_EX/LOCK_UN, so the native compiler build failed on macOS
with an implicit declaration error. Define _DARWIN_C_SOURCE on Apple
targets before the system headers. Linux preprocessing is unchanged.
2026-10-23 00:11:30 -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 67cfa74d92 Dispatch checked-dynamic method calls on method ids (#1953) 2026-10-22 23:56:15 -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
Chris Tate f03b798ea5 Convert typed arrays to BigInt through their element text (#1934) 2026-10-22 23:31:39 -05:00
Malte UblandClaude Opus 5.5 37ea2c9ce7 Keep arrays stored through a typed global view native
A fresh array literal written to a checked-dynamic slot is built as a
dynamic array. On a member of a typed view of the global object
(`(globalThis as typeof globalThis & { cache?: number[] }).cache ??= []`)
the program reads it back through that declaration, and each typed read
extracted a new native copy. `cache().push(x)` was lost, and two reads
were never the same array. Node keeps one array.

When the literal is the right side of a write (`=`, `??=`, `||=`) to a
member of the global object, and the member is declared as a native
array, build the native array and store it boxed. Every typed read then
extracts that same array, and untyped reads see it through its live
capsule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:31:28 -07:00
Chris Tate d027786c80 Convert Buffers and DataViews to BigInt through their own text
BigInt of byte storage joined the elements with "," for every kind, so a
Buffer, whose text is its UTF-8 decoding, and a DataView, whose text is
"[object DataView]", converted as typed arrays. BigInt now parses the
value's ToString, which the runtime value decides, as String does. This also
covers a Buffer held in a Uint8Array slot.

Corpus 7403 adds Buffers, a DataView, and a Buffer in a typed array slot.

Refs #1647
2026-10-22 23:29:34 -05:00
Chris Tate 5159d2e966 Keep replaceable method results that have no dynamic form native (#1947) 2026-10-22 23:28:45 -05:00
Chris Tate c5f0dbbebf Run two-counter while loops on integer counters (#1946) 2026-10-22 23:28:18 -05:00
Chris Tate ebefb8d208 Merge main 2026-10-22 23:23:00 -05:00
Chris Tate 6134dc4c89 Run two-counter while loops as two-counter for loops 2026-10-22 23:20:40 -05:00
Chris Tate acb31f7144 Dispatch String-only method names on untyped receivers by runtime kind (#1926) 2026-10-22 23:06:28 -05:00
Chris Tate 608c6202d4 Spread untyped iterables into Math.min and Math.max (#1928) 2026-10-22 23:02:05 -05:00
Chris Tate ac3cb1c98f Merge main 2026-10-22 22:59:19 -05:00
Chris Tate 7d424368ce Dispatch splice on arrays of unknown elements (#1935) 2026-10-22 22:57:55 -05:00
Chris Tate fc31dd5060 Enumerate each record in the key order its object literal wrote 2026-10-22 22:56:12 -05:00
Chris Tate e3e955eda0 Register Math.min and Math.max spreads as generator steps
dyn.mathFold steps the iterator of an untyped iterable, so the generator
dispatch pass now counts it among the runtime entry points that step
iterators. A generator whose only consumer is Math.min or Math.max then has
its step hooks registered, and the spread folds its values instead of
throwing "Reading this native iterator result has no lowering".
2026-10-22 22:55:26 -05:00
Chris Tate 4dce87dbcb Name the compiler's own shim files independently of the install path (#1941) 2026-10-22 22:51:57 -05:00
Chris Tate 0624d34786 Merge main 2026-10-22 22:50:18 -05:00
Malte UblandClaude Opus 5.5 dd770477b0 Format the boundary harness test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:47:13 -07:00
Chris Tate d190edd228 Declare the stream write-with-encoding helper's IrExpr result (#1939) 2026-10-22 22:46:52 -05:00
Chris Tate 1e4889bebe Declare the stream write-with-encoding helper's IrExpr result 2026-10-22 22:44:30 -05:00
Chris Tate 1171109012 Check with tsc-ts 0.0.8 on every platform (#1937) 2026-10-22 22:41:35 -05:00
Chris Tate b40f268c35 Build the native compiler again (#1938) 2026-10-22 22:41:11 -05:00
Malte UblandClaude Opus 5.5 0b969479c3 Build fresh literals a JavaScript callee may reshape as plain objects
A fresh TypeScript data literal passed to a checked-dynamic slot became a
typed view whose record layout requires every property, so a JavaScript
callee deleting one (zod 4's `normalizeParams` deletes `params.message`
from `.refine(fn, { message })`) threw "Cannot delete property". When
the callee is implemented in JavaScript (a call through an untyped value,
an untyped parameter of a JavaScript function, or a non-library `.d.ts`
signature whose parameter is untyped or makes one of the literal's
properties optional), the literal has no typed alias and is now built as
the plain object the callee receives. Standard-library and TypeScript
callees, and literals with methods or accessors, keep their native
storage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:40:40 -07:00
Chris Tate 1e35b1c56c Build the native compiler again
The self-hosted compiler spreads only known record shapes and tests a
value against null with == or != only as a plain read (SC1090).
rewriteCandidates in int32-arrays.ts spread whole IR statement and
expression unions: each spread now names one kind. The loop and block
cases spread per kind, the hoisted read and sequence rewrites narrow with
an if, switch cases spread through a typed helper, and the inlined
ternary, a fresh copy, takes the call's location directly. The checked
int32 array test in int32-slots.ts binds the range it reads first.
2026-10-22 22:36:34 -05:00
Chris Tate f030e5ad44 Check with tsc-ts 0.0.8 on every platform
tsc-ts 0.0.8's API server serves on the named pipe the Node host
connects over on Windows (tsc-ts --api --pipe), and the native compiler
already starts it over its standard streams there, so tsc-ts is the
default checker on every platform. SCRIPTC_CHECKER=tsgo still selects
tsgo.

The compiler depends on tsc-ts 0.0.8, which the release-age exclusions
admit with its platform packages and @scriptc/threads 0.4.2.

The Windows CLI smoke test runs with both checkers, and
verify-native-cli.mjs --run checks that a distribution's tsc-ts runs on
its platform (tsc-ts.exe on Windows).
2026-10-22 22:36:11 -05:00
Malte UblandClaude Opus 5.5 9a452b8129 Keep replaceable method results that have no dynamic form native
A method whose name an untyped write can replace is called through the
instance's property lookup: a ternary between the dynamic call of an
installed replacement and the native call, both boxed into a dynamic
value and converted back to the method's result type. A result with no
dynamic form, such as an Iterable<T> (its generator arm does not box),
stayed unboxed in the dynamic ternary, which failed IR validation with
"ternary else-branch: expected dyn, got union". Methods of generic class
instantiations always take this path once any write can replace their
name, because their prototype data is not modeled, so importing zod
(whose JavaScript has unknown-key writes) broke every such call.

Such a result now keeps its native type: the native call's value is used
as it is, and only a replacement's dynamic result takes the checked
conversion into the result type. Results that box are lowered as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:24:18 -07:00
Chris Tate 97921c2764 Read and write Error.stackTraceLimit through the Error constructor value (#1932) 2026-10-22 22:21:42 -05:00