Commit Graph
4679 Commits
Author SHA1 Message Date
Chris Tate 15a183026f Allocate the shared two-byte characters from the system allocator
The one-character strings of U+0080..U+07FF that string iteration shares
between threads were allocated from the calling thread's memory. On Linux a
worker that exits without sharing anything returns that memory, so the table
kept strings that a later read found discarded. They now come from the system
allocator, as other state that outlives a thread does.
2026-10-25 13:27:58 -05:00
Chris Tate 1b9a4e1361 Refuse SC9001 cases and merge main 2026-10-25 13:26:27 -05:00
Chris Tate 2a0489a334 Keep the TypeError of receivers that may be null in static-object rewrites
The rewrites decide on a read's non-nullish receivers, since the analysis
also gives an absent key's read undefined (ns.util before ns.util = {...}
runs). A rewrite then removed the property access, so a receiver that was
in fact null or undefined no longer threw.

- A read whose receiver may also be nullish is rewritten to the read as
  written followed by the replacement in a comma expression: a direct
  reference becomes (recv.k, name) and a folded member (recv.k, literal),
  so a nullish receiver throws Node's TypeError and any other receiver
  yields the same value.
- A function table call through such a table reads table[key] first,
  before its arguments, where the call reads it.
- The static-tables case reads, calls and dispatches through receivers
  that are the constant object or undefined, and records the ts7
  baselines of the npm cases added in this change.
2026-10-25 13:23:54 -05:00
Chris Tate ea599922a4 Iterate and read native arrays from untyped code without type-name scans
Untyped code that loops over a native array (`for (const ch of checks)` in
zod, over arrays the compiled code built) paid for the loop before its
first element:

- Every check of whether a typed capsule is a generator compared its type
  name against "async-generator<" with strncmp, and native array type
  names also start with 'a'. Iterator creation made several such checks.
  The test now looks at the second byte before comparing.
- scr_dyn_iterator called the builtin values() through the general call
  path for a native array; it now creates the native iterator directly,
  as it already did for untyped arrays.
- Read sites of `length` and `next` call scr_js_get_probe_native, which
  answers a native array's `length` and a native iterator's `next`, where
  the generic read built the array's checked view or looked the method up
  by name. Other read sites keep scr_js_get_probe, so their receivers that
  are not ordinary objects still miss without a further test.
2026-10-25 13:23:22 -05:00
Malte UblandClaude Opus 5.5 bd1bf39a76 Close recursive record knots through erased generic method members
A record or union that refers back to itself only closes its recursive
knot when the frame did not resolve anything through context-sensitive
hooks; otherwise the same checker type could answer differently per
instantiation, so the knot is refused. Erasing a generic method member
(`send<T>(message: Message<T>): string`) resolves the method's own type
parameters to `unknown`, and that resolution counted as context-sensitive.
Any recursive interface declaring a generic method was therefore refused:
`interface Sub extends Base { send<T>(m: Message<T>): string }` reached
from `Base { child?: Sub }`, or a list node with a generic method beside
its `next` field.

The member erasure now records its own type parameters, and resolving one
of them no longer counts. Consulting an enclosing binding still does, so a
generic method that mentions an outer type parameter keeps the fence. The
erased member's mapping is not memoized under the member's type, since the
same type maps to nothing outside the erasure.

pi's ReplacedSessionContext (sendMessage<T>) extends ExtensionCommandContext,
whose methods take callbacks over ReplacedSessionContext. The fence refused
ExtensionAPI.registerCommand and, through it, pi's main().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 11:18:18 -07:00
Chris Tate fb17fc0715 Read fixed encrypted keys in corpus 8167's wrong-passphrase cases
The cases that decrypt with a wrong passphrase used keys encrypted
afresh on each run. A wrong key decrypts to random bytes, and about one
run in two hundred those bytes pass the padding check, so OpenSSL fails
at the ASN.1 step with a longer error than "bad decrypt" and the
program's output changes. The section that reads encrypted keys now
uses fixed encryptions of one P-256 key, so each case fails the same
way on every run.
2026-10-25 13:15:28 -05:00
Chris Tate e8a64267cb Call toJSON on foreign elements and merge main 2026-10-25 13:13:02 -05:00
Chris Tate 13a9476df9 Fix the latin1 decode bounds and merge main 2026-10-25 13:11:33 -05:00
Malte UblandClaude Opus 5.5 ed50daa853 Keep the whole union of an optional parameter that reaches a recursive placeholder
An optional parameter typed by a union that is still being mapped gets a
recursive placeholder: a union id whose arms arrive when the outer frame
finishes. The function-type mapping then armed that placeholder with
undefined, but the placeholder's arm list was still empty, so the result
was a fresh union holding only `undefined`. The parameter lost its record
arm, the same checker type mapped to two different layouts depending on
where the mapping started, and later conversions failed (SC2001, SC2002,
SC2003).

