Commit Graph
4789 Commits
Author SHA1 Message Date
Chris Tate 43ff8a1122 Inline destructured literal results and read ASCII slice ranges inline
- A function whose whole body is `return [a, b]` or `return { a, b }`,
  with number, boolean or string fields, is now inlined where its result
  is only destructured (`const [head, tail] = split(text, at)`). The
  parameters are declared from the arguments, then each field straight
  into its destructured binding, in the literal's order; an unread field
  still evaluates into a binding of its own. No record is built, and a
  slice field read only through its length or char codes becomes a
  scalar slice that copies nothing. Arguments may own their value, but
  as for numeric records nothing inside an argument may hold a
  temporary of its own.
- A scalar slice of a proven-ASCII string computes its range inline:
  the bounds clamp over the byte length exactly as scr_str_slice_range
  does, and no boundary can split a surrogate pair. Other strings keep
  the runtime call, which gains the same ASCII fast path.
- value-records' tuples section, `[head, tail] = split(...)` read only
  through lengths, goes from two heap slices per step to none.
2026-10-24 17:35:25 -05:00
Chris Tate bac7a80377 Raise slow CI job time limits and prune closed pull request caches (#1994) 2026-10-24 17:33:56 -05:00
Chris Tate b6c6be080c Parse npm-static declaration and package files only when a rewrite can apply (#2146) 2026-10-24 17:22:04 -05:00
Chris Tate aa9664bf8a Propagate exceptions by unwinding from directly called functions on Linux (#2115) 2026-10-24 17:15:36 -05:00
Chris Tate bc9fdb9c08 Answer stream, StringDecoder and handle-object members in untyped code as Node does (#2105) 2026-10-24 17:11:01 -05:00
Chris Tate fba274439c Report constant object graphs in untyped JavaScript (static objects, stage 1) (#2147) 2026-10-24 17:08:34 -05:00
Chris Tate 65e62fe1cb Compare integer values without converting them to doubles (#2060) 2026-10-24 17:02:42 -05:00
Chris Tate 8d9705e22d Run CommonJS top-level return, untyped assert forms, and untyped instanceof Object as Node does (#2101) 2026-10-24 17:01:39 -05:00
Chris Tate c37963b8aa Let objects inherit from JavaScript class prototypes (#2078) 2026-10-24 16:58:49 -05:00
Chris Tate 54cb7af31b Settle BigInt quotients from the top limbs, and vectorize limb shifts (#2144) 2026-10-24 16:50:56 -05:00
Chris Tate 32a2d3663a Compare identical references in the test262 adapter (#2090) 2026-10-24 16:46:09 -05:00
Chris Tate 12e16cdb32 Find weak collection entries through hashed indexes (#2004) 2026-10-24 16:40:37 -05:00
Chris Tate 6852b778c3 Verify runtime pack artifacts once per file identity, and store pack selections (#2106) 2026-10-24 16:36:34 -05:00
Chris Tate c1deea4d3f Keep integer index arithmetic in integers across nested counted loops (#2113) 2026-10-24 16:34:12 -05:00
Chris Tate e22cdf358b Decode an untyped stream unshift's string chunk with its encoding
`s.unshift("e9", "hex")` on a native stream held as an untyped value
dropped the encoding argument and put the string's UTF-8 bytes at the
front. A byte-mode stream now takes a string chunk with an encoding as the
bytes Buffer.from(chunk, encoding) holds, as Node's
readableAddChunkUnshift converts it, and an unknown encoding throws
ERR_UNKNOWN_ENCODING. Object-mode streams keep the string as the entry.
2026-10-24 16:28:43 -05:00
Chris Tate 51f64baebf Add a runner for Node's test/parallel files (#2082) 2026-10-24 16:27:34 -05:00
Chris Tate f645e8e702 Run a prototype's constructor getter on the instance
An instance's `constructor` read that reaches a class prototype's own property now reads it with the
instance as the receiver (Reflect.get), so an accessor's getter sees the instance as `this`, as in
Node, instead of the prototype.
2026-10-24 16:27:15 -05:00
Chris Tate 49964dc997 Read prototype methods stored under computed keys on ES5 instances
A member read on a `new F()` instance looked up the name only among the
fields of F's prototype objects. A method stored under a computed key
(`F.prototype[name] = fn`) is kept in the prototype's unknown-key field,
so the read found nothing: the method's body was never visited, and the
objects it writes stayed constant in the report. The read now also takes
the prototype's unknown-key field, as reads of the object's own fields
do.
2026-10-24 16:26:38 -05:00
Chris Tate e00d3fd911 Check comparison operands and walk instanceof chains as Node does
- The assert comparison methods read as function values (equal,
  strictEqual, deepEqual, partialDeepStrictEqual and their negations)
  throw Node's ERR_MISSING_ARGS TypeError, `The "actual" and "expected"
  arguments must be specified`, when called with fewer than two
  arguments, where `[assert.equal][0]()` passed.
- Untyped `instanceof Object` starts at the value's prototype, so
  Object.prototype itself, whose prototype is null, is not an Object.
- Untyped `instanceof Function` follows the whole prototype chain through
  objects and functions: an object whose prototype is a function, or a
  function whose prototype was set to such an object, is a Function.
2026-10-24 16:22:03 -05:00
Chris Tate 6a8ba89d9f Make Dates mutable objects with Node's setters and conversions (#2072) 2026-10-24 16:21:06 -05:00
Chris Tate 7405e93e56 Convert compareArray's message only when the arrays differ 2026-10-24 16:16:23 -05:00
Chris Tate c98c1fe345 Test powers of two exactly before treating a product as exact
The product of two exact integers was treated as exact everywhere when one
of them was a constant power of two, tested with Number.isInteger(Math.log2).
Math.log2 rounds to 52 for 2^52 - 1, so a product by 2^52 - 1 that passes
2^53 was compared as its integer instead of its rounded double. The test now
compares 2 ** Math.round(Math.log2(value)) with the value. Corpus 7983 adds
products by 2^52 - 1 compared with products by 4.
2026-10-24 16:15:53 -05:00
Chris Tate 317162af78 Count repeated calls through constructors, apply and per-value callbacks
The constant-graph pass treats a function whose call edges all run once
at initialization as init-only. Some calls recorded no edge or a
once-only one:

- `new F()` of an ES5 constructor and `f.apply(thisArg, args)` bound the
  arguments without a call edge, so a function also called directly once
  was init-only even when a loop constructed or applied it.
- Callbacks of Array.from, toSorted, replace and replaceAll run once per
  element, comparison or match, but those methods were missing from the
  repeating callback set.
- JSON.parse revivers, JSON.stringify replacers and toJSON methods run
  once per value; their edges at the JSON call site now count as
  repeated, including toJSON calls resolved after the call's handling.

Objects these functions write or allocate were reported as constant, and
their reads as foldable. constant-graphs.test.ts covers each case.
2026-10-24 16:13:05 -05:00
Chris Tate 1d579ea5a2 Check the identity of the open file a runtime pack artifact is read from
Staging stat'ed an artifact's path, read the path, and stat'ed it again.
When the path was a symbolic link switched to another file of the same
size during the read and back before the second stat, both identities
were those of the verified file, so the bytes of the other file were
staged without hashing. Staging now opens the artifact once and takes
both identities from the open file with fstat, so they describe the
file the bytes come from.

A test switches the artifact's link to a same-sized file during the read
and checks that the staged copy has the verified bytes.
2026-10-24 16:05:19 -05:00
Chris Tate 69e6c11290 Merge main 2026-10-24 16:03:54 -05:00
Chris Tate 1f621ce2c1 Version string scans over the UTF-16 units of strings that are not ASCII (#2074) 2026-10-24 16:02:58 -05:00
Chris Tate 190fb8d58e Mix weak-collection hashes in 64 bits on 32-bit targets
uintptr_t is 32 bits on wasm32, where the weak-collection hashes shifted it by 33 and 56 bits. The hashes now widen keys, owners and kinds to uint64_t before mixing; 64-bit targets compute the same values as before.
2026-10-24 16:01:33 -05:00
Chris Tate d813fdafd3 Compare termination signals and resolve the Node checkout path in the test/parallel runner
A run that a signal ends has a null exit code, so the runner recorded a
pass when Node and the executable ended by different signals, or when one
ended by a signal and the other with an exit code compared as null. The
verdict now compares both the exit code and the signal (verdict.mjs), and
records Node's signal in nodeExit as it does for the executable.

A relative --node path also made the minimal lane's fixtures symlink
relative to the copied tree, where it does not resolve. The checkout path
is now resolved first.

tests/harness/node-parallel-runner.test.ts covers both: the termination
comparison, and a relative --node path whose fixtures resolve from the
minimal tree.
2026-10-24 16:00:06 -05:00
Chris Tate ebeec4818d Prove 32-bit products exact only below 2^53
The exactness test for a product's 32-bit form multiplied the operands'
magnitude bounds in a double and accepted a result of at most 2^53. A
bound of 2^53 + 1 (3 * 3002399751580331) rounds to 2^53 there, so a
product the double rounds took the integer multiply's low bits:
`(3 * 3002399751580331) | 0` printed 1 where Node prints 0. The test is
now strict: a bound below 2^53 is exact, and no bound of 2^53 or more
rounds below it.
2026-10-24 15:58:14 -05:00
Malte UblandClaude Opus 5.5 d837036f87 Report constant object graphs in untyped JavaScript (static objects, stage 1)
SCRIPTC_DEBUG_SHAPES=const reports the constant object graphs shape
inference finds (DESIGN-constant-objects.md, stage 1): objects created by
code that runs at most once (module top level, IIFEs, functions reached
only through one call site from such code) whose fields are written only
there, that never meet a reflective or computed write, and that never
escape. Namespaces (`var ns = ns || {}; ns.util = {}`), revealing
modules, `module.exports` tables, and ES5 constructors' prototype objects
qualify. Each near miss names its rule: allocated repeatedly, created by an
init factory (one object per call site), escapes, reflective or computed
write, literal form, written after init, or lazy cache. The report counts
the member reads and calls a later stage can fold.

The analysis now models ES5 constructors (a function's prototype object,
instances reading through it) and links CommonJS require() and export
bindings to the analyzed module's exports, which it previously read as
nothing. Code generation is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 13:53:14 -07:00
Chris Tate 87bd7905f6 Resolve each package file's module edges once when pruning npm re-exports (#2145) 2026-10-24 15:48:45 -05:00
Chris Tate d4fcd787a8 Reference the unwinder weakly from the runtime's raise
scr_unwind.c is linked into every Linux executable, and its references
to _Unwind_ForcedUnwind and _Unwind_GetCFA pulled the unwinder out of
libgcc_eh (or Zig's libunwind) even for programs that never raise, which
grew a static hello world past its size ceiling. The references are now
weak: programs that raise reference the personality routine from their
landing pads, and its unit links the unwinder in.
2026-10-24 15:38:37 -05:00
Chris Tate dd57655bb1 Parse npm-static declaration and package files only when a rewrite can apply
Loading a program with --npm-static parses package files on their own
before the checker reads them:

- The declaration closure of each named package was walked twice, once for
  class overloads and once for class properties, and the one-entry parse
  cache could not serve the second walk. One walk now reads both from each
  parse.
- The nullable class field and Array.find return rewrites parsed every
  package JavaScript file. They now parse only files whose text contains
  `class` and `null` (or `find`), or a Unicode escape that could spell
  them.
- The JSDoc namepath rewrite parsed every file containing `#` or `~`. It
  now parses only files with a namepath segment joined by one of them
  (`a#b`, `a~b`), the pattern the rewrite replaces.

The rewritten sources are unchanged.
2026-10-24 15:38:16 -05:00
Chris Tate e5d386add7 List caches and open pull requests in full before pruning 2026-10-24 15:27:31 -05:00
Chris Tate 9b6a25f57e Resolve each package file's module edges once when pruning npm re-exports
planNpmStaticReexports visited a file again each time another of its
export names was demanded. Every visit resolved the file's import and
export declarations again and walked its whole syntax tree for import()
and require() calls. The purity check kept only pure trees and the failing
root, so once a walk reached an impure file, the next namespace re-export
walked the same files again.

Each file's edges are now collected once per plan; the calls come from
moduleSourceCandidates, which fork and worker discovery already fill. A
repeated visit re-checks only the file's namespace re-exports, the only
edges whose pruning depends on the demand. Purity is decided per strongly
connected component of the module graph and kept for every file, and a
walk that reaches an impure file marks every file on its stack impure.

The pruned re-exports and the module order are unchanged.
2026-10-24 15:20:53 -05:00
Chris Tate ab341be202 Settle BigInt quotients from the top limbs, and vectorize limb shifts
A quotient-only BigInt division with a one-limb quotient returns Knuth's
estimate without a pass over the operands when the top limbs prove it:
with rhat * B + u0 - qhat * d0 at least qhat, a - qhat * b stays positive
whatever the lower limbs hold, since they subtract less than
qhat * B^(n-2). Otherwise one pass settles the estimate as before.

The add and subtract loops process eight limbs a pass, which rebuilds
the carry half as often. Left and right shifts, including the operand
normalization of the general division, form each limb from two source
limbs in a restrict-qualified helper with no dependency between limbs,
so the loops vectorize.
2026-10-24 15:19:33 -05:00
Chris Tate 276542e017 Use fixed timestamps in the fs.cp preserveTimestamps test (#2136) 2026-10-24 15:01:18 -05:00
Chris Tate 850aab38a4 Link Linux executables with lld when the driver finds it (#2135) 2026-10-24 14:55:16 -05:00
Chris Tate ea495056d8 Answer 'in' on native Node handles from their declared members (#1949) 2026-10-24 14:51:03 -05:00
Chris Tate e5cdc68bab Store AbortController and AbortSignal handles in class fields (#2125) 2026-10-24 14:32:15 -05:00
Chris Tate 9cb8ed3ce5 Map resolved empty mapped types as the empty object type (#2127) 2026-10-24 14:25:04 -05:00
Chris Tate 7499f9bd39 Keep versioned loops' slow copies free of speculative integer copies (#2137) 2026-10-24 14:14:40 -05:00
Chris Tate 4efcbbdfe4 Report inferred shapes for untyped npm JavaScript (#2036) 2026-10-24 13:49:30 -05:00
Chris Tate 3f03c90e86 Keep versioned loops' slow copies free of speculative integer copies
The copy of a dense-array, byte-window, counted, callback or typed-array
versioned loop that runs when its guard fails no longer gives its loops
speculative integer or int32 kind copies, which emitted each loop again for
a path that rarely runs. Proven integer loops, which emit one copy, are kept
in it.
2026-10-24 13:47:17 -05:00
Chris Tate bf02b9d7cc Write a number-keyed Map update through the entry its read found (#2130) 2026-10-24 13:38:20 -05:00
Chris Tate 3896957230 Report JSON.parse syntax errors with V8's messages and positions (#2129) 2026-10-24 13:34:12 -05:00
Chris Tate a8c0fa772b Use fixed timestamps in the fs.cp preserveTimestamps test 2026-10-24 13:28:53 -05:00
Chris Tate c33d693775 Answer instanceof against the primitive wrapper constructors (#2126) 2026-10-24 13:28:00 -05:00
Chris Tate 9dfaddf7bc Check unchanged frontend inputs by file identity before hashing them (#2128) 2026-10-24 13:24:46 -05:00
Chris Tate 587443428c Link Linux executables with lld when the driver finds it
Linux executables linked through the clang driver with its default linker,
GNU ld. lld links the same objects in about half the time.

- Native and Node-hosted Linux exe links pass -fuse-ld=lld when ld.lld is
  beside the driver's executable (where clang looks first) or on PATH, and
  the default driver is in use (no SCRIPTC_LINKER, no sanitizer link).
- A link with lld that fails runs again with the driver's default linker,
  whose error is the one reported when that fails too.
2026-10-24 13:22:26 -05:00