Commit Graph
2626 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 3ffcdfb08f Refuse a default-locale toLocaleString in the coverage tier fixtures
This branch lowers Number.prototype.toPrecision statically, so the
tier-demotions and dynamic-mix coverage fixtures lost the operation they
used as "no static lowering". Use n.toLocaleString() instead: the host's
default locale is something a compiled binary cannot carry, so it stays
refused (SC2020) and the fixtures keep the same blocker and demotion
request on the same lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 19:34:12 -07:00
Malte Ubl 7f4a150902 Merge remote-tracking branch 'origin/main' into opt/builtins-npm2
# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-calls.ts
2026-10-18 19:34:12 -07:00
Chris Tate 907d481a1d Refuse super() into a constructor that returns an instance only when it returns another one
A base constructor that returns an instance of its class gets a result
slot, and super() from a subclass refused. three.js's Color constructor
ends in `return this.set(r, g, b)`, where set returns this, so every
construction of Color4 (`super(r, g, b)` during three.webgpu.js's module
initialization) threw the refusal, while Node constructs the subclass's
own instance.

In JavaScript, super() into such a class now calls a per-subclass helper
after the base constructor: it reads and clears the base's result slot,
and refuses at run time only when the base returned an instance other
than the subclass's this, where Node would rebind it. TypeScript
subclasses keep the compile-time refusal.
2026-10-18 21:31:43 -05:00
Chris Tate f629d03dd8 Merge main 2026-10-18 21:28:03 -05:00
Chris Tate b9ca34887f Merge main 2026-10-18 21:24:49 -05:00
Chris Tate a7e3a9d0aa Cancel running subtests when their parent test times out (#1350) 2026-10-18 21:24:04 -05:00
Chris Tate c3b1303e7a Convert class instances to strings and numbers through their own methods (#1358) 2026-10-18 21:20:19 -05:00
Malte UblandClaude Opus 5.5 f79ed58138 Link the runtime units of lowered IR as compile() does in the harnesses
The self-hosting harnesses link the IR they lower through compileC. Most
hand-picked a subset of runtime feature flags, which lacked dynTier: a
sanitized build compiles exactly the listed runtime sources, so programs
with jsval operations failed to link scr_js_value.c's entry points, and
harnesses passing no flags at all failed in both lanes.

Every such site now spreads moduleLinkFeatures (tests/harness/
link-features.ts), which is executableLinkFeatures, the selection
compile() uses, so a runtime unit added later cannot drift out of the
harness builds. moduleUsesDynTier joins the IR facade's unit gates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 19:12:42 -07:00
Chris Tate d4d407bb07 Fix npm crashes, internal errors and false TDZ errors from the compatibility research (#1357) 2026-10-18 21:05:11 -05:00
Chris Tate 82683b5b4d Clear timers through handles that may be undefined or null (#1356) 2026-10-18 21:02:08 -05:00
Chris Tate e8e432b6dd Clean up subtests cancelled by their parent's timeout
node:test changes, matching Node 24.22:

- A subtest cancelled because its parent timed out has its mocks
  restored and its mock timers reset when it is cancelled, as Node's
  postRun resets the test's mock tracker. Before, a mocked method stayed
  mocked for the tests that ran after it.
- Its afterEach chain and t.after hooks run at the next turn, once the
  runner has moved on to the next test, as Node runs them when the
  cancelled test's own run resumes. Before, they never ran. The chain is
  copied when the test is cancelled, since its ancestors drop their hooks
  when they finish.
- Its hooks are then dropped, so closures that capture its context do not
  keep it alive. The registration parent is still not restored when the
  cancelled body settles: it belongs to the test that runs then.

The timeout-cancel-cleanup fixture records the mocked method and the hook
order against the next test's body and compares them with Node, plain and
with SCRIPTC_SAN=1. It fails without this change.
2026-10-18 20:58:20 -05:00
Chris Tate 2ed5136923 Convert class instances to strings and numbers through their own methods
String(x), template literals, `+` with a string and Number(x) or unary
plus on a program class instance failed with SC2001 ("values of type
'Money' cannot be compiled yet"), even when the class defines toString.
They now run Node's ToPrimitive protocol through the class's methods:

- `[Symbol.toPrimitive](hint)` when the class defines it, with the hint
  "string" for String(x) and template literals, "default" for `+`, and
  "number" for Number(x) and unary plus.
- Otherwise toString (string hint) or valueOf and then toString (default
  and number hints), and "[object Object]" (NaN as a number) when the
  class defines neither.
- The method dispatches like a call of it. Subclasses that convert
  through a different method are tested first with instanceof, so an
  array of base-class values converts each element by its own class.
- Unions of class instances and primitives (`Box | undefined`) convert
  each arm as it would alone.

Conversions the program can change at run time keep a named refusal: a
toString or valueOf instance property or prototype write, a
Symbol.toStringTag, and conversion methods that can return an object.
Builtin classes and their subclasses keep their existing conversions.
2026-10-18 20:55:25 -05:00
Chris Tate 3b46ea1603 Return undefined from generic functions that read a missing element (#1307) 2026-10-18 20:48:26 -05:00
Chris Tate 076d3e01fb List the Date string formatters in the library profile's Date family
The determinism fence over stdlib.date. now covers toString,
toDateString and toTimeString, which read the host time zone.
2026-10-18 20:46:53 -05:00
Chris Tate 684f73d186 Add ThreadSlots, per-thread values on shared objects, to @scriptc/experimental (#1271) 2026-10-18 20:39:11 -05:00
Chris Tate c48983889c Clear timers through handles that may be undefined or null
clearTimeout, clearInterval and clearImmediate refused a handle typed
`Timeout | undefined` or `Timeout | null`, the type of a timer variable
that starts empty (`let timer: ReturnType<typeof setTimeout> | undefined`)
and of `handles[i]!` reads. Such a handle now passes id 0 when it is
absent, which matches no timer, so the clear does nothing, as in Node.
2026-10-18 20:35:23 -05:00
Chris Tate 4e92ec16d8 Compile derived constructors whose super() call leads a comma statement (#1354) 2026-10-18 20:25:58 -05:00
Chris Tate d9ac08e57c Merge main 2026-10-18 20:25:10 -05:00
Chris Tate ee9a0e1c33 Pool keep-alive sockets in http Agents and use globalAgent for default requests (#1230) 2026-10-18 20:21:54 -05:00
Chris Tate 705c197a71 Format Dates with Node's local time zone names
Date.prototype.toString, toDateString and toTimeString, String(date),
template literals and string concatenation print local time with the GMT
offset and the zone's long name, as Node does. The runtime identifies the
host zone from TZ or /etc/localtime, again after each write to
process.env.TZ, and looks its name up in a table generated from Node's
ICU data. Dates held in untyped values convert to strings with toString()
and to numbers with their time value.
2026-10-18 20:20:05 -05:00
Chris Tate c03479abe5 Merge main 2026-10-18 20:16:15 -05:00
Chris Tate 4f1a304163 Merge main 2026-10-18 20:12:11 -05:00
Chris Tate 2d37d451e9 Run iterator helpers on untyped iterators (#1352) 2026-10-18 20:07:41 -05:00
Chris Tate ada2657873 Stop creating self-capture cycles and buffer cycle candidates on the release fast path (#1320) 2026-10-18 20:03:40 -05:00
Malte UblandClaude Opus 5.5 18033dc42d Compile derived constructors whose super() call leads a comma statement
Minifiers join a derived constructor's statements into one comma
expression (`super(message, options), this.name = ..., ...;`), which jose
6.2.12 ships in its error classes. The statement lowering already runs
each operand of a statement-level comma as its own statement, so super()
is accepted when only parentheses and comma operators separate it from
its expression statement. Every other expression position keeps the
SC1090 refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:57:35 -07:00
Chris Tate 6c07441c24 Check programs with tsc-ts by default
scriptc's frontend talks to a TypeScript 7 API server. tsc-ts serves the
same API as tsgo, and it is now the default checker; SCRIPTC_CHECKER=tsgo
selects tsgo, which scriptc still includes.

- @scriptc/compiler depends on tsc-ts 0.0.3. The native distribution
  ships its platform package as lib/tsc-ts, named by a new tsc_ts field
  of the toolchain manifest; a manifest without it takes the executable
  of an installed tsc-ts package.
- The executable cache key includes the tsc-ts executable's identity, in
  the compiler and in the CLI's startup route.
- tsc-ts keeps the syntax trees it builds for the life of its process,
  so the source parser starts a new server after 256 KB of parsed
  source instead of letting one server grow with every file.
- A CI job compiles every corpus program with both checkers, in release
  and dev, and requires the same LLVM IR and diagnostics
  (scripts/checker-identity.mjs).
- Tests that compare with the TypeScript 7 SDK, the checker-panic
  diagnostics, and the protocol tests run against tsgo.
2026-10-18 19:55:52 -05:00
Chris Tate 45d4105351 Compile maybe-promise unions, unknown[] rest parameters, and function-valued Maps and Sets (#1347) 2026-10-18 19:55:09 -05:00
Chris Tate f761f7d75a Count idle pooled sockets in agent.totalSocketCount
agent.totalSocketCount counted only the requests an agent was serving,
so the idle sockets of a keepAlive agent in agent.freeSockets were left
out, and a second request that dialed a new socket while the first was
idle counted 1 instead of 2. As in Node, the count is now the number of
sockets the agent created that have not closed: a socket joins it when
its dial starts and leaves it when it closes, or before the request's
'close' when it does not go back to the pool. A request that takes a
free socket keeps the count.

Corpus 5593 covers reuse, a second socket, agent.destroy() and an agent
without keepAlive.
2026-10-18 19:54:32 -05:00
Malte Ubl cdf731e4da Merge remote-tracking branch 'origin/main' into opt/npm-runtime-bugs
# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-modules.ts
#	packages/compiler/src/frontend/lowering/lower-stmts.ts
#	packages/compiler/src/frontend/program.ts
2026-10-18 17:50:35 -07:00
Malte UblandClaude Opus 5.5 20f15f89fd Let implicit JS instances that fall through return undefined
A JavaScript function specialized per call site takes its declared
return type from JSDoc. bcryptjs's `hash` and `compare` document
`@returns {!Promise}` but return nothing when a callback is passed, so
their instances ended with the "the checker proved every path returns"
trap and every callback-style call aborted with SC9002. Non-generic
JavaScript functions already widen such a return with an undefined arm;
implicit instances now do the same.

The widened `Promise<unknown> | undefined` result is then awaited by
TypeScript callers, which was refused: an await of a promise-of-dynamic
or unit union now goes through the dynamic await, which adopts the
promise or hops once for the unit, as Node does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:48:01 -07:00
Malte UblandClaude Opus 5.5 f8fa60afec Link the promise adapter when a boxed class instance holds a promise
Boxing a class instance as a dynamic value (Object.keys, spread,
inspection) boxes its fields, and a promise field goes through
scr_dyn_new_promise_adapting in scr_async_dyn.c. The link-unit scan
followed promises through records, arrays, unions and functions but not
class instances, so `Object.keys(job)` over a class with an optional
`Promise` field failed to link.

The scan now follows a class instance's fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:48:01 -07:00
Malte UblandClaude Opus 5.5 b6637c882d Check record, array and string fields that construction can read early
A field without an initializer that the constructor assigns only after
handing `this` to other code is undefined in Node until that assignment.
Class-typed fields in that position already take the undefined-armed
deferred-init slot whose reads check it. Record, array and string fields
kept a plain pointer slot, so a method the constructor calls first could
read null and crash with a segfault (`this.internal.n` read from a
method before `this.internal = ...`).

Those field kinds now take the same checked slot when a method or
accessor the constructor calls through `this` (transitively, over the
class and its bases) reads them. Fields only exposed in other ways keep
their plain slot, so constructors whose early calls never touch the
field compile as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:48:00 -07:00
Chris Tate ec79cf2789 Run iterator helpers on untyped iterators
In JavaScript, a receiver without an inferred type, such as an untyped
parameter's xs.values(), is the runtime's checked iterator. Its only
method was next(), so xs.values().filter(f), .map(f) and the other
iterator helpers threw "TypeError: ... is not a function" where Node
runs them, and [].values().reduce(f) threw the same instead of Node's
"Reduce of a done iterator with no initial value".

The native array, string, Map and Set iterators now answer the
Iterator.prototype methods. map, filter, take, drop and flatMap make
helper objects (SCR_DYNH_ITERATOR_HELPER) that pull lazily and support
next(), return(), for...of and spreads, print as
"Object [Iterator Helper] {}" and tag as "[object Iterator Helper]".
toArray, forEach, reduce, some, every and find drive the iterator.

The helpers close the iterator they were made from where Node does:
when a callback throws, when take's budget is spent, on return(), and
before the error for an invalid argument. flatMap closes its inner
iterator first. An error from a close that runs because of another
error is dropped. Invalid arguments throw Node's messages, such as
"number 5 is not a function" and "x must be positive".
2026-10-18 19:42:27 -05:00
Malte Ubl 91c2a608b6 Merge remote-tracking branch 'origin/main' into opt/tier-s3-jsval 2026-10-18 17:37:42 -07:00
Malte Ubl 63d04d3a70 Merge remote-tracking branch 'origin/main' into opt/silent-bugs
# Conflicts:
#	tests/harness/errors.test.ts
2026-10-18 17:32:39 -07:00
Malte UblandClaude Opus 5.5 4e72477c55 Answer NaN for ** with a base of ±1 and a non-finite exponent
`1 ** x` with x = Infinity printed 1 where Node prints NaN: the operator
lowered to libm's pow, which answers 1 for pow(±1, ±Infinity) and
pow(1, NaN) (and LLVM folds pow(1.0, x) to 1.0). Such operands now keep
the direct pow call behind an inline guard that selects NaN when the
base is ±1 and the exponent is not finite. A finite literal exponent
(x ** 2) or a literal base other than ±1 (2 ** k) cannot meet those
cases and keeps the bare call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:32:38 -07:00
Malte UblandClaude Opus 5.5 a1576d7c5f Keep the deferred-class-receiver fixture on a rejected field type
The fixture's class was rejected through a WeakMap-typed field, which
this branch makes legal. A MapIterator-typed field is still refused, so
the class stays rejected and the test still checks that a class reached
only through a receiver probe reports its own diagnostic exactly once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:30:54 -07:00
Chris Tate 3de2fb56a0 Cancel running subtests when their parent test times out
node:test changes, matching Node 24.22:

- When a test's { timeout } elapses, its subtests that are still running
  are cancelled, the innermost first. Each reports its duration so far and
  fails with "test did not finish before its parent and was cancelled",
  before the parent's result.
- A cancelled subtest's body keeps running unobserved: when it settles it
  reports nothing and leaves the runner's state alone. Before, it reported
  its result after the run's summary.

The timeout-subtests fixture compares the results, counts and failing
section with Node, plain and with SCRIPTC_SAN=1.
2026-10-18 19:28:55 -05:00
Chris Tate 008eb0712c Merge main 2026-10-18 19:13:48 -05:00
Chris Tate 6fd2dedb15 Merge main 2026-10-18 19:04:59 -05:00
Chris Tate 6ab49f33b2 Stringify class instances with JSON.stringify (#1338) 2026-10-18 18:59:22 -05:00
Chris Tate 9414bdc5fe Merge main 2026-10-18 18:58:47 -05:00
Chris Tate aa9289b9d9 Concatenate untyped Buffer lists as Node does (#1272) 2026-10-18 18:46:36 -05:00
Chris Tate 550e00549c Move callSiteReturnType into ir.ts for devirtualization
Lowering may not import ir/validate.ts, and devirtualize.ts imported
callSiteReturnType from it. The function only reads an IrFunction's
types, so it moves next to typeEquals in ir.ts, which both the
validator and lowering import.

The Date formatter fence's hint now lists toUTCString(), which this
branch lowers, so the date-formatters and stdlib-fence snapshots record
the new hint. toString() and toLocaleString() still have no lowering,
and the fixtures keep them.
2026-10-18 18:40:52 -05:00
Chris Tate 707ac5cf77 Carry Hash and Hmac values through unknown and any slots (#1257) 2026-10-18 18:38:52 -05:00
Chris Tate 8d94e5d993 Refuse RegExp and Promise class fields in JSON.stringify
A Date field reads through the class's checked-dynamic property view and
writes its toJSON text, so json-class-fields.ts compiled and matched Node
while the diagnostics test expected a refusal. The Date cases move to
6000-json-stringify-class-instances.ts.

RegExp and Promise fields read through the view but the runtime JSON
traversal has no writer for them: a RegExp field threw at run time where
Node writes {}, and a Promise field failed to link. Both are now refused
at compile time naming the field, also inside a field's arrays, records
and unions, and the fixture covers them.
2026-10-18 18:34:09 -05:00
Chris Tate 9239521160 Answer instanceof Writable for Duplex streams as Node does
Node's Writable[Symbol.hasInstance] answers true for any stream with a
writable side, so a Duplex, Transform, PassThrough or zlib stream (and
user subclasses of Duplex) is an instance of Writable without inheriting
from it. `x instanceof Writable` tested only Writable's own interval and
answered false for them. It now also tests Duplex's interval for object,
union and catch-binding operands, and the static folds count Duplex
subclasses. Subclasses of Writable keep the prototype test, as in Node.
2026-10-18 18:15:01 -05:00
Chris Tate cf11fbaaeb WebAssembly W0–W2: free handle tags, a cross-thread clone hook, the WebAssembly types and refusals, and a decoder that reports every module a program can load (#1339) 2026-10-18 18:12:42 -05:00
Chris Tate 4dc25a18c1 Lower iterator helpers on generators and Map and Set iterators (#1342) 2026-10-18 18:11:21 -05:00
Chris Tate 14372c8274 Build "is not a function" errors for Hash, Hmac and KeyObject handles at any length
The Hash/Hmac and KeyObject handle dispatchers formatted "<callee> is not
a function" into a 160-byte stack buffer and passed snprintf's return
value as the message length. A callee text longer than about 140 bytes
made that length exceed the buffer, so the error copied bytes past it:
the message ended in stack contents, and sanitized builds aborted. Both
dispatchers now build the message in a growable buffer, as the shared
dynamic-call path does, so the TypeError carries the whole callee text,
as in Node.

Corpus 5774 calls a missing method on a Hash, an Hmac and a KeyObject
through callee texts longer than 160 bytes, and through a short one, and
corpus-costs.json records its plain and sanitized costs.
2026-10-18 18:09:08 -05:00