This happens when an interface method takes an options object whose
callbacks are typed by a subinterface of that same interface:
`newSession(options?: { withSession?: (ctx: Replaced) => Promise<void> })`
with `interface Replaced extends CommandContext`.

When the checker spelled the parameter with an undefined constituent, the
placeholder already carries that arm, so the parameter keeps the
placeholder. withUnitArm now refuses a pending placeholder instead of
building the unit-only union.

pi's ExtensionCommandContext is such an interface. It refused
ExtensionAPI.registerCommand, and with it pi's main().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 11:01:57 -07:00
Chris Tate a88fdacbe2 Iterate strings over their bytes
for-of, spread, Array.from, yield* and [Symbol.iterator]() over a string
walked a UTF-16 index. Each step mapped the index to a byte offset, built
the character, and measured its UTF-16 length to advance.

They now walk a byte cursor over the WTF-8 data:
- The cpAtByte intrinsic reads the character that starts at the cursor.
  ASCII bytes take the one-character table inline; other characters call
  scr_str_cp_at_byte. byteLength advances the cursor by the character's
  byte count. A lone surrogate is its own three-byte character, so it
  stays a one-unit character, as in Node.
- Spread and Array.from of a string call scr_str_code_points, which
  counts the characters, allocates the array once and fills it.
- The one-character strings of U+0080..U+07FF are made immortal on first
  use and shared, as the ASCII ones are static.

The cpAt intrinsic, which no lowering emits any more, is removed.
2026-10-25 13:00:18 -05:00
Chris Tate e24ed12933 Decode string storage within its length in Buffer encodings
String storage can hold bytes that are not well-formed UTF-8: an HTTP
header value keeps the raw bytes a client sent. Encoding such a string
read past its end.

- scr_bytes_next_cp decoded any high byte as the lead of a sequence and
  read up to three bytes after it without a bound. It now takes the
  storage length, decodes only complete, well-formed sequences, and
  returns any other byte as U+0000..U+00FF, advancing by one. That is
  each such byte's value in Node, which decodes header values as latin1.
  This covers latin1, ascii, binary and utf16le encoding, hex and base64
  of non-ASCII text (which narrow through latin1), Buffer#write and
  TextEncoder#encodeInto.
- base64 decoding read one byte past its input when the eight-character
  groups ended exactly at the end.

The runtime's bytes unit test (built with ASan) encodes ill-formed
storage at every offset within a word and random high bytes, and
compares latin1, ascii and utf16le with a scalar reference. A corpus
program sends such header bytes to a server and encodes them as Node
does.
2026-10-25 12:48:21 -05:00
Malte UblandClaude Opus 5.5 c46f70e451 Fix wrong npm declarations locally with type patches
A project can now patch a package's declarations in package.json:
"scriptc": { "typePatches": { "<package>@<range>": { "declarations":
{ "<selector>": "<type>" } } } }. A leading "|" widens the declared type,
any other type replaces it. Selectors name declarations by path
(Type.member, fn(), fn(param), list()[], Env[string], export=(), default,
file.js:local()) and apply to the package's .d.ts, its JSDoc and its
@types twin alike.

Patches edit file text where every consumer reads it: the program host's
source reads (both checker worlds) and the npm-static passes that read
package files themselves. Only type nodes and JSDoc type tags change, and
no edit adds or removes a line. Installed copies are selected by version;
an @types twin by the runtime package's installed version. Patches that do
not apply as written warn (SC8030-SC8033) and the warnings repeat on
executable cache hits. Without the setting nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:27:52 -07:00
Malte UblandClaude Opus 5.5 9cdbc83aee Prefer the union arm that keeps every field of a converted record
A record flowing into a union where several record arms could hold it by a
width conversion was refused with SC2003 as ambiguous, even when one arm
keeps every field of the record and the others hold it only by dropping
fields (a model record whose option union is narrower than the catalog's,
beside a classifier arm with a subset of its fields). That arm is the
value's own layout, widened: it now wins when it is the only one. Plans
that already resolved (a single candidate, the absent-field preference,
discriminant and presence routes) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:07:59 -07:00
Malte UblandClaude Opus 5.5 30e6ca9160 Convert promise and function values into a union's arm of their kind
A promise or function value flowing into a union whose only arm of that
kind has a wider payload or result (`Promise<T>` into `Promise<unknown> |
undefined`, `(s) => Promise<string | null>` into an optional
`(s) => Promise<string | null | undefined>` member) was refused with SC2003:
an arm value wrapped into a union only when the union held an identical
arm. It now takes the arm-level conversion (the checked payload widening,
the function adapter) and wraps the result.

