Commit Graph
4679 Commits
Author SHA1 Message Date
Chris Tate 29e5bacb9c Pump untyped iterable pipeline sources (#2111) 2026-10-24 18:03:36 -05:00
Chris Tate fcc81c034c Read the final result's value only for sync pipeline sources
for await reads the value of a done result only through the
async-from-sync wrapper; an async iterator's done result is not read.
The source's generator read it for both, so an async iterator whose
done result has a throwing value getter rejected the pipeline. It now
reads and awaits the final value only when the iterator is sync.
2026-10-24 17:59:56 -05:00
Chris Tate 9c670e8d93 Count the arguments a function receives when it is called through a value (#1880) 2026-10-24 17:58:41 -05:00
Chris Tate c0a4496900 Check with a tsc-ts server shared by later builds (opt-in)
With SCRIPTC_CHECKER_SERVER=1 on Linux and macOS, the native compiler's
checker connections go to a shared `tsc-ts --api --listen` server, which
keeps the files it parsed for the next build. When the variable is unset,
nothing changes.

checkerServerName names the server by a digest of the tsc-ts executable's
identity, the working directory, the file system callbacks, the timing
flag and the TSC_TS_* environment. scriptc_ts7_attach takes the server's
lock file with a non-blocking flock, connects to its socket in this
user's private server directory ($XDG_RUNTIME_DIR/scriptc or
/tmp/scriptc-<uid>), and starts the server when none answers: in its own
process group, with its standard streams on /dev/null and no other
descriptor of the build, with --idle-exit 300 and TSC_TS_LISTEN_MEMORY=2048
unless the environment sets it. A build that finds the server in use,
cannot start or reach it, or gets no answer to a ping checks with its own
tsc-ts process, as without the variable; so does a tsc-ts without
--listen, which exits as it starts. The syntax-only source parser, which
reads no libraries, always uses its own process and leaves the server to
the program's checker.
2026-10-24 17:50:45 -05:00
Chris Tate 9d08b301fc Merge main 2026-10-24 17:50:19 -05:00
Chris Tate 587b8f40c0 Type untyped JavaScript Maps, Sets and arrays from their uses (#2089) 2026-10-24 17:45:47 -05:00
Chris Tate da738a78ba Construct readline.Interface and builtin classes bound through require() members in JavaScript (#2095) 2026-10-24 17:43:27 -05:00
Chris Tate 01f5a3455a Test row-pair overlaps only for nests of two or more rows
The paired copy's test read the stored and read arrays' headers whatever
the row range, so a nest whose arrays the program assigns only for a
nonempty range dereferenced their NULL headers on an empty range, where
the loop as written never reads them. The overlap tests now run only
when the nest has at least two rows, and an array that is not there
answers that it is not disjoint, so the loop runs as written.
2026-10-24 17:38:44 -05:00
Chris Tate fe6dd9a5c5 Skip a byte order mark at the start of package.json
The run-time resolver parsed package.json as it read it, so a file that
starts with a UTF-8 byte order mark failed to parse and computed require
and require.resolve ended with the invalid-package-config refusal. Node's
reader skips the mark; the resolver now does too.

The corpus program writes the package with the marked package.json to a
fresh directory at run time and resolves it by its absolute path, so no
committed file contains the mark.
2026-10-24 17:38:25 -05:00
Chris Tate a711d74ecf Keep copied array callbacks exact with local classes, switches and contracts
- A binding that a local class captures counts as written: the class's
  methods may assign it while the loop runs, so the call keeps its helper.
- Copied bodies rename the labels of switch and for-of statements and the
  binding of for-of loops.
- The known-union and record-temporary cleanups rewrite switch cases and
  for-of bodies, where they left declarations of removed locals.
- Copied locals keep their source bindings. In programs that write
  contract types, a parameter with a source binding keeps its own local,
  so an Int32 parameter is checked against the elements the loop passes.
2026-10-24 17:35:51 -05:00
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 6e4bfb3c91 Await the final value of an untyped sync pipeline source
for await over a sync iterator awaits the value of the result that ends
it too, so a sync iterator that returns a rejected promise rejects the
pump. The source's generator skipped that value and reported success.
It now awaits a final value that is an object (a promise included)
before it stops, without closing the finished iterator.
2026-10-24 17:34:40 -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 5155793155 Decode passphrases with the key's encoding, and refuse RSA-PSS keys restricted to other digests
createPrivateKey decoded a string passphrase as UTF-8 whatever the
options' encoding, so a correct passphrase given in that encoding failed
to decrypt, and an encrypted export read its passphrase the same way.
Both now decode it with the encoding, as Node does.

An RSA-PSS key whose parameters name a digest the runtime does not sign
with, such as sha512-224 or sha512-256, was imported without its digest,
MGF1 and salt restrictions, so it allowed operations the key forbids. It
now throws a coded SC2020 error that names the digest. Parameters that
are malformed still leave the key unrestricted, as OpenSSL reads them.
2026-10-24 17:33:30 -05:00
Chris Tate 2edebe7e2f Read spread tuple fields before later rest arguments run
The packing path for `(T | undefined)[]` rest parameters saved plain
arguments to temporaries but read tuple fields only when it built the
pack, after every later argument had run. A later argument that wrote
the tuple changed values already spread into the call. Each tuple field
is now saved where its spread appears, in argument order.
2026-10-24 17:30:32 -05:00
Chris Tate 7fb950deaf Keep rejectUnauthorized on unless false, and certificates independent of raw
A server with requestCert read a runtime rejectUnauthorized through
JavaScript truthiness, so null, 0 or "" let clients without a valid
certificate through, and tls.connect read it the same way. Both now treat
only false as off, as Node's options.rejectUnauthorized !== false does; a
standalone TLSSocket keeps Node's truthiness test.

An X509Certificate's methods read the record's raw Buffer, so writing
into the Buffer that certificate.raw answers changed later checks, the
PEM and the public key. The record now keeps a hidden copy of the DER
that the methods read, and raw stays the same Buffer at every read, as
in Node.
2026-10-24 17:29:34 -05:00
Chris Tate cc6b114468 Take the argument count before parameters bind
A function that reads arguments.length and is called through a value
reads the count its adapter stored, then clears the slot. That read ran
after the parameter prologue, so a parameter binding that threw would
leave the slot set for the next direct call. The count is now taken, and
the slot cleared, before any parameter binds.
2026-10-24 17:23:08 -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 055c9ffa25 Evaluate the arguments of deferred require.resolve calls before they throw
Preflight admits a createRequire loader's require.resolve call with a
computed request inside a try block, but lowering refused the call, so
in JavaScript source the whole statement became a runtime fence. The
fence threw before the request expression ran, and before anything else
in the statement: `try { where = load.resolve(candidate(name)); }`
skipped candidate()'s side effects, so the catch clause saw a different
state than in Node.

Such calls (a computed request, another argument shape, or
runtime-computed options, on require.resolve and require.resolve.paths)
now evaluate their arguments in order and then throw the fence, as a
computed require call does. The addon-probe fixture checks that a
computed candidate path runs before each failed lookup.
2026-10-24 17:07:44 -05:00
Chris Tate 34ada100c4 Format the string comparison emission test 2026-10-24 17:07:05 -05:00
Chris Tate 40adc2d161 Hoist a call only when its arguments write no binding
A hoisted call runs before the rest of its write. An argument such as
`i++` in `acc = acc * 3 + i + tally(i++)` would then move that write
ahead of the write's earlier read of `i`, so such a call stays where it
is and the write is not speculated.
2026-10-24 17:06:14 -05:00
Chris Tate 01e6d55fc5 Keep optional-element arrays through nested conversions, union arms, rest packing and receiver reassignment
- Checked conversions from dynamic values key their builders and matchers
  by the type with `?` after every `(T | undefined)[]`, at any depth.
  `number[][]` and `(number | undefined)[][]` share a typeKey, so the
  second conversion reused the first one's builder and rejected undefined
  elements.
- The dynamic matcher of a `(T | undefined)[]` takes undefined elements,
  so a union with such an arm matches arrays that hold undefined.
- An optional-element rest parameter packs tuple-spread values and plain
  arguments together as present undefined elements, each evaluated once in
  argument order, instead of converting each value to T.
- The element-state scan counts an argument in a rest position that may be
  undefined, and a spread tuple with undefined elements, as undefined
  elements of the rest array, so reads in the callee check element states.
- A union index read keeps the receiver in a temporary when the key can
  assign it, so `a[(a = other, 0)]` reads the array `a` held before the key.
2026-10-24 17:05:18 -05:00
Chris Tate 9406ba5b89 Run row sums two rows per pass
A loop nest that sums one term per row,

  for (let i = start; i < end; i++) {
    let t = 0;
    for (let j = from; j < to; j++) t += term;
    out[i] = t;
  }

where the term only reads numbers, the counters and typed-array
elements, now runs two rows per pass when both row bounds are int32,
the start is at most the end, and the stored array shares no byte with
any array the term reads. Each row keeps its order of additions, so each
sum has the bits of the loop as written; an odd row count runs its last
row as written, and any other case runs the nest as written. Element
reads that do not depend on the row are read once per column for both
rows. With two independent sums in the inner loop, neither waits on the
other's addition, and LLVM computes both rows' terms in one vector.

bytes.disjoint compares two typed arrays' element ranges in memory.
2026-10-24 17:04:14 -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 02ec0e86f6 Address review findings in the npm and CommonJS interop batch
- Intl.Segmenter accepts an options object literal made only of
  `granularity` entries again, including the empty `{}` main accepted;
  with none the segmenter is a grapheme segmenter, and a repeated key
  takes its last value, as in JavaScript.
- Namespace reads of a CommonJS module refuse a name that any module it
  re-exports assigns inside a function, not only the module itself:
  Node's namespace keeps the values from the end of evaluation.
- The named-import link check treats `module.exports` as an export every
  CommonJS module provides, as Node 24 does, instead of throwing its
  missing-export SyntaxError.
- The npm cases cjs-esm-named and cjs-lexer-aligned, which the batch no
  longer refuses at build time, are listed with the run-time SC1090 their
  CommonJS exports-object reads report.
2026-10-24 16:54:24 -05:00
Chris Tate eeaf622fbe Merge main 2026-10-24 16:51:57 -05:00
Chris Tate d60cdcc843 Merge the base branch 2026-10-24 16:49:25 -05:00
Chris Tate e367415976 Merge the base branch 2026-10-24 16:47:33 -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 9b64c7388d Resume hoisted-call writes reached from earlier bails, and shift only by exact powers of two
- A speculated write with hoisted calls resumes over its stored call
  results only when its own check failed. Reached in the resume copy
  from an earlier write's failed check, its calls never ran on that
  iteration, so it now runs as written: the calls happen once and the
  arithmetic reads their fresh results.
- A literal divisor is a power of two only when it is exactly 2^k with k
  in [0, 52]. Math.log2 rounds 2^52 + 1 to 52, so `Math.floor(v / (2^52 + 1))`
  was emitted as a shift by 52.
- A helper expands only when every call in its body is of another helper
  that expands, so an expansion never moves a call with effects ahead of
  an argument read. Hoisting already refused such writes; the
  classification now says so itself.
2026-10-24 16:38:12 -05:00
Chris Tate 54f12d73dc Use built-in typed-array species, copy slices after species creation, and read element descriptors
- A built-in typed-array constructor set as a view's `constructor` is its
  own species, as the getter it inherits from %TypedArray% returns it:
  `u8.constructor = Float64Array; u8.map(f)` makes a Float64Array. An own
  Symbol.species on the constructor still wins.
- TypedArray.prototype.slice with a species constructor creates the
  result first and then copies the source's current elements, up to its
  current length: a constructor that changes the source is seen.
- Object.defineProperty on a typed-array element reads the descriptor as
  ToPropertyDescriptor does, through its getters and in its order, and
  throws a TypeError for a primitive descriptor or an accessor with a
  value. The generic object path shares the same reader.
2026-10-24 16:37:07 -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 d9fdd81ee9 Construct X509Certificate bound through a require() member
`const X = require("crypto").X509Certificate; new X(data)` now resolves
to the builtin class, so the standard-library constructor fence reported
SC2020 for it before the X509Certificate lowering ran. That lowering now
runs ahead of the fence. Corpus 8231 constructs a certificate through the
binding.
2026-10-24 16:32:40 -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 bffd887145 Keep the native receiver for private accessors and private method values
A JavaScript prototype method value runs over the call-site receiver when its private members are
fields (brand-checked reads and writes) or private methods it calls. A private accessor, or a private
method read as a value, would read a public property of the same name, so such a method keeps its
native receiver, as before. A super method call with spread arguments over the call-site receiver
now spreads them into the dynamic call instead of rejecting them.
2026-10-24 16:26:20 -05:00
Chris Tate e7c7a45810 Convert a read length once in fs.read and fs.readSync
fs.read and fs.readSync with arguments the compiler could not type
validated the length with `length |= 0` and then converted it again for
the read. A length with a valueOf method ran valueOf twice, and the read
used the second result. Node converts the length once.

scr_fs_read_validate now returns the converted length, and both reads
use it.
2026-10-24 16:24:50 -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 0c6ed44227 Match a nested union in full after a getter runs in a trusted build
A union without recursion builds its matched arm without a visit
session, so its getters and proxy traps ran without marking the
conversion. A nested union then took its arm from the earlier match,
even when the getter returned another value. A getter or trap that runs
during a trusted build outside a session now marks it as well, and the
outermost trusted build clears the mark when it ends.
2026-10-24 16:21:32 -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 49d99d7764 Rewrite tests of held character locals inside other optional reads
An optional read tested against a one-unit literal is replaced by a test
of its code unit, built from the read's receiver and index. When that
receiver or index tested a held character local, as in
`src[c === "a" ? pos + 1 : pos] === "b"`, the inner test was kept as it
was and still compared the local as a string|undefined union while the
local's binding became a number, which failed IR validation. The same
held for a held local's own initializer. The receiver, index and temps of
both reads are now rewritten first.
2026-10-24 16:20:33 -05:00
Chris Tate a538ff42f0 Read only value slots in lastIndexOf with an element-typed needle
lastIndexOf with a needle of the element type read every non-hole index
as the element type. An undefined or foreign element written through a
dynamic view (or a hole that toSorted turned into undefined) has no
element-typed payload, so the read aborted with "array index N is
undefined". The search now reads an index only when it holds a value,
and skips undefined and foreign elements, as Node's strict equality
does for a string or number needle.
2026-10-24 16:19:16 -05:00
Chris Tate af33be8046 Treat a collection exported through an export list as escaping
A reference in `export { m }` resolved to the export's alias symbol, not to
the local binding, so the inference did not see the export and typed the
collection from its local uses. An importing module then used it as an
untyped Map and failed to compile (SC9001). An unrenamed export specifier
now resolves to the binding it exports, which refuses the inference as
`export const` does. Corpus 7985 adds `export { listed, renamed as alias }`.
2026-10-24 16:16:49 -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 8f3c2c28d6 Count comparator calls in AArch64 and Mach-O assembly
The string comparison emission test matched only x86-64 `call`
instructions to unprefixed symbols, so on AArch64 and macOS hosts it
counted no calls and failed its positive-count check. It now also matches
AArch64 `bl` and Mach-O's leading underscore, and it runs for the host and
for aarch64-linux-gnu and x86_64-macos assembly, so every host checks all
three spellings.
2026-10-24 16:14:15 -05:00