Commit Graph
3745 Commits
Author SHA1 Message Date
Chris Tate 4a5ddadd3d Memoize an empty literal's type separately as a union arm
The type mapper maps the type inferred from an empty object literal to the
empty record as an arm of an unreduced union and to a checked-dynamic
value elsewhere, but both answers shared one memo entry. Once the type had
been mapped on its own, a union such as `typeof empty | DataView` reused
the dynamic answer for its arm and refused (SC2009). The memo key now
includes whether the mapping is for such an arm.
2026-10-21 21:28:27 -05:00
Chris Tate d15e822d26 Hold any non-nullish value in an empty object type inferred from a literal
The type mapper lowered the type tsc infers from an empty object literal
(`const o = {}`) as the empty record, while a declared `{}` already lowered
as a checked-dynamic value. tsc's `{}` admits every non-nullish value in
both cases: union subtype reduction folds primitives into a literal's `{}`
(`["12", {}]` is `{}[]`, `flag ? "x" : {}` is `{}`), and `let o = {}`
accepts `o = "x"`. Such a program built and then threw a TypeError at run
time ("expected object at $[0], got string" in a JavaScript file, "a
'string' value is not representable in the target union" in TypeScript),
where Node prints the values.

An empty object type with no members, index signatures or signatures now
lowers as a checked-dynamic value, the same rule as `object` and `Object`.
As an arm of a union that keeps another non-nullish arm, which subtype
reduction would have removed, it holds only the literal's objects and
stays the empty record.

An assertion's scalar check (`as string`, `as number`, `as boolean`) on a
dynamic value whose result goes straight back into a dynamic slot keeps
the original value, since JavaScript erases assertions: passing `{} as
unknown as string` to process.loadEnvFile reports Node's
ERR_INVALID_ARG_TYPE instead of a TypeError from the check.

Object.assign's two-argument path for a dynamic target now applies the
same refusal as the general path when a class instance is copied into a
fresh object literal whose result the checker types as the class, so
`Object.assign({}, instance)` keeps its compile-time refusal now that the
literal lowers as a dynamic value.
2026-10-21 17:16:12 -05:00
Chris Tate 8428bed383 Call an observed prototype method through a bag that does not hold it (#1712) 2026-10-21 17:04:57 -05:00
Chris Tate d0a581f837 Keep class methods on structural views asserted from unknown (#1713) 2026-10-21 17:03:38 -05:00
Chris Tate d6f857bb1d Keep class methods on structural views asserted from unknown 2026-10-21 16:59:02 -05:00
Chris Tate fa7aa25078 Call an observed prototype method through a bag that does not hold it
A dynamic method call on a class instance whose method name the program
also reads through a prototype (`X.prototype.map` anywhere) uses the
observed prototype slot: the callback descriptor read the method from the
instance's property bag, on the assumption that the bag answers every
read through the class prototype. An instance whose bag was built before
the class had prototype data reads as a fresh empty object, so the read
answered undefined and the call threw "... is not a function" (fast-check's
`integer({ min, max }).map(...)` once its result reaches the call through a
dynamic value).

The descriptor now reads the bag when the bag holds the name, own or
through its prototype, and otherwise the class's prototype data.
2026-10-21 16:58:37 -05:00
Chris Tate 5954d475b7 Record TypeScript 7 baselines for the string field corpus program (#1711) 2026-10-21 16:49:19 -05:00
Chris Tate 228c64a282 Record TypeScript 7 baselines for the string field corpus program
The TypeScript 7 parity lane checks every corpus program against a
recorded preflight and order baseline. 6950-string-field-fast-paths.ts
arrived without one; this records it from the current native lane.
2026-10-21 16:46:53 -05:00
Chris Tate 4124e2a501 Convert global isFinite's argument with ToNumber (#1662) 2026-10-21 16:37:03 -05:00
Chris Tate bbb37ae03b Read a spread's elements before an earlier argument converts
When the Math functions evaluate their arguments into temporaries before
converting (an earlier argument whose ToNumber can run valueOf), a spread
argument's array is now copied where it is evaluated. Before, the
temporary held the array itself and its elements were read after the
conversions, so a valueOf that changed the array changed the values
passed, while JavaScript reads a spread's elements during argument
evaluation.
2026-10-21 16:34:37 -05:00
Chris Tate baa9af6be1 Speed up split, indexOf, slice and Number() on ASCII fields (#1710) 2026-10-21 16:16:14 -05:00
Chris Tate ea4d570e59 Speed up split, indexOf, slice and Number() on ASCII fields
Number() of a string with an optional sign and 1 to 15 digits returns
the integer directly; every other string keeps the full StringToNumber
grammar, out of line.

split() with a separator collects its first batch of pieces before
creating the result, so the result is an inline array of exactly that
size, filled in place. A split's default limit skips the uint32
conversion, and pieces of a proven-ASCII string are created ASCII,
including the scratch pieces of split loops.

indexOf() from the start of a proven-ASCII string searches bytes
directly, and slice() of one skips the UTF-16 index map. The one-byte
case of the byte search reaches memchr without a stack frame.
2026-10-21 16:12:52 -05:00
Chris Tate 2ca85a3d4c Give the fs corpus programs per-process scratch directories (#1709) 2026-10-21 15:56:38 -05:00
Chris Tate f6f00d8b05 Collect the compiler's heap as lowering and LLVM emission grow it (#1708) 2026-10-21 15:54:44 -05:00
Chris Tate 47671d5bed Give the fs corpus programs per-process scratch directories
5568-fs-cp-errors built its fixture in a /tmp directory named after the
program file, which it deletes and recreates. The Node oracle and the
native runs of one program run at the same time, so two runs with the
same name deleted each other's files and the program exited 1 on
random shards. Its root, and that of the five other corpus programs
that name a /tmp/scr-* directory after the program file alone, now
ends in the process id, as 5571-fs-cp-deep's does.
2026-10-21 15:54:23 -05:00
Chris Tate 692569a804 Collect the compiler's heap as lowering and LLVM emission grow it 2026-10-21 15:50:32 -05:00
Chris Tate 621b7cfe58 Build the native compiler with profile-guided optimization (#1707) 2026-10-21 15:36:42 -05:00
Chris Tate 2b48451cf4 Build the native compiler with profile-guided optimization 2026-10-21 15:33:37 -05:00
Chris Tate 1c2b80040b Deliver queued worker messages before immediates, as Node does (#1682) 2026-10-21 15:05:13 -05:00
Chris Tate 4140bcefe2 Walk AST subtrees by their node ranges (#1706) 2026-10-21 15:04:21 -05:00
Chris Tate ca68d63c1c Finish a worker message batch before port messages, and count queued messages as they arrive
- Port messages that a 'message' listener posts while more worker
  messages are queued are delivered after those messages and before
  setImmediate callbacks, as in Node. The ports' poll phase waits while a
  batch's next message is queued, and the loop pass continues once more
  after the batch when a port has something to deliver.
- A mailbox counts its queued events per kind as they are posted and
  taken, so starting a batch reads the count instead of scanning the
  queue under the lock. Messages that alternate with a worker's captured
  stdout or stderr start a batch each, which made delivery quadratic:
  20000 such messages took 5.6 s to drain, and now take 0.5 s (Node 0.57 s).
- The continue hook passes on that the batch's next message is at the
  head, so the dispatch that follows does not lock the mailbox again.
- Corpus 6212 checks the order of worker messages, a MessageChannel
  message posted by the first listener, and setImmediate against Node.
  6211's costs are now taken from its CI timings.
2026-10-21 15:00:22 -05:00
Chris Tate 7ff0ea472d Walk AST subtrees by their node ranges 2026-10-21 14:59:26 -05:00
Chris Tate 543639bdbd Shorten the cycle collector's per-object paths (#1705) 2026-10-21 14:28:10 -05:00
Chris Tate a1f87d817e Resolve the stream-construction fixture's file read on close (#1704) 2026-10-21 14:27:24 -05:00
Chris Tate c1b413e5cc Shorten the cycle collector's per-object paths
markGray's edge visitor kept a stack frame on every call, though most
edges end at an object that is skipped or already gray. Those now return
without one, and the first visit and skipped cross-generation edges
tail-call out of line, as the scan visitors already do.

The walks no longer call the dense-array walker for an empty array. The
routing of nursery candidates past bypassed levels inlines its bypass
test, and the end of each pass tests its work vectors' capacity inline.
What is collected, and when, is unchanged.
2026-10-21 14:25:23 -05:00
Chris Tate 3b35db6b6b Resolve the stream-construction fixture's file read on close
readFileStream resolved on 'end' while its 'close' listener printed
"released". 'close' follows 'end' once the descriptor is closed, so the
line landed wherever that completion fell among the child processes
the fixture starts next, and the comparison with Node depended on
timing. The read now resolves from its 'close' listener, after the line
is printed.
2026-10-21 14:24:57 -05:00
Chris Tate 27855a3835 Resolve conditional member types over literal class type arguments (#1669) 2026-10-21 14:23:50 -05:00
Chris Tate ca31b48bfd Revisit only the changed sites of the optional-read fixed point (#1703) 2026-10-21 14:22:33 -05:00
Chris Tate 558fb7c8ba Bind only conditional-dependent class type arguments as checker types
A generic class instance now binds a type parameter to its checker type
only when a conditional type in the class depends on it, and that checker
type keys the instance. Before, every type argument was bound while only
literal arguments of those parameters were in the key, so two instances
such as Holder<R, "a"> and Holder<R, "b"> with a `T[K]` field shared the
first instance's layout. The other parameters get no checker binding, so
member types that depend on their literal identity stay unresolved, as
before.

Detection now follows the type aliases and interfaces a member's type
refers to, so a conditional wrapped in a union alias, such as
`(K extends "required" ? T : T | undefined) | null`, also separates the
instances.
2026-10-21 14:20:20 -05:00
Chris Tate 453bcf8e38 Revisit only the changed sites of the optional-read fixed point 2026-10-21 14:19:48 -05:00
Chris Tate 7c235ce72b Stop importing runtime bitcode into program builds (#1701) 2026-10-21 14:13:58 -05:00
Chris Tate fc38e62791 Stop importing runtime bitcode into program builds 2026-10-21 14:10:23 -05:00
Chris Tate 3a139a8d40 Negotiate ALPN and bound the TLS version in tls.connect and tls.createServer (#1664) 2026-10-21 13:52:22 -05:00
Chris Tate 9e751b3d38 Give arrays of a known final length exact dense capacity (#1699) 2026-10-21 13:31:21 -05:00
Chris Tate 320abb066e Fix Readable.push and write of string | null and Buffer | null chunks (#1698) 2026-10-21 13:27:12 -05:00
Chris Tate 1d6f26a071 Give arrays of a known final length exact dense capacity 2026-10-21 13:26:50 -05:00
Chris Tate 23ec584deb Read nullable union chunks of push and write by their representation
Readable.push and Writable.write with a union chunk (Buffer, string and
null arms) read the chunk's tag and payload from a tagged union box. A
union with one reference arm and null, such as `string | null` or
`Buffer | null`, is the arm pointer itself, so `push(line)` with
`line: string | null` and `push(done ? null : text)` read past the
string and crashed. Both now read the tag and payload with unionTag and
unionPeek, as the other union consumers do. unshift and end refuse
union chunks and are unchanged.
2026-10-21 13:24:10 -05:00
Chris Tate a4d32a8e38 Use the default TLS version bound for null minVersion and maxVersion in tls.createServer
tls.createServer validated minVersion and maxVersion with its own loop,
which threw ERR_TLS_INVALID_PROTOCOL_VERSION for null. It now uses the
same check as tls.connect, where null and undefined select the default
bound, as in Node.
2026-10-21 13:22:56 -05:00
Chris Tate 5e30e13c87 Read ASCII characters of strings inline in charAt and indexing (#1697) 2026-10-21 13:16:06 -05:00
Chris Tate 7821dccba2 Shrink the array header to 32 bytes (#1695) 2026-10-21 13:14:21 -05:00
Chris Tate 5c851762d6 Read ASCII characters of strings inline in charAt and indexing
charAt and s[i] on a string proven ASCII at an exact in-range index now
return the character from a module-level table of the 128 one-character
strings, emitted with the helper as immortal literals with the same
capacity word and key hash as the runtime's single-character statics.
An exact integer index (a counted-loop counter) skips the double
conversion. Every other string or index calls scr_str_char_at as before.
2026-10-21 13:12:24 -05:00
Chris Tate 96f31cbc18 Shrink the array header to 32 bytes
An array's element ops are a registry id in `spare` instead of a pointer
field: descriptors get their id on their first array (emitted descriptors
are writable for it), and a program with more element types than the id
holds keeps the rest in the array's out-of-line storage.

The out-of-line storage (sparse indices, noncanonical numeric properties,
match metadata) is no longer a header field. An array that has it keeps
its pointer in the 8 bytes before its dense allocation and sets
SCR_ARR_HAS_EXT; such an array always has a dense allocation of its own
(inline slots move out, an empty array gets the prefix alone), and every
allocation, reallocation and free of the dense storage accounts for the
prefix.

Loops over elements look the ops up once, and the state lookup below the
dense capacity reads no out-of-line storage.
2026-10-21 13:04:07 -05:00
Chris Tate 56ff099115 Document when builds use multiple cores (#1694) 2026-10-21 12:55:10 -05:00
Chris TateandQuino Terrasa 1bf49286d1 Document when builds use multiple cores
How It Works gains a Build parallelism section: the frontend runs on one thread, executables divide into
partitions by the size of their LLVM text, the LLVM helper compiles partitions on a thread per hardware thread,
what dev and optimized builds run in parallel and in order, what a rebuild compiles again, and what
profile-guided optimization adds to build time. The CLI reference links to it from --emit, --optimization,
--profile-use, SCRIPTC_CACHE_DIR, and Profile-guided optimization.

Fixes #1340

Co-authored-by: Quino Terrasa <26749475+espetro@users.noreply.github.com>
2026-10-21 12:50:36 -05:00
Chris Tate 349ab75298 Evaluate every ToNumber argument before converting, and convert conditionals
The Math functions and String.fromCharCode/fromCodePoint evaluate all of
their arguments before they convert any, as in Node. When an argument's
conversion can run program code (valueOf or toString on a dynamic value)
and a later argument follows, the arguments now evaluate into temporaries
first and convert in order after (lowerToNumberArguments). Before, a
dynamic argument converted before the next one was evaluated.

A conditional argument whose branches are not both numbers boxes its
value and converts it like any dynamic value, so `isFinite(flag ? "12" :
"abc")` in a JavaScript file returns a boolean instead of reaching a
run-time fence.

A statement inside an expression can end its block (an argument call that
always throws). The expression's later statements and result now emit in
a block with no predecessors, so the instructions there read only values
defined in it.
2026-10-21 12:50:14 -05:00
Chris Tate 975eda9646 Link scr_bytes_dyn.c into libraries (#1693) 2026-10-21 12:40:07 -05:00
Chris Tate d8265b2209 Link scr_bytes_dyn.c into libraries 2026-10-21 12:35:58 -05:00
Chris Tate a437102af6 Keep object literals' key order through IR rewrites and on shapes typed JSON.parse also builds (#1607) 2026-10-21 12:28:31 -05:00
Chris Tate 0739b81f7a Group record-flow references by symbol once per name (#1690) 2026-10-21 12:22:44 -05:00
Chris Tate 293facfdf6 Group record-flow references by symbol once per name 2026-10-21 12:20:12 -05:00