`p === u` against such a union compares both sides as untyped values, so a
promise keeps its identity although converting it into the union's arm
builds a new promise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:07:51 -07:00
Chris Tate 54899bcc10 Leave factories that declare a class out of per-site clones
A class's constructor and methods are analyzed in receiver contexts
rooted at each member. Inside a per-site clone of the function that
declares the class, they resolved that function's bindings in the
default context: their writes through captured variables were lost,
and reads of the written objects folded to stale literals. A function
with a class declaration or expression anywhere inside is no longer
cloned per site.

The static-tables case adds a factory whose class method writes the
factory's object at initialization. It prints Node's `1,1,1` (it
printed `1,1,0`), and the count of constant reads stays at 8. zod3's
report is unchanged: 180 constant objects and 360 foldable reads.
2026-10-25 11:38:58 -05:00
Malte UblandClaude Opus 5.5 25a630a8ca Map intersections of equally represented constituents as that type
An intersection that restates an optional member as required with a
separately written element type (`Opts & { files: Array<{ path: string }> }`
over `files?: Array<{ path: string }>`) gives the member the type
`A[] & B[]`. The checker keeps both element literals, and no intersection
rule mapped an intersection of arrays, so the record was refused (SC2009).
When every constituent maps to one IR type that is not the dynamic value,
the intersection now maps to it. The rule runs only after every other
intersection rule declined, so intersections that already mapped keep their
mapping.

pi's NormalizedBuildSystemPromptOptions is such an intersection; it sits on
the chain from main()'s options to the extension API and refused main().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:37:46 -07:00
Chris Tate 7b24b410aa Merge main 2026-10-25 11:30:45 -05:00
Malte UblandClaude Opus 5.5 541b05ddea Map generic alias casts inside generic bodies with the instance's arguments
A cast to a generic type alias spelled over the enclosing function's own
type parameters (`return x as Catalog<G, P>`) stays symbolic in the
checker inside the body, so a mapped type with key remapping, a template
key filter or a conditional member refused with SC2001 in every instance.

The checker interns alias instantiations, so the body's `Catalog<G, P>` is
the same type the signature declares. Binding unification now records, per
call-keyed instance, each declared generic alias reference together with
the alias the call's resolved signature instantiates. When the symbolic
form does not map, mapType maps that concrete instantiation instead. A body
forwarding its own alias to another generic resolves through the enclosing
instance. Types that already mapped are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:22:06 -07:00
Malte UblandClaude Opus 5.5 5455a4d817 Make the shape overlay's facts sound on the real-package suites
The overlay validation suites (npm, npm-static, three, Effect 3 and 4) now
pass with the overlay on. Each cause had an npm repro or suite case that
failed with the overlay and passed without it:

Analysis facts:
- Symbol members: Symbol.for/keyFor are functions, well-known symbols are
  symbols, other members unknown.
- Reads that may answer undefined: index reads past the end (unless the
  enclosing for loop proves them in bounds of an array that never shrinks or
  has holes), computed reads of objects, keys an object literal lacks at
  creation, optional-chain short circuits, `let x;` before assignment.
- Object.create(proto) inherits proto members; arguments past the declared
  parameters are unknown; `new` of a primitive answers nothing.
- Arithmetic on bigints answers bigints (number|bigint is never printed).

Overlay:
- Package JSDoc is a declaration: it stays as written and the overlay adds
  only types the JSDoc does not declare.
- Objects held by module-level bindings (and what they hold) are never typed
  as records: a typed return would copy them.
- Only checks of overlay facts carry the inferred-fact message.
- static-objects ignores nullish receivers outside optional chains; folded
  literal keys are unparenthesized.

Fallback (the overlay never adds a failure the default build lacks):
- the overlay state is kept per load (a later load reset it, so the
  fallback never ran);
