An async generator whose body suspends only at its own awaits and yields
now compiles to an LLVM switched coroutine, as async functions of that
kind already do, instead of running on a fiber with its own stack. Its
steps run on the stack that resumes it (the loop's after an await), its
awaits suspend through scr_coro_await_prepare and scr_coro_hop_prepare,
and its yields store OUT through scr_coro_yield_* before suspending.
- scr_async_gen_new_stackless creates the generator. The stackless hop
settles an async generator's state as the fiber hop does, and a step
that would start below the stack guard throws a RangeError, which
rejects its request.
- Calling an async generator's next, return or throw no longer counts as
parking the caller: they queue a request and run the body to its next
suspension, which returns to the caller. Bodies that consume async
generators can now be stackless too.
- An untyped await (async.awaitDyn) has a native coroutine form,
extracted by scr_coro_await_dyn_value, so async bodies that await
untyped values can be stackless.
- An async generator's return through a finally region takes its await
turn through the coroutine when the body is one.
Debug builds, libraries, worker programs and Windows executables keep
fibers, as for stackless async functions.
Stage L5 of the localized-failure design.
- SC6101 names a dynamic slot as the cause of a likely-hot untyped function
('tool' is a dynamic slot, declared 'ToolDefinition', which does not
compile).
- Coverage counts the dynamic operations each slot's values take part in,
lists the costly slots first, and puts the count in the JSON envelope.
- A build whose only performance warnings are dynamic slots closes with a
line about declared types instead of function types.
- Docs: a Dynamic slots section in the coverage guide, the limitations
page bullet, and --fail-on=dynamic-slots in the CLI reference and usage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pipeline stage that arrives as an untyped value now follows Node's
stage ladder for more shapes. As the first stage, a function is a source
function: the pipeline calls it with { signal } while it is set up and
pumps its result, which must be iterable (ERR_INVALID_RETURN_VALUE
otherwise), and a { readable, writable } pair pumps its readable side.
After the first stage, the pipeline call is lowered once per combination
of its first three untyped stages being functions or not, and a typeof
test picks the branch at run time. A function stage is called with the
previous stage and { signal }; before the last stage its result must be
async iterable, and as the last stage a promise resolves the pipeline's
value and an async iterable feeds the PassThrough pipeline() returns
(SCR_PIPE_DEST_DYN). Any other value that is not a stream becomes
Duplex.from of it, which the next stage reads from without the previous
stage being piped into it, and Duplex.from's ERR_INVALID_ARG_TYPE for
the values it rejects.
An untyped stage followed by an untyped function stage hands its value to
that stage through a cell created per pipeline call, so the function
receives the previous stage's own iterator, as Node's makeAsyncIterable
answers it, instead of an async generator over it.
Untyped web streams, pairs after the first stage, and function stages
that return a stream throw the not-supported error.
RUNTIME_ABI_VERSION goes from 45 to 46 for the new stream exports.
Records inherit toString, toLocaleString, valueOf, hasOwnProperty,
isPrototypeOf and propertyIsEnumerable. Calls of these methods by name
run an own callable entry when the record's type allows one and the
record holds it, and otherwise the built-in with the record as receiver;
toLocaleString, valueOf and isPrototypeOf on records were refused, and
toString ignored an own entry. Keyed reads of records of functions answer
the built-in as a native function value when the key misses: bound to the
record when called at once, with an undefined `this` otherwise. The
backend fuses the read with its fallback, so hits cost nothing extra.
Records of other value types keep reading undefined for these names; the
limitations page says so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
A pipeline whose first stage is a checked-dynamic value now checks it at
run time, as Node's stage ladder does: a stream is piped as before, and a
string or a value with a Symbol.asyncIterator or Symbol.iterator method is
pumped into the next stage through an async generator over its iterator.
A typed Set, Map, generator or async generator object as the first stage
takes the same path. This covers the callback and stream/promises forms,
with or without function stages and pipeline options.
A write whose callback fails synchronously no longer sets needDrain, so no
'drain' follows the error and a pumped source stops at the failed write,
as in Node.
RUNTIME_ABI_VERSION goes from 37 to 42 for scr_stream_dyn_iterable_source.
tls.connect, https.request and http2.connect verified the server against
the default CAs when ca was an empty Buffer, because the runtime read a
zero-length ca as none. The compiled calls now pass a null ca for none
(an omitted ca, or an empty string, which Node reads as no ca) and a
non-null one for any Buffer, and runtime option records read ca the same
way, so an empty Buffer or array trusts no certificate, as in Node. The
connection pool keys tell an empty ca from none. An empty array, which
threw, is an empty trust store too. A runtime-valued empty string ca in
http2.connect is refused instead of being read as an empty store.