Commit Graph
4211 Commits
Author SHA1 Message Date
Chris Tate f9da1eb6ed Store local number arrays of proven int32s in 4-byte slots (#1920) 2026-10-22 21:30:45 -05:00
Chris Tate e470edc111 Merge main 2026-10-22 21:24:30 -05:00
Chris Tate 6d7d9382d9 Read only IR nodes as IR nodes in the code-unit locals pass (#1927) 2026-10-22 21:23:42 -05:00
Chris Tate 5edf9df5ef Read only IR nodes as IR nodes in the code-unit locals pass 2026-10-22 21:19:05 -05:00
Chris Tate d62d12e9c3 Pass zlib's flush sentinels to _transform as Buffers (#1924) 2026-10-22 21:17:02 -05:00
Chris Tate 107d773382 Pass zlib's flush sentinels to _transform as Buffers
flush() on a zlib stream writes a zero-length chunk that _transform
recognizes by identity and compresses with that flush kind. Since
writes of a typed array that is not a Buffer reach _write as a Buffer
over the same bytes (as Node's Stream._uint8ArrayToBuffer does), the
sentinel, created as a plain Uint8Array, reached _transform as a new
Buffer. _transform then used the default NO_FLUSH, so flush() emitted
nothing: corpus 5374-zlib-stream-flush-params decoded an empty prefix
where Node decodes "hello ".

The sentinels are Buffers, as Node's kFlushBuffers are (Buffer.alloc(0)),
so write() passes them through unchanged.

The npm fixture stream-shims, whose test expected a deferred SC1090, now
runs to a later deferred refusal, SC2020 (write completion callbacks
taking the error argument); its expectation names that code.
2026-10-22 21:14:00 -05:00
Chris Tate 5e1078115f Borrow the array of an inline Atomics operation from its local (#1922) 2026-10-22 20:59:58 -05:00
Chris Tate f3bb5d2cb1 Borrow the array of an inline Atomics operation from its local
emitInlineAtomicsOp read its typed array with emitExpr, so every Atomics
call on a local array retained the array before the atomic instruction and
released it after, two out-of-line calls around one instruction. The
array now goes through emitBorrowedInput: an unchanged, unboxed local owns
it across the later operands and the operation, and the instruction and
scr_atomics_op only read it. Fields, globals, captures and bindings an
operand can reassign keep their own reference.
2026-10-22 20:57:07 -05:00
Chris Tate e501b046d5 Keep the native compiler's LLVM text as the emitter's parts (#1919) 2026-10-22 20:56:27 -05:00
Chris Tate 1bd65afe61 Keep the charAt unit reads' origins on the emitter (#1921) 2026-10-22 20:56:03 -05:00
Chris Tate b025254f79 Keep the charAt unit reads' origins on the emitter
The charCodeAt reads that stand for rewritten charAt reads (one-unit
tests and switches) recorded the charAt each stands for in a
module-level WeakMap. Compiler sources keep such caches on their
owners: the map now belongs to the emitter (charAtUnitReads), and
charAtUnitRead takes it.
2026-10-22 20:53:28 -05:00
Chris Tate 5ed872b843 Index arrays with exact i64 products of int32 values (#1857) 2026-10-22 20:53:08 -05:00
Chris Tate 7b79d73a55 Store local number arrays of proven int32s in 4-byte slots
A local number[] that only compiled element operations reach, and whose
every stored element the int32 slot analysis proves an int32 (never -0),
is now stored as SCR_ELEM_I32: 4-byte int32 slots, with state bytes once
it holds a hole or undefined, as for every array. Element reads give
consumers an exact int32, so indexing with an element skips the double
round trip, and the slots take half the memory.

The runtime's dense slot accessors convert between the int32 slot and the
double, so its double-valued entry points serve int32 arrays unchanged;
growth, copies, fill and length changes use the 4-byte width.

ir/int32-arrays.ts proposes the candidates and inlines their element-reader
helper calls; the int32 slot analysis keeps the proven ones, and
selectInt32Arrays rewrites the module for exactly those, so a program
without an int32 array emits the same LLVM IR as before.
2026-10-22 20:52:38 -05:00
Chris Tate 5e4303e62d Keep the native compiler's LLVM text as the emitter's parts
The LLVM emitter builds a module as parts and joined them into one
string: on a large program such as tsc-ts the parts and the 150 MB joined
text were live together at the end of emission. The native compiler only
writes, escapes and hashes the text, so it now takes the emitter's parts
(emitLlvmModuleParts) and never joins them. Consecutive small parts are
joined into batches of about 1 MiB as they are written, escaped or hashed
(joinedParts), so the many small parts cost few calls. A frontend cache
hit reads the text back as one part. The three emission entry points
share one module pipeline (moduleEmitter).
2026-10-22 20:52:22 -05:00
Chris Tate fc05167a29 Merge main 2026-10-22 20:50:35 -05:00
Chris Tate 5ad562da7e Hold any later value in JavaScript var bindings (#1916) 2026-10-22 20:46:16 -05:00
Chris Tate 3991a49094 Hold any later value in JavaScript var bindings
A JavaScript `var` took the checker's type of its initializer, which no
later write is checked against in a JavaScript file. js-yaml 4.1.1 failed
on every timestamp with "a 'string' value is not representable in the
target union": its `var fraction = 0` holds the fraction's digits as a
string while it pads them (`fraction = match[7].slice(0, 3)`). Every YAML
error also threw the "assignment to non-variables" fence instead of the
YAMLException, because its error mark is a `var` object literal that
gains a member (`mark.snippet = snippet(mark)`).

- A `var` whose writes the declared type cannot hold is checked-dynamic,
  by the rules `let` already uses (jsBindingHasOpenWrites), and so is a
  `var` whose object literal gains members or carries methods
  (jsDeclarationExpandsObject, shared with `let` and `const`).
- `+=` of a string onto a number or boolean binding (`width += "px"`)
  counts as such a write, for `var` and `let`.
- A JavaScript function that returns such a binding returns a
  checked-dynamic value (an async one a promise of one) instead of the
  initializer's type. A string binding that is only appended to stays a
  string.
2026-10-22 20:40:56 -05:00
Chris Tate 8c67f04062 Throw Node's TypeError for class constructors called through call/apply (#1892) 2026-10-22 20:36:01 -05:00
Chris Tate 5966f23bd3 Keep the class-value call fence when a subclass declares a static call
`C.call(...)` through a class value typed as `Base` may run a subclass's own
static `call`, which Node calls instead of the class constructor. The
constructor-call lowering now applies only when no class the receiver can
hold declares a static `call` or `apply`: the class and its bases (any
static member of that name, including accessors, fields without
initializers and computed names), and, for a receiver that may hold a
subclass, every subclass. Otherwise the call keeps its existing
diagnostic.
2026-10-22 20:33:33 -05:00
Chris Tate 210dee9eb0 Read the global object without rebuilding its name table (#1895) 2026-10-22 20:26:51 -05:00
Chris Tate a9ee38611e Register the global object's names again after a library re-init 2026-10-22 20:20:56 -05:00
Chris Tate 8807c24526 Compile zod 3 natively (#1835) 2026-10-22 20:13:04 -05:00
Chris Tate 0c6a35996d Fix the native compiler build on main (#1913) 2026-10-22 20:10:49 -05:00
Malte UblandClaude Opus 5.5 122c98cf4f Read the global object without rebuilding its name table
Every evaluation of `globalThis` as a value built an array literal of
every name the global type declares (hundreds of strings) and registered
each one in the known-name table again. A server that reads a singleton
through `globalThis` per request spent about a tenth of its time there.

Emit the name table as one immortal static array per program (the
tagged-template site array, keyed by its contents), and return the
global object at once when the runtime sees a table it has already
registered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:10:05 -07:00
Chris Tate ae5c7d7780 Map interfaces that extend an any-typed base (zod z.infer) to checked-dynamic values (#1912) 2026-10-22 20:08:05 -05:00
Chris Tate 33b22c5bac Fix the native compiler build on main 2026-10-22 20:07:43 -05:00
Chris Tate 1660531c7b Support FinalizationRegistry with bookkeeping and no cleanup callbacks (#1854) 2026-10-22 20:03:31 -05:00
Chris Tate 62a0426193 Keep Linux poller timers in a heap behind one timerfd (#1896) 2026-10-22 20:02:08 -05:00
Chris Tate 636ffa21be Merge main 2026-10-22 19:59:51 -05:00
Malte UblandClaude Opus 5.5 d0334f93f5 Record the preflight/order baseline for the zod3-infer npm case
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:57:59 -07:00
Chris Tate b90a01fb80 Update the diagnostics fixtures now that Iterable values compile (#1910) 2026-10-22 19:57:19 -05:00
Chris Tate 56149fde04 Count each epoll test deadline from its own arm call
The poller test recorded every initial deadline as the same start time
plus the delay, while the poller reads the clock at each arm call; when
arming the 2,000 timers took over 1 ms, the recorded order could differ
from the delivered one by more than the assertion's slack. Each deadline
now counts from the time read just before its scrp_timer_arm call, as
the re-arm loop's do, and a 3 ms pause halfway through arming keeps the
test exercising that case.
2026-10-22 19:56:53 -05:00
Chris Tate fbaa71a66f Run the fromEntries aliasing program and drop compiled Iterable entries from the iterator fences
Since Iterable<T> values compile (#1875), two diagnostics fixtures changed:

- fromentries-aliased-entries.ts compiles and prints what Node prints:
  the entries' one array stays shared by both fields. It moves to the run
  corpus as 7440-fromentries-aliased-entries.ts.
- iterator-slot-fences.ts no longer reports its Iterable<number>
  parameter (SC2020) and the call through it (SC2004). That part leaves
  the fixture, which keeps the class-iterator fence; corpus 7380-7383
  cover Iterable values.
2026-10-22 19:54:17 -05:00
Chris Tate b19a589c12 Keep the global object behind a typeof globalThis intersection cast (#1894) 2026-10-22 19:53:35 -05:00
Chris Tate b7d355ed13 Stringify live typed records and arrays without a key list (#1897) 2026-10-22 19:52:19 -05:00
Chris Tate 7a96b0de57 Spread untyped sources into typed object literals (#1893) 2026-10-22 19:48:13 -05:00
Chris Tate 3cace3e785 Throw Node's TypeError for class constructors called through call/apply
`C.call(thisArg, ...)` and `C.apply(thisArg, args)` on a program class that
declares no static `call` or `apply` now lower as a call through the boxed
class value. It evaluates the arguments in order, applies apply's
argument-list checks, and throws `TypeError: Class constructor C cannot be
invoked without 'new'` with the runtime class name, as Node does. Before,
TypeScript reported SC2020 at compile time and JavaScript threw the
standard-library fence's Error at run time. This covers ES5 constructors that
call a class as their base (`function Sub() { Base.call(this); }`).

Runtime-provided classes (Error, EventEmitter, streams, EventTarget), classes
with runtime statics, spread arguments, and receivers other than a name,
`this` or a namespace member keep their existing lowering.
2026-10-22 19:36:04 -05:00
Chris Tate 2442872d62 Version charAt loops and compare characters as code units (#1889) 2026-10-22 19:28:24 -05:00
Chris Tate ba8889104a Free arrays whose elements need no release without a teardown (#1890) 2026-10-22 19:27:12 -05:00
Chris Tate c38792f5f3 Keep the unreached JavaScript coverage fixture failing to compile (#1891) 2026-10-22 19:25:07 -05:00
Chris Tate 36209f6d17 Keep the unreached JavaScript coverage fixture failing to compile
The fixture's unreached statement pushed values onto `{ items: [] }`, which
mapped to a `number[]` and could not hold them. Since #1881 a JavaScript
`[]` literal field is a checked-dynamic array, so the push compiles and the
test "a failed unreached JavaScript statement is always explained" found no
failed statement. The fixture now starts `items` as `[0]`, a `number[]`, so
the push of a boolean onto it still does not compile and reports SC1090 at
the same line.
2026-10-22 19:24:07 -05:00
Chris Tate d4a7b82dd8 Free arrays whose elements need no release without a teardown
When the last reference to an array dropped, the runtime ran the generic
teardown: scr_rc_destroy's depth bookkeeping, an indirect call to the array
destroy, and a loop over the element slots. Arrays of numbers, booleans and
compact ProgramLifetime references release nothing, and neither do arrays of
other ProgramLifetime instances, whose release does nothing.

- scr_arr_release frees an acyclic array without an ext directly when its
  elements are numbers, booleans or compact references, or its descriptor
  has SCR_ARR_OPS_RELEASE_NOTHING. Every other array keeps the full
  teardown.
- The backend sets SCR_ARR_OPS_RELEASE_NOTHING in the descriptor's flags
  word on 64-bit targets for ProgramLifetime element types that are not
  compact, plain or as the pointer arm of a nullable union, in programs
  without lifetime regions. Registration copies it into a table indexed by
  the descriptor's registry id before the id is published. Descriptors
  without the flag read 0 and keep the full teardown.

Corpus 7401 drops arrays of ProgramLifetime instances, numbers and booleans
through every array operation, with holes and far indices, next to arrays of
ordinary objects, and matches Node.
2026-10-22 19:22:55 -05:00
Chris Tate 93d2797bac Version charAt loops and compare characters as code units
`for (let i = 0; i < s.length; i++) if (s[i] === " ") n++` and the same
loop over `s.charAt(i)` got no loop version: `s[i]` lowers to charAt,
and the version covered only charCodeAt reads. Each character paid the
inline helper's ASCII and bounds tests and kept its opaque fallback.
`const c = s[i]; if (c >= "a" && c <= "z") n++` also built the
character string and compared strings twice.

- The typed-array loop version covers `s.charAt(index)` like
  `s.charCodeAt(index)`, and a counted loop that reads either at its
  counter takes an integer counter. In the fast copy, a charAt used as a
  string is the immortal one-character string for its byte, and the
  equality and switch rewrites to charCodeAt read the byte.
- A comparison of `s.charAt(i)` with a one-unit literal compares the
  code unit, as the equality does. An index out of range reads "",
  which is below every one-unit literal; its unit read is NaN, which the
  unordered predicates count as below.
- A const string local initialized from a one-argument charAt and used
  only in one-unit equalities, comparisons and switches holds the code
  unit instead (char-unit-locals.ts).
2026-10-22 19:22:20 -05:00
Chris Tate 42d7d9a0c2 Revert "Enumerate each record in the key order its object literal wrote" (#1888) 2026-10-22 19:04:00 -05:00
Chris Tate f5f0af3203 Inline string releases in release builds (#1887) 2026-10-22 19:03:12 -05:00
Chris Tate 3024e57eeb Revert "Enumerate each record in the key order its object literal wrote (#1802)" 2026-10-22 19:02:41 -05:00
Chris Tate bc6a824b55 Inline string releases in release builds
Release builds called scr_str_release for every string release, so a record update by object spread paid a call for each string field it dropped. A string release now uses the emitted fast-path helper, as string retains do; in release builds a function with more than 8 string release sites keeps the runtime call at each, so string-heavy builders and module initializers grow little. Speed builds are unchanged.
2026-10-22 18:59:44 -05:00
Chris Tate 22d136e6ac Read records by keyof keys when their fields differ in type (#1883) 2026-10-22 18:54:16 -05:00
Chris Tate 575bd9485b Copy unaligned AST word tables in one block (#1884) 2026-10-22 18:50:32 -05:00
Chris Tate e6317b3a88 Support construct, writev, decodeStrings and defaultEncoding on streams (#1842) 2026-10-22 18:48:26 -05:00