- deferred refusals in unreached code count;
- a preflight, deferred refusals or IR the program without the overlay does
  not have make the build compile without the overlay.

CI: the shape_overlay job builds its own native packages; a manual run with
scope shape_overlay runs it alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:20:03 -07:00
Malte UblandClaude Opus 5.5 34eef83ef4 Decide concat spreading of dynamic values at run time; keep JS lets assigned from index reads unchecked
Three JavaScript lowering bugs threw a checked conversion where Node answers:

- concat with a checked-dynamic argument lowered it as an element, so an
  array argument threw. The argument now spreads when it is an array at run
  time and is appended otherwise.
- `let c; while ((c = str[i++]) !== "]" && c)`: the checker types c from the
  index read, without the undefined a read past the end answers. Evolving-let
  inference ignores index-read writes, and reads of a JS `let x;` statement
  binding keep the actual value.
- Strict equality on a flow-narrowed dynamic JS name compares unchecked.

npm cases concat-dynamic-args and evolving-let-index match Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:19:53 -07:00
Chris Tate 8a22e0f515 Answer net.Socket write() with Node's backpressure boolean, and fire 'drain' (#2198) 2026-10-25 11:04:01 -05:00
Chris Tate e29ea20af1 Address review feedback 2026-10-25 11:02:41 -05:00
Chris Tate eca815da02 Merge main 2026-10-25 10:55:32 -05:00
Chris Tate 18fd46be1c Merge main 2026-10-25 10:43:36 -05:00
Chris Tate 7c0cbce93a Address review feedback 2026-10-25 10:42:26 -05:00
Chris Tate 44b3efcb01 Read a typed array's length without an invariant header load (#2225) 2026-10-25 10:38:13 -05:00
Chris Tate e87dfe9398 Keep captured bindings and bound receivers in per-site clones
Two cases where a per-site clone lost writes, so later reads of the
written objects folded to their initial literals:

- A function walked in an enclosing clone's or receiver's context is
  no longer cloned per site. The clone was its own context root, so the
  variables it captures resolved to the default context's bindings, and
  its writes through them reached no object.
- A clone's receiver now receives what the function's own receiver
  does, such as the object `bind` supplies, and an arrow's clone keeps
  the arrow's lexical `this`. Before, a bound function called at module
  initialization wrote through a fresh, empty receiver.

