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>
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.
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>
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.
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.
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.
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.
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>
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.
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.
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>
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>
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>
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".
`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>
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>
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.
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.
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.
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.
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.