Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate dd2e53f00c Look up a number Map key on its integer view
A Map or Set operation on a number key that the emitter knows is an
integer (exactInteger: an integer view, a range proof or a literal) now
looks the key up through sc_map_entry_f64_int, which tests the map's
direct index with an unsigned compare of that integer. Before, the
lookup converted the double back to an integer and compared it with the
original to prove it integral. The hashed probe still takes the double,
so hashing and SameValueZero are unchanged.
2026-10-24 21:40:50 -05:00
Chris Tate 1553247279 Reuse symbol storage keys and checked-dynamic symbol boxes (#2165) 2026-10-24 21:28:36 -05:00
Chris Tate 8a93b888d6 Run the three.js nodes fixture unsanitized in the sanitized lane (#2164) 2026-10-24 21:26:08 -05:00
Chris Tate 79c4984bed Initialize a deferred module binding that a reached body assigns (#2166) 2026-10-24 21:24:43 -05:00
Chris Tate 492d1558be Complete consecutive synchronous writes in one afterWrite step
Node's onwrite queues one afterWrite nextTick for a synchronous write
completion and lets later synchronous completions with the same callback
join it (afterWriteTickInfo.count); writes without a callback share one.
The step then checks 'drain' once, calls the callback once per write and
runs finishMaybe once. The runtime queued a stream tick and a nextTick
entry per write, which ran the callbacks of later writes after nextTicks
queued between them. The queued afterWrite tick now counts the writes
that join it, as Node's does.
2026-10-24 21:22:54 -05:00
Chris Tate 031c6172f9 Emit serialized IR with the unwinding ABI in the serialized IR emission test
compile() emits textual LLVM on Linux with the unwinding ABI since #2163,
so the test's emission from serialized IR passes the same option.
2026-10-24 21:20:10 -05:00
Chris Tate 5f602ff3b8 Decode asserted optional typed arrays other than Uint8Array
`decoder.decode(view!)` on a `Uint16Array | undefined` (or another typed
array that may be undefined) was refused with SC2020: the assertion keeps
the undefined arm for decode, and the decode input only read the
undefined arm of `Uint8Array | undefined`. The undefined arm of any typed
array that may be undefined now reads as an empty array of the same type,
so a defined input decodes its bytes and an undefined one decodes no
bytes, as in Node.
2026-10-24 21:16:59 -05:00
Chris Tate 8f6f8929dd Merge main 2026-10-24 21:15:49 -05:00
Chris Tate 4e9a4311b0 Prove a typed-array loop's views disjoint and give each its own alias scope
A loop that reads one typed array and writes another kept every element
in memory: separate views can share a buffer, so LLVM had to assume a
store to one could change the other's elements. A loop of eight lanes
held in Float64Arrays (zr[k], zi[k], tr[k], ti[k]) ran as scalar loads
and stores of each lane on every step.

When a typed-array loop version covers at least two views and writes at
least one of them, its guard now also requires every pair of the views'
byte ranges to be disjoint: one ends at or before the other starts. In
the fast copy, each view's element reads and writes carry an alias scope
of their own and no-alias with the others' scopes, so LLVM keeps the
elements in registers and vectorizes across lanes. Views that share
bytes, or one array passed twice, take the checked copy as before. The
scopes are string-named, written inline, and named after the function
and the version, so every copy of a loop has its own. Worker programs
scope only views the guard proves thread-owned.

Corpus 8182 runs such loops over disjoint, adjacent, overlapping and
identical views, views of different element types over one buffer, and
empty views.
2026-10-24 21:15:01 -05:00
Chris Tate 90f0dd3dc5 Merge main 2026-10-24 21:09:40 -05:00
Chris Tate eeb847094e Merge main 2026-10-24 21:05:55 -05:00
Chris Tate ab93f728ed Initialize a deferred module binding that a reached body assigns
A module binding whose initializer only allocates data (a function, an
arrow function, a literal or a registry object) is initialized only when
a reached body uses the binding. Only reads and updates counted as uses,
so a binding that reached code only assigned was never initialized, and
the assignment, which checks that the binding is initialized and
releases its value, threw "Cannot access '<name>' before
initialization" where Node runs. Assignments now count as uses.

Corpus 8320 assigns two such bindings, one through an assignment
expression, without reading them.
2026-10-24 20:53:18 -05:00
Chris Tate 0eb7cc12e5 Reuse symbol storage keys and checked-dynamic symbol boxes
Every read or write of a symbol-keyed property on a checked-dynamic value
formatted the symbol's address into a new string to find its storage key,
and every conversion of a symbol to a checked value allocated a new box.
Programs that key hot properties by symbols, such as Effect's type ids
checked on each combinator, spent a large share of their time there.

The storage key is a function of the address, so a 256-entry per-thread
cache by address now returns the existing string, released at exit. A
symbol also keeps a weak pointer to its live box: conversions share it,
and the box clears the pointer when it is freed.
2026-10-24 20:51:37 -05:00
Chris Tate c3e266bc83 Merge main 2026-10-24 20:48:06 -05:00
Chris Tate e73dbe0bb3 Run the three.js nodes fixture unsanitized in the sanitized lane
The sanitized nodes build compiles one 1.14 GB LLVM module, and its clang
compile peaks at about 16 GB, more than the CI runners have, so the
fixture failed in every san shard that ran it. It now builds without the
sanitizer there, as in the plain lane; every other three.js fixture keeps
it. The fixture goes back under the sanitizer once sanitized dev builds
compile large programs in shards.
2026-10-24 20:41:45 -05:00
Chris Tate e3bda96fd1 Merge main 2026-10-24 20:31:45 -05:00
Chris Tate 03af6fb9ff Iterate classes with object-literal iterators and close them on early exits (#1860) 2026-10-24 20:30:36 -05:00
Chris Tate f1bc74f934 Give the module namespace handle its own tag (#2161) 2026-10-24 20:29:29 -05:00
Chris Tate 120c65bfbb Read an iterator's next once, and await return()'s value in for await
JavaScript reads an iterator's `next` when the iterator opens and keeps
calling that function, so a `next` the iterator assigns while it runs is
not called. The for-of loop and the spread drain over an object-literal
iterator read the field on every step; they now read it once after the
open (classIteratorNextRead) and call the function they read. The
checked-dynamic spread drain (%iterator.pack.checked) does the same,
using the stepping the for-of and Array.from drains already use.

`for await` over a synchronous iterator closes it on an early exit the
way the async-from-sync iterator does: return()'s result is read for done
and value, and the value is awaited, so a pending promise settles before
the loop continues and a rejected one rejects the loop (after a throw from
the body, the body's error still wins).
2026-10-24 20:24:11 -05:00
Chris Tate e13dfe1ba1 Write slices stored in records that are only read into stack buffers (#2157) 2026-10-24 20:23:12 -05:00
Chris Tate e6a8d735ca Bound strict throttle state and evaluate MIME arguments before the call
A strict throttler kept every invocation timestamp until its whole window
emptied, so sustained traffic grew the array without bound; ticks that left
the window now give their slots back, and at most limit ticks are live.

new MIMEType, new MIMEParams and MIMEType.parse in JavaScript evaluated
surplus arguments after the call. All arguments now evaluate first, in
order, as in JavaScript, so a surplus argument's effects happen before the
constructor coerces or throws.
2026-10-24 20:20:39 -05:00
Chris Tate 320a97bf3b Throw Node's TypeError for util.diff of two distinct empty sequences
util.diff([], []) with two arrays read one element past its trace row
while backtracking. Node's myersDiff answers undefined for two empty
sequences, and util.diff's ArrayPrototypeReverse throws "Cannot convert
undefined or null to object"; scr_util_diff now throws the same TypeError
before it allocates the trace.
2026-10-24 20:19:50 -05:00
Chris Tate 4b1a6b01d1 Construct through arrays of class values that mix a class and its subclasses (#1868) 2026-10-24 20:18:35 -05:00
Chris Tate bf6bc12056 Represent record-only unions used as Map keys and Set elements as record pointers (#2160) 2026-10-24 20:17:11 -05:00
Chris Tate 304612b7e2 Inline destructured literal results and read ASCII slice ranges inline (#2151) 2026-10-24 20:11:44 -05:00
Chris Tate d64f18d2a8 Evaluate constructor arguments before the class-value check
`new kinds[i](args)` over an array of class values read the element and
narrowed it to the class value before the arguments ran, so an index past
the end threw "is not a constructor" without evaluating them. JavaScript
evaluates the arguments first: the construction is now a call of a lifted
helper (checkedElementConstruction) that takes the element read and the
arguments, in that order, then runs the check and constructs. Keeping the
arguments in one call also keeps a spread's argument pack alive until
every argument has read it.

A checked class-value boundary accepted every subclass's class value into a
base class's slot, so construction through the slot could call a subclass
constructor that takes different arguments with the base constructor's. It
now accepts only the subclasses whose class-value constructor takes the
same arguments (classValueAbiEquals, the rule the classval upcast validator
already applied); any other subclass fails the check with a TypeError.
2026-10-24 20:11:25 -05:00
Chris Tate 1547fd47cd Keep record factory parameters that a closure in the record captures
findRecordFieldProducers counted only direct reads of a parameter, so a
factory such as `(text) => ({ text, upper: () => text.toUpperCase() })`
counted as storing `text` only in its field. A caller's slice passed to
it was written into a stack buffer, and the closure kept by the caller
then read whatever the next iteration wrote there. Captures by closures
and classes now count as reads, so such a factory is not a producer.
2026-10-24 20:10:29 -05:00
Chris Tate 43edb72447 Emit unwinding code for textual LLVM and assembly output on Linux (#2163) 2026-10-24 20:09:47 -05:00
Chris Tate c091adc0e4 Build symbol-keyed fields into frozen class views whatever their attributes
A JavaScript class whose instances track which fields they have copies
the instance's property bag into its view first, attributes included,
and then wrote each symbol-keyed field with an ordinary assignment. Once
the instance was frozen, its symbol entry was read-only and the copy
non-extensible, so any later reflection that rebuilt the view
(Object.isFrozen, a property read) threw "Cannot assign to a read only
symbol property".

String-keyed fields already use scr_dyn_obj_set_field, which replaces a
field's value whatever the attributes copied into the view. The new
scr_dyn_symbol_field_set does the same for symbol-keyed fields, and the
view uses it when it copies the bag or carries integrity attributes.
2026-10-24 20:09:28 -05:00
Chris Tate 8032e6c6c6 Free frozen records when a shape union drops their last reference
A frozen record's rc word carries SCR_RC_FROZEN beside its count. The
shared shape-union release compared the whole word, so the last reference
to a frozen record took the shared-reference branch: it decremented the
count and never freed the record or its fields. When an arm is freezable,
the release now compares the count without the frozen bit, as the
record's own release does.
2026-10-24 20:08:30 -05:00
Chris Tate e2db1ef344 Hold Reflect.ownKeys's function value in the Reflect namespace object
Reading `ownKeys` from Reflect read as a value (`const r = Reflect;
r.ownKeys`) gave the member function that throws SC1090 when called,
although the spelled `Reflect.ownKeys` read already has a function
value. The namespace object now holds that function value, so calls
through the alias run and `r.ownKeys === Reflect.ownKeys` is true, as in
Node. The spelled read and the namespace member share one lowering
(reflectMemberValue).
2026-10-24 20:07:36 -05:00
Chris Tate fc076258e4 Merge main 2026-10-24 20:07:16 -05:00
Chris Tate e6790def72 Merge main 2026-10-24 20:06:31 -05:00
Chris Tate 50421e1cbd Merge main 2026-10-24 20:05:11 -05:00
Chris Tate 7464f12e6b Emit unwinding code for textual LLVM and assembly output on Linux 2026-10-24 20:01:49 -05:00
Chris Tate e0f1de0a2b Merge main 2026-10-24 20:00:43 -05:00
Chris Tate 1004b07e37 Merge main 2026-10-24 19:59:33 -05:00
Chris Tate afd9c52467 Prove typed array reads in while loops bounded by the length in bounds (#2155) 2026-10-24 19:13:16 -05:00
Chris Tate 89ac800d1c Read the runtime ABI in the runtime pack test fixture from the compiler (#2162) 2026-10-24 19:12:43 -05:00
Chris Tate adeddf8724 Check with a tsc-ts server shared by later builds (opt-in) (#2153) 2026-10-24 19:12:05 -05:00
Chris Tate c41af6c36d Move the MIME handle tags to 81 and 82
Main now uses tag 78 (STRING_DECODER), 79 (MODULE_NAMESPACE, after #2161)
and 80; 81 and 82 are the next free values.
2026-10-24 19:04:38 -05:00
Chris Tate e0db07a46d Merge main 2026-10-24 19:03:30 -05:00
Chris Tate b7491676bb Read the runtime ABI in the runtime pack test fixture from the compiler 2026-10-24 19:01:18 -05:00
Chris Tate 97c3b04f73 Merge main and add the Node 24 util batch 2026-10-24 18:55:14 -05:00
Chris Tate f26cc427c2 Merge main 2026-10-24 18:51:06 -05:00
Chris Tate 76735a8d92 Run private members and super calls over foreign receivers like Node (#1975) 2026-10-24 18:50:35 -05:00
Chris Tate 53bddc6ed7 Give the module namespace handle its own tag 2026-10-24 18:46:36 -05:00
Chris Tate e2d0941e3a Speculate integer loops through pure helper calls, powers of two and byte reads (#2100) 2026-10-24 18:45:59 -05:00
Chris Tate 3121f6909d Evaluate a JavaScript constructorOpt, and keep a non-extensible error's stack
- Error.captureStackTrace in JavaScript discarded a constructorOpt it
  does not recognize (a getter, a call), so its side effects and
  exceptions did not happen. It now evaluates after the target and
  before the capture, as V8 evaluates the call's arguments; the target is
  held in a local when evaluating it twice would not be safe.
- Capturing on an error replaced its saved stack without the checks V8
  makes when it defines `stack`. A frozen, sealed or non-extensible error
  now throws "Cannot define property stack, object is not extensible",
  and one whose `stack` is non-configurable throws "Cannot redefine
  property: stack"; both keep their stack.

Corpus 6874 checks the evaluation order and a constructorOpt that
throws.
2026-10-24 18:43:07 -05:00
Chris Tate d6c09279a7 Compile createRequire probes for optional addons as deferred refusals (#2015) 2026-10-24 18:41:05 -05:00