The static-tables case writes at initialization through a helper's
captured binding and through a bound function. It prints Node's `1,1`
(it printed `0,0`), and the count of constant reads stays at 8. zod3's
report keeps its 180 constant objects and 360 foldable reads.
2026-10-25 10:23:48 -05:00
Chris Tate 7721fbd92d Address review feedback 2026-10-25 10:19:26 -05:00
Chris Tate 853033c8ec Throw Node's TypeError when an asserted callee holds no function (#2224) 2026-10-25 10:06:51 -05:00
Chris Tate ffc7af36c7 Read a typed array's length without an invariant header load
`length` and `byteLength` loaded the typed array's header with
`!invariant.load` (#2016). LLVM may move such a load down to its use,
past the release that frees the view, so `const slots = this.slots;
this.slots = next; return slots.length` returned a garbage number in
release builds. These reads are now ordinary loads that keep the
header's alias tag. Header loads that only bound or address an element
access stay invariant: their values reach only that access, which runs
while the view is alive, so typed-array loops still hoist the length
and data pointer.

Corpus typed-array-length-after-release covers a replaced field, a
deleted map entry and a rebound global.
2026-10-25 09:59:43 -05:00
Chris Tate 37b4540b11 Poll worker cancellation between blocks of typed-array loop iterations (#2203) 2026-10-25 09:54:30 -05:00
Chris Tate f0add87f10 Crypto parity: RSA key generation lengths, RSA-PSS keys and encrypted private keys (#2103) 2026-10-25 09:45:21 -05:00
Chris Tate 466ae48980 Merge main 2026-10-25 09:42:07 -05:00
Malte UblandClaude Opus 5.5 c1c4daed82 Throw Node's TypeError when an asserted callee holds no function
`f!()`, `t[k]!()` and `o.cb!()` on a value that holds undefined threw
scriptc's checked-extraction message ("undefined is not representable in
the target union …"). The non-null assertion is erased in JavaScript, so
Node throws the call's own "<callee> is not a function". The checked
extraction behind an asserted callee now carries that message, with
V8's spelling of the callee: parentheses and non-null assertions dropped
and call results printed as `f(...)`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 07:34:32 -07:00
Malte UblandClaude Opus 5.5 d9c1b89b5d Cut the shape overlay's compile cost; keep package JSDoc it does not replace
Compile cost (Hono fixture: 53 s -> 99 s with the overlay before):

- The overlay is planned once per program text: a reload that only drops
  refused annotations renders the cached plan (overlay.ts plan/render
  split) and reuses the cached structural rewrites, instead of analyzing,
  reporting and planning again.
- The overlay's analyses run without per-call-site clones, which only the
  constant-graph report uses so far (SCRIPTC_SHAPE_SITE_CLONES opts in).
- A failing round also drops what the deferred refusals blame, saving a
  reload; deferred refusals no longer blame every annotation of their
  enclosing function (most of them exist without the overlay).
- A parameter whose `typeof` tests the proven values never pass stays
  untyped: the typed parameter would narrow to never in that branch,
  which the lowering refuses (zod 4's Doc.write).
- Deferred refusals in methods blame the arguments of the method's call
  sites (implicit instantiations), like those in functions.

Package JSDoc: the overlay neutralized the package's JSDoc in every
overlaid file, so declarations it does not type lost their documented
types and the lowering instantiated them per call site instead. In
commander, this changed which refusal `--help` reaches: the run passed
the refusal the default build stops at and failed later, in
Command.helpInformation, at a checked conversion of the package's own
documented contract (a help width that is undefined off a TTY). Nothing
the analysis proved was contradicted (SCRIPTC_SHAPE_CHECK_LOG logs no
inferred-fact failure). Only the leading JSDoc of the declarations the
overlay annotates is neutralized now; commander's overlay build fails
exactly where its default build does, and the OVERLAY_KNOWN entries go.

The overlay npm suite (134 cases), the shape tests, lint and format pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 07:34:05 -07:00
Malte UblandClaude Opus 5.5 fc3f0de981 Keep shape inference within its budget on large packages
The analysis exceeded its 20M-step budget on the Hono fixture (32.5 s)
and on moment 2.30 (60 s, two files), so it reported no facts for them.

- Widening: a value node holding more than 32 atoms (merged code that
  every value flows through, such as moment's utilities) becomes ⊤. Its
  values escape, so the facts stay sound, and later atoms escape instead
  of multiplying constraints. SCRIPTC_SHAPE_WIDEN sets the limit.
- A member a string, number or boolean prototype lacks reads undefined
  instead of creating a builtin atom per name (Hono's request and route
  objects meet strings in shared helpers).
- Members of builtin instances (`RegExp.instance`) no longer chain into
  static builtin paths.
- Symbol resolution is memoized per identifier.

Hono: 1.16M steps in 2.4 s with complete facts (55 constant objects).
moment: 115k steps in 0.4 s. zod 3 and the namespace benchmark are
unchanged (same steps and overlay annotations).

SCRIPTC_DEBUG_SHAPES_PROFILE prints where the solver's steps go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 07:34:04 -07:00
Malte UblandClaude Opus 5.5 0c0077da21 Call constant function tables and cross-module namespace members directly
Static-object specialization for namespace-style JavaScript, with the
shape overlay on (shape-inference/static-objects.ts):

- Functions stored in object literals that a top-level statement creates
  (`ns.registry = { run: function () {} }`) are hoisted to module
  functions, like those stored by assignment.
- `table[key](...)` on exactly one constant object whose members are all
  hoisted functions, with a string key, compares the key against each
  member and calls its function directly; any other key reads the table
  as written. The members do not use `this`, the table has no accessors,
  and comparing a string runs no code.
- A read of a primitive member written in the object's own literal
  (`Kind = { CIRCLE: 1 }`) is replaced by the literal. Members stored
  after the object was created stay reads (they could be read first).
- A read in another JavaScript file of a hoisted namespace member refers
  to it through the defining module's exports: the member joins the
  module's `module.exports = { ... }` literal, and the reading file binds
  the module right after the top-level statement that requires it. This
  applies when the exports object is constant and nothing enumerates,
  reflects on, or lets it escape, and the module does not require the
  reading file.

The overlay's cross-file class types and member function types are now
on by default (SCRIPTC_SHAPE_CROSS_FILE=0 and
SCRIPTC_SHAPE_MEMBER_FUNCTIONS=0 turn them off).

The analysis records computed callee reads as computed reads.

New npm case static-tables (with a JavaScript consumer) and
tests/harness/static-tables.test.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 07:34:04 -07:00
Malte UblandClaude Opus 5.5 0d4954f40c Never let the shape overlay break compilation
With the overlay on, a lowering refusal no longer fails the build. The
annotations the refusal depends on are dropped and the program is loaded
and lowered again, so those declarations compile untyped, as they do
without the overlay:

- Each overlay annotation carries an id (`@scriptcShape #<pos>`).
- shape-inference/fallback.ts blames the annotations of the declarations
  enclosing a refusal and of the declarations its expression names,
  following unannotated functions' returns.
- Rounds that stop converging drop whole files' overlays, then the
  overlay for the build. SC6103 reports what was dropped.

Also:

- The tsc gate's project world is built over the files as written; it
  used to see the overlay's files, which failed the typecheck of three
  npm cases.
- Arrays a builtin creates (match results, `new Array(n)`) are no longer
  typed by the overlay: they may have holes, and the library types them.
- compile() accepts `shapeOverlay: true`.

tests/harness/npm-shape-overlay.test.ts runs the whole npm suite with the
overlay on (npm.test.ts's body moves to npm-suite.ts): every case builds
and runs as in the default build, and a refused build that compiles with
the overlay matches Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 07:34:03 -07:00
Chris Tate 4458830db1 Keep native private methods out of reach of Node islands 2026-10-25 09:28:49 -05:00
Chris Tate 64e350edb6 Drop a socket's 'drain' listeners when it closes
The close step drops a socket's listener lists so that a listener which
captures its socket does not keep it alive; the 'drain' list was not
among them. A once('drain') listener still pending when the peer
disconnected, or an on('drain') listener, held its socket and the
socket's buffers after 'close'. The close step now drops the 'drain'
listeners with the others.
2026-10-25 09:25:45 -05:00
Chris Tate 2c125b44b7 Keep a union loop binding in the debug-variables fixture (#2213) 2026-10-25 09:17:31 -05:00
Chris Tate 21e4c3683f Keep the budget in typed-array length loops that assign their array
A loop to a typed array's length reads the length again on every
iteration, so a body that assigns the array's binding changes the limit
for the next iteration. The strip-mined form took the length once per
block of 64 iterations, and after the body assigned a shorter array it
ran on to the block's end, reading past the new length.

The loop to a typed array's length is now strip-mined only when the
array is a local of the function that the body never assigns
(stableReceiver on the match); others keep the budget and test the
length on every iteration.

New corpus program: native-worker-length-loop-reassign.ts.
2026-10-25 09:13:52 -05:00
Chris Tate bc3a7301bc Add object literal members through the store caches in untyped code (#2218) 2026-10-25 09:07:14 -05:00
Chris Tate 0488bef1a9 Describe RSA-PSS AUTO verification against restricted keys as Node does
RSA_PSS_SALTLEN_AUTO recovers the salt length from the signature and
does not enforce the key's minimum salt length, as in Node (OpenSSL).
The runtime's exemption now says so, and corpus 8178's header, which
said the minimum was still enforced, describes what it checks.
2026-10-25 09:00:40 -05:00
Chris Tate fba4d6553e Add no empty chunk to a byte-mode Readable's buffer (#2217) 2026-10-25 08:55:59 -05:00
Chris Tate adf97e900f Merge main 2026-10-25 08:50:59 -05:00
Chris Tate 3f013df4f9 Skip the weak-key table when freeing objects that were never keys (#2215) 2026-10-25 08:50:03 -05:00
Chris Tate 14fa2aedd3 Merge main 2026-10-25 08:28:00 -05:00
Chris Tate 9b2b97033a Add object literal members through the store caches in untyped code
Untyped functions built each member of an object literal with
scr_dyn_key_set: a lookup of the key in the new object, a walk of its
empty prototype chain, a second lookup before the append, and a hash of
the key to share its bytes. The first cached read of the object then
computed its shape from its keys, hashing each key again.

A member whose key is a constant string other than __proto__ now owns a
store cache site and calls scr_js_store_probe, which appends the member
through the cached shape transition, takes the key bytes from the shape,
and leaves the object shaped. Members the probe does not take (Error
field names, members past the 64th, a site's first object of a shape)
keep scr_dyn_key_set, as do computed keys and literals in module
initializers.
2026-10-25 08:24:12 -05:00
Chris Tate 700526ab17 Merge base branch 2026-10-25 08:19:36 -05:00