Commit Graph
5257 Commits
Author SHA1 Message Date
Chris Tate ec79f2aca0 Destructure narrowed and joined union sources, await promise-armed unions, support writeFileSync(fd) (#2466) 2026-10-28 08:57:38 -05:00
Chris Tate 1ffc738c58 Keep undefined out of the shape overlay's set of constructed functions (#2464) 2026-10-28 08:12:42 -05:00
Malte UblandClaude Opus 5.5 071be693d9 Support writeFileSync(fd, data)
Node's writeFileSync accepts an open file descriptor where it accepts a
path: it writes the data at the descriptor's current offset and leaves the
descriptor open, since closeSync owns its lifetime. The static fs surface
declared only the path forms, so `fs.writeFileSync(fd, text)` failed type
mapping — the compiler was missing a documented overload, not a
conversion.

Declare the (fd, data) and (fd, data, "utf8" | "utf-8") forms in the
node-fallback typings, route the call by the first argument's static f64
type to a new fs.writeFileSyncFd libfn, and implement it as
scr_fs_write_file_fd: a write loop over the whole string through the
descriptor-validating scr_fs_write_bytes, resuming short writes, with the
descriptor operation's errno shape on failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:00 -07:00
Malte UblandClaude Opus 5.5 99ebf7501d Await a union whose promise arm settles a union
`Promise<A | B> | undefined` awaits as Awaited of the operand, which the
maybe-promise lowering already computes: it awaits the narrowed promise
arm and coerces its settled union, and the non-promise arms take JS's one
microtask hop. The await lowering refused any union inner instead, so the
promise-or-unit shape now goes through that same path; a union whose
promise arm has no combined result keeps the SC1090 fence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:59 -07:00
Malte UblandClaude Opus 5.5 a0a6910e4e Destructure union sources by narrowing first and joining arm reads
An object pattern over a union whose arms type a field differently, or do
not declare it at all, was refused with SC1031, even though the pattern
element's own type is the checker's answer: the join of the arms' declared
types, with undefined for an arm that omits the key, or the narrowed arm
when a discriminant test or an `in` guard refined the source at the
pattern's site.

Keep the unionDisc fast path wherever every arm declares the field with
one type, so programs that compile today emit identical IR. Otherwise read
through the existing keyed join (unionKeyGet): each arm answers its
declared type and a record arm that does not declare the key answers
undefined, which is exactly the desugar's own property read. Only
destructuring passes the new missingFieldAsUndefined flag, so dotted reads
keep their current refusals.

Narrow first when the un-narrowed pattern does not fit: bind the
checker-narrowed view of the source to a hidden local — a trusting
arm extraction for one arm, a sub-union re-tag whose excluded arms trap,
and the stored layout view for an intersection refinement — and lower the
pattern over that view.

A rest element over a union source packs each arm's unconsumed fields into
a fresh record of the checker's arm-wise rest type, selected by the
runtime tag; CopyDataProperties makes a fresh object too, so nothing
aliases the source. Shapes that do not spell the rest type still fence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:58 -07:00
Chris Tate e2b95cdee8 Run short runs of small innermost worker loops without a poll (#2463) 2026-10-28 06:52:19 -05:00
Chris Tate 8b27e9cd2f Name program modules by the entry's file name so profiles match in every checkout (#2462) 2026-10-28 06:43:48 -05:00
Chris Tate 35eda071b2 Give JavaScript fetch bodies the members of a web ReadableStream (#2459) 2026-10-28 06:41:21 -05:00
Chris Tate 3134642f19 Keep undefined out of the shape overlay's set of constructed functions
A union that is a Set element or a Map key is stored as a union box, and
a union type has one representation in a program. The overlay's
`Set<ts.Node | undefined>` made the native compiler box every
`ts.Node | undefined` value, including each optional child a syntax node
returns. The set now holds the defined nodes only.
2026-10-28 06:40:50 -05:00
Chris Tate 74a131bbcc Run short runs of small innermost worker loops without a poll 2026-10-28 06:14:20 -05:00
Chris Tate 0bf0f69dc3 Balance program partitions by size (#2461) 2026-10-28 06:10:55 -05:00
Chris Tate 23d95a99fd Name program modules by the entry's file name so profiles match in every checkout
Each partition's source file name was the program entry's absolute path,
and profiles name a module's local functions after it. Since programs keep
partition-local functions internal (#2449), a profile collected in one
directory missed those functions in builds from any other: a native
compiler profile collected on main named 27% of its records, holding 2.2%
of its counts, after the checkout's path. Modules now carry the entry's
file name. The partition cache key still includes the full path.
2026-10-28 05:44:38 -05:00
Chris Tate 98f5926119 Update bodyUsed when a body's web view starts a read, and link the view for untyped uses
After a fetch body's handle gained its web view, a read, cancel or pipe
started on the view left Response.bodyUsed false until the view's pull
reached the body, where Node's bodyUsed reads the stream's disturbed state
at once. bodyUsed now also answers the view's disturbed state, which the
web streams' bridge reports (ScrWebStreamBridge.disturbed).

A program that names none of the web stream classes linked them only for
member accesses the checker resolves to ReadableStream, so body.tee() or
for await over a body inside a function whose parameters have no type
threw a TypeError. The same member names on an untyped receiver, and
for await over an untyped value, now link the classes in a program that
links the fetch unit.

Adds corpus program fetch-body-web-stream-helpers.mjs, with its corpus
cost and preflight/order baseline, and bodyUsed cases to
fetch-body-web-stream-linked.mjs.
2026-10-28 05:29:42 -05:00
Chris Tate 007a144cb6 Balance program partitions by size 2026-10-28 05:12:02 -05:00
Chris Tate 3bf316de6b Annotate only mismatched functions from block counts
A block-count file annotated every function the profile left without
counts, including functions the profile has no record of. The recovered
count could then exceed the mismatched functions it annotated and hide the
stale-profile warning for mismatched functions left without counts. Only
functions the profile annotation tagged as mismatched are annotated now, so
the recovered count stays among the mismatched ones.
2026-10-28 05:10:57 -05:00
Chris Tate d9481023c3 Annotate functions a stale profile misses from its block counts
A profile annotates a function only while its control flow hashes as it did
when the profile was collected, so compiler and source changes leave the
changed functions without counts. A --profile-use build now reads a
block-count file beside the profile (app.blocks beside app.profdata). Right
after the annotation before inlining, the helper aligns each uncounted
function's blocks with its recorded blocks by label kind and successor
count. Paired blocks take their recorded counts and their edges the recorded
edge counts, by target or, between targets whose labels changed, by
position. LLVM's flow inference fills in the rest, avoiding the edges the
function's own llvm.expect weights make unlikely. The helper then sets the
entry count, branch weights and hot or cold attribute as the profile
annotation does.

SCRIPTC_RECORD_PROFILE_BLOCKS names the file a --profile-use build writes
for the functions its profile annotates; such a build reuses no cached
artifacts. The block file keys every cache, the build report counts the
functions it annotated, and the staleness warning counts only the functions
left without counts. A single-module build with a context-sensitive profile
no longer counts annotated, mismatched and missing functions a second time
after inlining. scripts/refresh-cli-profile.mjs records the file in the
second phase and build-native-cli.mts reads it beside the profile.
2026-10-28 04:50:01 -05:00
Chris Tate d6bb226f48 Pass numbers to directly called untyped functions as jsvals (#2458) 2026-10-28 03:59:31 -05:00
Chris Tate 0325b921aa Compile pre-module constructor functions as classes and namespace functions as direct functions (#2175) 2026-10-28 03:57:06 -05:00
Chris Tate e85af20b7e Defer evaluating Node island modules until the program uses them (NI-7) (#1901) 2026-10-28 03:53:45 -05:00
Chris Tate 17efed329f Give JavaScript fetch bodies the members of a web ReadableStream
In JavaScript, a Response or Request body answered getReader, cancel and
locked, and every other ReadableStream member was undefined: tee,
pipeTo, pipeThrough, values and for await threw TypeErrors where Node's
body is a ReadableStream.

In programs that link the web stream classes, the body's handle now
stands for a ReadableStream once any other member is used
(sf_stream_web_view): a stream with a zero high-water mark whose native
source reads the body (scr_webstream_readable_over). From then on every
member goes to it, the class's method values and getters run on it
through the alias's view (scr_dyn_handle_class_receiver), and text(),
clone(), new Response(body) and fetch uploads read the body through it,
so its lock and bodyUsed follow it as in Node. A body locked when the
view is made keeps the view locked until the lock is released. Once
only the view's source holds the body, the body lets the view go.

The compiler links the classes into a program whose JavaScript reads a
ReadableStream member that the fetch profile lists as unsupported by the
native stream, or iterates a ReadableStream with for await, and that
names none of the classes: %main calls webstream.install first.
Programs without web streams build to the same binaries as before.

Adds corpus programs fetch-body-web-stream-linked.mjs,
fetch-body-web-stream-unlinked.mjs and fetch-body-web-stream-network.mjs,
with their corpus costs and preflight/order baselines.
2026-10-28 03:44:57 -05:00
Chris Tate ba3e160f69 Keep union locals copied between locals in private storage (#2457) 2026-10-28 03:08:49 -05:00
Chris Tate 98c178d093 Check the stores of for-in and for-of heads before converting a constructor
A `for (k in o)` or `for (k of a)` whose head is an expression stores into
it on every iteration. The strict-mode check of constructor and method
bodies now treats such a head like the target of `=`: a property store
not made through `this`, a store into a read-only global, and a store into
an undeclared name keep the constructor function, since sloppy mode drops
or completes those stores where a class body throws.

The static-objects-order fixture gains loops.js, whose methods store from
a for-in head into an undeclared name and from a for-of head into a
property of a string.
2026-10-28 03:08:04 -05:00
Chris Tate c9c8224aa2 Merge main 2026-10-28 02:45:59 -05:00
Chris Tate e8fdfa6a72 Pass native closures, promises and objects into Node islands by reference (NI-5) (#1900) 2026-10-28 02:40:19 -05:00
Chris Tate 722c449ae8 Raise through cached unwind plans on x86-64 Linux (#2455) 2026-10-28 02:22:18 -05:00
Chris Tate 33272914de Pass numbers to directly called untyped functions as jsvals 2026-10-28 02:20:29 -05:00
Chris Tate 98006639b7 Read the receiver first when an element write's index can rebind it
An element write of a `T | undefined` binding holds a computed index in a
temporary ahead of its two stores, which then read the receiver after the
index. When the index can rebind the receiver (`a[(a = b, 0)] = u`), the
write now keeps reading the receiver first, so it stores into the array
the receiver held before the index ran.

The key conversion of a union index (a sequence that declares a temporary
and may read the constant "undefined" key) no longer counts as running
other code, so such an index is still held.
2026-10-28 02:19:44 -05:00
Chris Tate 0cf5e87b03 Keep union locals copied between locals in private storage
A union local that is copied to another local (`const t = pp[p0]; p0 = t`)
was a whole-value use, so both locals held heap boxes: every number[]
read held in such a local allocated a `%ScrUnion`, and every copy
retained and released it.

Call lifetimes now record statement copies between union locals
(copiedLocals) instead of rejecting the source. Local union storage
takes a copied local when every local it is copied to also has private
storage, so the copy snapshots the tag and bits and the source's box
stays private. Copies in expression position and copies of parameters
keep the heap path.

An element assignment of a `T | undefined` binding with a computed index
holds the index in one temporary ahead of its two stores, so the key
conversion of a union index declares its temporary once.
2026-10-28 01:56:14 -05:00
Chris Tate 195c38e671 Declare scr_unwind_resume in the runtime header
The backend declares scr_unwind_resume, so scr_exception.h carries its
prototype, as the runtime ABI test requires. The unwind-ABI pattern names
both entry points in full, so the test reads no partial symbol from it.
2026-10-28 01:26:22 -05:00
Chris Tate d3e8ab41ef Publish unwind plans without a plain write to their pc
A new plan was copied into its slot as a whole struct, which wrote the
slot's pc with a plain store while other threads' lookups read it
atomically: a data race. The insert now copies the fields after pc and
publishes pc only with the release store.

The unwinder test also runs its threaded cases under ThreadSanitizer,
which reports the race without this change.
2026-10-28 00:36:18 -05:00
Chris Tate 5ef693cb89 Keep the promise identity keys in the Node island build of the async unit 2026-10-28 00:34:57 -05:00
Chris Tate 30d611e9da Read a stream body to its end when its queue filled before the read
text(), json(), bytes() and the other body readers of a Response or
Request whose body is a ReadableStream asked the stream's source for more
chunks after draining its queue only when the stream had a zero
high-water mark. When the queue had filled before the read began (a timer
ran after the Response was made), the reader took the queued chunk and
then waited for a close that no pull would bring, and the read never
settled. The reader now pulls whenever it empties the queue, so it reads
to the end, as Node does. This applies to streams from ReadableStream.from,
from an underlying source, and to JavaScript ReadableStreams.

Adds corpus programs fetch-body-queued-before-read.ts and
fetch-body-queued-before-read-untyped.mjs, with their corpus costs and
preflight/order baselines.
2026-10-28 00:23:51 -05:00
Chris Tate d5ac31f812 Raise through cached unwind plans on x86-64 Linux
scr_unwind_raise walks the stack with plans cached per return address.
The first raise through a return address finds the function's FDE
through the .eh_frame_hdr of the object that contains it, runs the CFI
up to the call, and finds the call site's landing pad in the LSDA; the
plan records the frame's CFA rule, where its callee-saved registers are
saved, and the landing pad. Later raises look the plans up, restore the
registers, and jump to the first landing pad. Plans are inserted under
a lock and read without one.

Landing pads continue the unwind with scr_unwind_resume instead of
`resume`, so the walk goes on from their frame. A frame whose CFI,
pointer encodings or personality the plans do not express, or a return
address without an FDE, leaves the unwind to libgcc's forced unwind
from the frame the walk started at, as before. Other Linux targets keep
libgcc's unwinder; their scr_unwind_resume calls _Unwind_Resume.

bench:runtime gains layered-errors, a workload whose errors are thrown
below small functions that hold references and caught per entry in a
loop, and the corpus gains unwind-landing-pad-chains, which pins landing
pads that continue the unwind, including in frames LLVM inlines into a
caller whose handler takes the exception.
2026-10-28 00:22:04 -05:00
Chris Tate 04e5e1c92e Weigh the runtime profile's staleness by its counts (#2454) 2026-10-28 00:12:28 -05:00
Chris Tate 68ddd68210 Keep the elements of InlineArray fields in their instances (#2380) 2026-10-27 23:55:57 -05:00
Chris Tate e3f079e12b Give integer clones borrowed records and worker programs (#2450) 2026-10-27 23:50:34 -05:00
Chris Tate 5ec343312a Keep process's emitter methods as Node shows them in Node islands (#2452) 2026-10-27 23:22:26 -05:00
Chris Tate 1278729267 Merge main 2026-10-27 23:21:09 -05:00
Chris Tate d47df7c9c6 Weigh the runtime profile's staleness by its counts
The runtime-unit helper now lists the functions the profile annotated and
the ones whose control flow changed since it was collected, each with the
sum of its counters in the profile. The Linux x64 pack manifest records the
counts on matched and on changed functions (flavors.speed.runtime_profile
annotated_counts and mismatched_counts) and names the changed functions
with the most counts. A function that one unit variant matches counts as
matched in the other variant too.

The runtime profile test fails when changed functions hold more than 5% of
the counts. RELEASING.md adds refreshing the runtime profile to the release
steps.
2026-10-27 23:20:02 -05:00
Chris Tate d03c4b9b57 Merge main 2026-10-27 23:16:08 -05:00
Chris Tate 82ba2ef6ed Merge main 2026-10-27 23:02:33 -05:00
Chris Tate 18a47fedd5 Run npm packages in Node islands: object handles and compiler routing (NI-4) (#1899) 2026-10-27 22:42:29 -05:00
Chris Tate 08c7b72c2d Keep process's emitter methods as Node shows them in Node islands
The island host replaced process.on, process.addListener and
process.prependListener with separate wrappers, and process.emit with its
own, as own enumerable properties of process. Island code could see the
difference: Object.keys(process) listed the four, process.on was no
longer process.addListener, prependListener was named addListener, and
each was an own property where Node inherits it.

One wrapper now serves on and addListener, and prependListener and emit
keep their names. They are defined on process's own prototype, which
Node gives process below EventEmitter.prototype, with
EventEmitter.prototype's attributes. process has no own property for
them, so Object.keys, Object.hasOwn and for...in see what they see in
Node. for...in lists a prototype's keys before its parent's, so the
keys EventEmitter.prototype lists before the wrapped ones (_events,
_eventsCount, _maxListeners, setMaxListeners, getMaxListeners) are
defined there too, with the same values, in their order.

6891 compares the identities, names, lengths, own-ness, Object.keys and
for...in over process against Node.
2026-10-27 22:33:01 -05:00
Chris Tate 590ed5ecff Keep emission's workers busy (#2451) 2026-10-27 22:27:48 -05:00
Chris Tate 93081ea679 Host the runtime's event loop on Node's libuv once Node loads (NI-3) (#1696) 2026-10-27 22:19:38 -05:00
Chris Tate d5c215c684 Merge main 2026-10-27 22:03:19 -05:00
Chris Tate 506683f16b Distribute conditional members over a generic body's bound parameter (#2258) 2026-10-27 21:56:52 -05:00
Chris Tate 9df673b23e Keep unloaded packages out of import() graphs and reject those imports (#1526) 2026-10-27 21:53:19 -05:00
Chris Tate 9e91b7d912 Merge main 2026-10-27 21:44:40 -05:00
Chris Tate 7f27866add Keep emission's workers busy 2026-10-27 21:43:36 -05:00