Chains of global one-character replacements (the HTML-escape idiom) run
as one scr_str_replace_chars call. It searched for the next match eight
bytes at a time and started the search over after each replacement.
It now reads 16-byte blocks with SSE2 or NEON (8-byte words elsewhere on
little-endian hosts) and replaces every match of a block before reading
the next one. The table of characters and replacements is kept for the
next call with the same immortal operands, which are the literals the
compiler passes, instead of being rebuilt from the replacement text on
every call.
mentionsTypeParameter walked union constituents and type arguments
without remembering the types on its path. A parameter typed with an
alias that reaches itself through its own type arguments, such as
`type Tree = string | Tree[]` in `function f<T>(x: T, ...trees:
Tree[])`, recursed until the compiler ran out of stack. The walk now
skips a type it has already entered; a type parameter reached through
any other path is still found.
The TypeScript 7 parity lane checks every corpus program against a
recorded preflight and order baseline. 6950-string-field-fast-paths.ts
arrived without one; this records it from the current native lane.
- Port messages that a 'message' listener posts while more worker
messages are queued are delivered after those messages and before
setImmediate callbacks, as in Node. The ports' poll phase waits while a
batch's next message is queued, and the loop pass continues once more
after the batch when a port has something to deliver.
- A mailbox counts its queued events per kind as they are posted and
taken, so starting a batch reads the count instead of scanning the
queue under the lock. Messages that alternate with a worker's captured
stdout or stderr start a batch each, which made delivery quadratic:
20000 such messages took 5.6 s to drain, and now take 0.5 s (Node 0.57 s).
- The continue hook passes on that the batch's next message is at the
head, so the dispatch that follows does not lock the mailbox again.
- Corpus 6212 checks the order of worker messages, a MessageChannel
message posted by the first listener, and setImmediate against Node.
6211's costs are now taken from its CI timings.
Readable.push and Writable.write with a union chunk (Buffer, string and
null arms) read the chunk's tag and payload from a tagged union box. A
union with one reference arm and null, such as `string | null` or
`Buffer | null`, is the arm pointer itself, so `push(line)` with
`line: string | null` and `push(done ? null : text)` read past the
string and crashed. Both now read the tag and payload with unionTag and
unionPeek, as the other union consumers do. unshift and end refuse
union chunks and are unchanged.
charAt and s[i] on a string proven ASCII at an exact in-range index now
return the character from a module-level table of the 128 one-character
strings, emitted with the helper as immortal literals with the same
capacity word and key hash as the runtime's single-character statics.
An exact integer index (a counted-loop counter) skips the double
conversion. Every other string or index calls scr_str_char_at as before.
An event that typed code only listened to with a listener taking no
parameters had no bridge entry: the scan marked a listener as typed only
when one of its parameters was not a checked-dynamic value. Emitting the
event through an `any` reference then threw instead of calling the
listener. A TypeScript listener with no parameters now marks the event as
typed. A JavaScript function without parameters still registers as before,
since it can read `arguments`.
The first Object.freeze, Object.seal or Object.preventExtensions of a typed
function creates its property table from the function's dynamic box. The
box had no name, so later `f.name` reads answered "". The box now carries
the function's JS name, as a closure flowing into any checked-dynamic slot
does.
The runtime's own callers (published values, os and node constants, the
private-range block list, Wasm exports) freeze data they built, and now
call scr_dyn_freeze_data, the freeze for values that are not functions.
Object.isFrozen, Object.isSealed and Object.isExtensible read a
function's existing table and answer false, false and true when it has
none. Fully typed programs therefore link no dynamic-tier object code
for these calls again.
tsc wraps every CommonJS import in its helpers: `const events_1 =
__importDefault(require("events"))`, `const path =
__importStar(require("path"))`. cjs-tsc-interop.ts reads such bindings of
program modules as plain require bindings, so the helper declarations go
dead, but a builtin module is not a program module: its binding kept the
helpers live, and their probe of the module's top-level `this`
(`(this && this.__importDefault) ||`) failed at load ("property access
through top-level 'this'"). That is the shape of hundreds of tsc-compiled
files in a typical dependency tree.
A builtin carries no `__esModule`, so `events_1.default` is the module and
the namespace copy reads the module's members. When every reference has
that form, a package-file stage now respells the binding as the plain
require() of the builtin and `x.default` as `x`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A thread's event loop delivered one message from its workers or its
parent per loop turn, so timers and setImmediate callbacks ran between
messages that were already queued. Node's MessagePort delivers, in one
callback, the messages queued when it starts and those that arrive
meanwhile until it has taken 1000, with a microtask checkpoint after
each, before the loop goes on to the check phase.
- scr_worker_dispatch starts a batch when it delivers a message: the
messages queued then, and at least 1000 in all.
- scr_workers_more continues the loop pass while the next event in the
thread's mailbox is a message and the batch has room; other events
(online, exit, error, stdio) keep their order and their turns.
- scr_mailbox_head_kind and scr_mailbox_count_kind read the mailbox's
first event kind and the number of queued events of a kind.
- Corpus 6211 checks the order of messages, nextTick, microtasks and
setImmediate for worker-to-parent and parent-to-worker messages
against Node.
Revert 3a27a92cb ("Forward at most four emit arguments through
EventEmitter method values") and 0cf62ebec ("Answer EventEmitter methods
read as values off compiled emitters"). Under the scope-B plan,
EventEmitter methods read as values, `EventEmitter.prototype.on` in
JavaScript and constructor-function emitters belong to Node islands, not
the native compiler. The typed/untyped listener sharing and the
catchable throw in place of the trap stay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A union, unknown or any value is converted to a dynamic value before it
is thrown, and promise rejections and other runtime throws carry dynamic
values too. A catch binding then holds a dynamic payload, which the
native class test does not recognize: `e instanceof C` was false for
every class of a hierarchy, and a read narrowed by it trapped. A test
through a parameter of type unknown was already correct.
When a class or one of its subclasses crosses into unknown, the class
dynamic-dispatch pass now routes the catch-binding tests of that class
(`e instanceof C`, the reads it narrows and `e as C`) through helpers
that keep the native test and answer a dynamic payload with the dynamic
class test, as standalone classes already do. The new "dynamic" caught
test selects that payload, so native payloads are not converted.
Programs whose classes never cross into unknown compile as before.
fs.promises.rm(path, options) accepts the { recursive, force, maxRetries,
retryDelay } literal options that rmSync accepts. It runs the same removal
and rejects with its errors.
Removing a directory without recursive now throws Node's ERR_FS_EISDIR
SystemError from rmSync and fs.promises.rm. A retryDelay outside int32 >= 0
or a maxRetries outside uint32 throws ERR_OUT_OF_RANGE. rmSync checks these
options before the path, and fs.promises.rm checks the path first, as Node
does.
Fixes#1615
Co-authored-by: jlucaso1 <55464917+jlucaso1@users.noreply.github.com>
A generic class instance bound its type parameters only to their mapped
IR types, so a literal argument widened (K = "required" became string)
and a member typed by a conditional over it, such as
`get(): Value<T, K>` with `type Value<T, K> = K extends "required" ? T :
T | undefined`, refused with SC2001.
Class instantiation now also keeps the type arguments as checker types
for the members it collects and lowers, as generic functions do. A
literal argument for a type parameter that a conditional type in the
class depends on is part of the instance key, so Field<string,
"required"> and Field<string, "optional"> are separate instances. The
conditional evaluation decides `X extends Y` by assignability when both
sides are primitive or literal types.
Fixes#1240
Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>
A generic arrow or method held by an object literal is also lowered with
its type parameters erased, for the literal's own value. In that lowering
the object it returns builds as a checked-dynamic value, so a callback
member with an optional or defaulted parameter, such as
`decode(value, at = [])`, met a slot type that maps to a dynamic value.
The completed-signature check refused it with SC1090, although the same
code in a standalone generic function compiled.
A slot whose type maps to a dynamic value now takes the function the same
way an 'any' slot does: the value boxes into a checked-dynamic function,
which completes omitted arguments with undefined.
Fixes#1373
Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>
TypeScript accepts parentheses and non-null assertions around the
reference an assignment, a `++`/`--` update or a for-in/of head writes
(`(x) = 1`, `h.sums[k]! += 10`), and both are erased at run time. The
compiler now reads its source files with the AstFile writeTargets option:
the writer's `left`, `operand` or `initializer` is the reference itself
and the reference's `parent` is the writer, so lowering and every analysis
of a binding's or member's writes see one shape. The reference keeps its
own source span. Files read without the option, such as type nodes from
the checker and the AST conformance tests, keep TypeScript's exact tree.
Fixes#1625
Co-authored-by: ejc3 <1694850+ejc3@users.noreply.github.com>
tls.connect and tls.createServer accept ALPNProtocols as an array of
strings, a Buffer or a Uint8Array in the wire format. The server answers
with the first of its own protocols that the client offers, and both
sockets report it as alpnProtocol. A client that offers none of the
server's protocols fails with ERR_SSL_TLSV1_ALERT_NO_APPLICATION_PROTOCOL.
minVersion and maxVersion bound the handshake, and getProtocol() reports
the version it picked. Ranges that do not overlap fail with Node's
ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION or ERR_SSL_NO_PROTOCOLS_AVAILABLE,
and invalid versions throw Node's TypeError. tls.connect's timeout option
arms the socket's inactivity timeout before it connects.
Refs #1438
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
Global isFinite now converts any argument with ToNumber before the test,
the same way global isNaN does, and only Number.isFinite stays strict.
Before, an argument that wasn't typed as a number, such as a checker-unknown
or dynamic value, refused with SC2012.
The shared ToNumber conversion for these arguments (isNaN, isFinite, the
Math functions, String.fromCharCode, DataView offsets) now handles every
value kind: strings parse as numeric literals, booleans and null convert
to numbers, undefined is NaN, and dynamic values call valueOf/toString or
throw Node's TypeError for a Symbol or BigInt. Before, a dynamic value
went through a checked number cast and threw a TypeError for a string,
and a string or boolean in a JavaScript file reached a run-time SC1090.
Fixes#1386
Co-authored-by: espetro <26749475+espetro@users.noreply.github.com>
TypedArray.of(...items) on the numeric typed-array constructors creates a
new array of its arguments. Each argument converts with ToNumber and then
to the element type, in argument order after all arguments are
evaluated, as in Node. Arguments that are all numbers copy directly;
other values and spreads collect into an array that goes through
TypedArray.from's conversion.
Spreading a dynamic iterable into a call's items (Array.of, push,
TypedArray.of) now drains it through the iterator protocol with the
spread call's TypeErrors, and a typed iterable spread into dynamic items
boxes and drains the same way. Before, both reached a run-time SC2020.
Fixes#1440
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
A plain function's own properties (name, length, prototype, and the ones a
program assigns, as in `hash.outputLen = 32`) live in its property table.
Object.freeze, Object.seal and Object.preventExtensions of a function now
act on that table and answer the function, and Object.isFrozen,
Object.isSealed and Object.isExtensible answer from it, as in Node:
- Object.freeze of a function was refused as a possibly-aliased value,
and Object.isFrozen of one threw SC2020;
- Object.isExtensible answered false and Object.isSealed true for every
function.
Adding a property to a frozen or non-extensible function throws Node's
TypeError; assigning a read-only one throws the runtime's existing
read-only TypeError. Class constructors keep their fences. This lets
@noble/hashes' createHasher return its frozen hasher, so the sha256
program in the issue prints the digest Node prints.
Fixes#1437
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
merge-profiles wrote the merged profile to `<output>.tmp` and renamed it
into place. Opening that fixed path followed an existing link there and
truncated its target. The temporary file is now created exclusively with
createUniqueFile next to the output, as emission does, and is removed when
writing fails or the stream reports an error.
A comparator that chains key differences with `||`, such as
`(b.score ?? 0) - (a.score ?? 0) || a.id - b.id`, ran through the
comparator sort: TimSort called the closure once per comparison. Each
term orders one number field, ascending or descending, and a term that is
0 or NaN falls through to the next, so the chain orders elements by their
keys with the first deciding. With no key NaN or -0 that order is
consistent, and a stable sort by the keys gives the comparator sort's
permutation.
The recognizer now accepts such chains of up to eight terms over number
fields of records and class instances, and a key may read an optional
number field with a number default (`x.k ?? 0`), also as a single key. The
runtime reads and checks every key of every element before it moves a
slot, then runs stable keyed passes from the last key to the first. A NaN
or -0 key, or a chain with any other term, keeps the comparator sort.
`a instanceof Uint8Array || (... a.BYTES_PER_ELEMENT === 1)`, the
@noble/hashes isBytes guard, narrows `a` to never on the right side when
the guard is specialized for a Uint8Array argument, so the read missed
the typed-array member lowering; it now answers the element size from
the receiver's storage. The checked-dynamic read of BYTES_PER_ELEMENT on
a typed array or Buffer (an untyped argument) answered undefined; it now
answers the element size, as each kind's prototype does in Node.
Refs #1437
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>