Commit Graph
430 Commits
Author SHA1 Message Date
Chris Tate 7bf50dd9cc zlib: Brotli compression and decompression over the vendored brotli 1.2.0 (#2053) 2026-10-24 00:30:21 -05:00
Chris Tate 1af3a86370 Compile unions of typed-array classes (#2011) 2026-10-23 20:00:12 -05:00
Chris Tate 37df3c4dfd Support split CommonJS require bindings (#1945) 2026-10-23 19:50:13 -05:00
Chris Tate 4206155ac9 Convert non-null asserted operands that hold undefined or null as Node does (#2049) 2026-10-23 19:25:15 -05:00
Chris Tate bf68b3860c Skip split require calls by identity and climb parentheses in early-read checks 2026-10-23 19:05:58 -05:00
Chris Tate 9adb9692cd Accept file URL paths in existsSync and fs.promises (#2042) 2026-10-23 18:47:49 -05:00
Chris Tate a9da37b82e zlib: Brotli compression and decompression over the vendored brotli 1.2.0 2026-10-23 18:11:49 -05:00
Chris Tate 4b6a6e6bc4 Convert non-null asserted operands that hold undefined or null as Node does
`x!` performs no runtime check, but the compiler extracted the asserted arm
with a checked narrow, so a value that still held undefined or null threw
"TypeError: undefined is not representable in the target union" wherever it
was used. In Node the value reaches the operator, which converts it.

When `x!` is an operand of an arithmetic, bitwise or relational operator,
`+`, a compound assignment, a unary `-`, `+` or `~`, or an argument of a
Math function, and `x` is a union of numbers, strings, booleans, null and
undefined, the operand is now converted with ToNumber: undefined is NaN and
null is 0. As an operand of an equality operator, of `!`, of a template
span, of `+` with a string, or of a comparison with a string, the union
itself reaches the operator, so `x! === 0` is false and `${x!}` is
"undefined". Array and typed-array element reads keep their handling of a
missing element, and `+` whose result TypeScript types as a string keeps
the checked narrow where Node's result is NaN.

`x == null` on a union with both null and undefined arms evaluated its
operand twice, so an operand other than a plain read was refused. It is now
evaluated once into a hidden local.

Corpus 7392.
2026-10-23 17:34:59 -05:00
Chris Tate 68eda48251 Merge main 2026-10-23 17:27:47 -05:00
Chris Tate 02597fa3e8 Merge main and give split require cycles the const form's admission 2026-10-23 17:15:33 -05:00
Chris Tate 932837ee23 Compile string-key destructuring of any values and cast-guarded Buffer aliases (#2034) 2026-10-23 16:55:29 -05:00
Chris Tateandjlucaso1 0b83599af4 Accept file URL paths in existsSync, fs.promises and order-checked fs calls
existsSync and the fs.promises functions accept a file: URL wherever
they accept a path. They convert it after their arguments are evaluated,
and a URL that does not convert gives false from existsSync and a promise
rejected with fileURLToPath's error from fs.promises, as in Node.

copyFileSync and renameSync check a string source for null bytes before
they convert a URL destination, and readFileSync checks a runtime
encoding before it converts a URL path, so the error Node reports first
is the one thrown.

Refs #1616

Co-authored-by: jlucaso1 <55464917+jlucaso1@users.noreply.github.com>
2026-10-23 16:27:40 -05:00
Chris Tate 4fe88e0821 Keep the cast marker on copied and defaulted Buffer aliases 2026-10-23 16:23:37 -05:00
Chris Tate e7943984a5 Bind both Dates before comparing, and cache only new Date locals as time values 2026-10-23 15:50:09 -05:00
e75174aeea Compile string-key destructuring of any values and cast-guarded Buffer aliases
Co-authored-by: patlux <4481570+patlux@users.noreply.github.com>
Co-authored-by: jlucaso1 <55464917+jlucaso1@users.noreply.github.com>
2026-10-23 15:13:03 -05:00
Chris Tate c016b97acb Fix CI on typed-array unions 2026-10-23 12:54:17 -05:00
Chris Tate 1221104388 Merge main and fix CI 2026-10-23 07:50:09 -05:00
Chris Tate b6d6eb60ae Replace a scheduled _construct hook, and keep runtime option values in order
Node's constructNT reads _construct on the construction tick, so an
assignment after the constructor and before that tick replaces a scheduled
construct hook. The assignment was dropped, so the original hook ran. It
now lowers to stream.replaceConstruct, which swaps the hook while its tick
has not run and has no effect after it or on a stream without a hook.

Runtime decodeStrings and defaultEncoding values are read into locals
before the constructor evaluates the other option members, while Node
evaluates the members in order. A runtime value after a member whose value
has effects (a call or an assignment) now refuses instead of reading a
value that member could still change.
2026-10-23 01:34:19 -05:00
Chris Tate 9dbb16d7f8 Support new.target in class constructors (#1917) 2026-10-22 21:52:42 -05:00
Chris Tate df9f83bbc1 Refuse new.target over function-local subclasses lowered later
Lowering collects a function-local class with the body declaring it, so
a local subclass whose function lowers after its base's constructor was
missing from new.target's dispatch, which then answered with the base for
its instances. The refusal no longer depends on that order: the program's
function-local classes are found once, and one that extends the
constructor's class, directly or through other classes its extends
clauses name, refuses new.target as a collected one does.
2026-10-22 21:47:37 -05:00
Chris Tate 6115135d96 Dispatch new.target over generic subclasses and own static names
A constructor's new.target dispatch skipped generic class families, so a
generic subclass instantiated only in a body lowered later answered with
the base's name, and `new.target === Base` held for its instances. A
family now has its own arm, tested by its instanceof interval, which holds
every instantiation: `new.target.name` reads the family's name and
comparisons with a program class are false. Under an instantiation, the
generic subclasses of its family are candidates too. `new.target` as a
value refuses generic subclasses, which have no common class value.

`new.target.name` reads a class's own static `name` field, which replaces
its name (a subclass has its own name); a static method `name` or a
non-string field is refused.
2026-10-22 21:18:48 -05:00
Chris Tate 8c67f04062 Throw Node's TypeError for class constructors called through call/apply (#1892) 2026-10-22 20:36:01 -05:00
Chris Tate 5966f23bd3 Keep the class-value call fence when a subclass declares a static call
`C.call(...)` through a class value typed as `Base` may run a subclass's own
static `call`, which Node calls instead of the class constructor. The
constructor-call lowering now applies only when no class the receiver can
hold declares a static `call` or `apply`: the class and its bases (any
static member of that name, including accessors, fields without
initializers and computed names), and, for a receiver that may hold a
subclass, every subclass. Otherwise the call keeps its existing
diagnostic.
2026-10-22 20:33:33 -05:00
Chris Tate 8d5b3377aa Represent Dates as objects with identity
A Date is a refcounted object holding its time value, shared with the
checked-dynamic Date handle, so it keeps its identity through unknown.
Dates compile in arrays (includes, indexOf, lastIndexOf, join, default
sort by toString() text), in Date | undefined and Date | null slots, as
Map and Set keys and values, in index signatures, and in records, tuples,
class fields, closures, promises and generators. === compares identity,
relational operators compare time values, new Date(date) makes a new
Date, and JSON.stringify writes toJSON().

Closes #225
Co-authored-by: Max B <max@tfq.at>
2026-10-22 20:33:09 -05:00
Chris Tate fbaa71a66f Run the fromEntries aliasing program and drop compiled Iterable entries from the iterator fences
Since Iterable<T> values compile (#1875), two diagnostics fixtures changed:

- fromentries-aliased-entries.ts compiles and prints what Node prints:
  the entries' one array stays shared by both fields. It moves to the run
  corpus as 7440-fromentries-aliased-entries.ts.
- iterator-slot-fences.ts no longer reports its Iterable<number>
  parameter (SC2020) and the call through it (SC2004). That part leaves
  the fixture, which keeps the class-iterator fence; corpus 7380-7383
  cover Iterable values.
2026-10-22 19:54:17 -05:00
Malte UblandClaude Opus 5.5 ac940ed936 Support new.target in class constructors
Inside a class constructor new.target is the class of the instance under
construction. A compiled new allocates the instance with its concrete class
before the constructor chain runs, and the class hierarchy is closed, so
new.target lowers to a static dispatch over the constructor's class and its
descendants (deepest instanceof first). new.target.name reads the exact
class's name and new.target === C is an exact-class test, without a class
value, so subclasses may construct with other signatures. new.target as a
class value requires every subclass to share the constructor's signature.
Plain functions and function-local subclasses keep explicit refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:43:09 -07:00
Chris Tate f132cf5f39 Compile Iterable<T> values (#1875) 2026-10-22 18:30:45 -05:00
Chris Tate a0c207cc25 Merge main 2026-10-22 18:12:20 -05:00
Malte UblandClaude Opus 5.5 089ec6739b Compile Iterable<T> values
Iterable<T> maps to a union of the sources that keep their own
representation (T[], Set<T>, Map<K, V> for [K, V] entries, string) and a
generator over T that holds every iterator. An array stays the same
multi-pass object; a generator stays single-pass. for-of gives the array
arm its own array loop, so iterating an Iterable that holds an array runs
at array speed. Spread, Array.from, the Set/Map constructors and
Object.fromEntries consume it; optional Iterables narrow into it;
generator functions may declare an Iterable return. Class iterables and
typed arrays are refused at the conversion with a named diagnostic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:00:41 -07:00
Chris Tate 98712c364a Merge main 2026-10-22 17:49:58 -05:00
Chris Tate 4f6c55a8e6 Narrow filter results through written type predicates with checked conversions (#1819) 2026-10-22 17:21:41 -05:00
Chris Tate e4dc0900d7 Fence arguments.length in function expressions that call themselves by name
A function expression stored in a function type with more parameters
folds `arguments.length` to that type's parameter count, the count every
call through the slot passes. A named expression that calls itself
through its own name passes only its declared parameters there, so no
constant answers both calls. Such a read now reports SC1090 with the
reason, and expressions that do not refer to their own name keep the
fold.
2026-10-22 10:09:08 -05:00
Chris Tate 2f3e25b431 Accept Buffer-compatible chunk annotations and keep super(undefined) in byte mode 2026-10-22 08:35:56 -05:00
Chris Tate 12563f720e Compile conditional spreads of several properties and 2026-10-22 07:58:53 -05:00
Chris Tate dd3ea7c00c Narrow filter results through written type predicates with checked conversions
`.filter` with a written type predicate (`(p): p is Cat => ...`, or a
guard function passed as a value such as `filter(isDog)`) was refused with
SC1090, because TypeScript does not check the predicate. An inferred
predicate that keeps several union arms (`v => v !== undefined` over
`(string | number | undefined)[]`) was refused too.

The filter loop now keeps the receiver's element type, and the result
converts to the predicate's type element by element
(filter-predicates.ts):
- a union element to one of its arms, or to a union of some of its arms,
  by its tag;
- a checked-dynamic element by dynCheck;
- a class instance to a subclass by its class;
- a tuple or record to one with the same fields, field by field.

An element the predicate's type does not describe, kept because the
predicate returned true for it, throws a catchable TypeError: "a kept
element is not a 'Cat' (a type predicate returned true for a value of
another type)". The diagnostics fixture filter-narrow.ts, which pinned
the three refusals, compiles now; corpus 7007 covers its cases.

Fixes #1680.
2026-10-22 07:47:03 -05:00
Chris Tate 41ef12e463 Construct builtins through stored constructors and fix function-local class names and dispatch 2026-10-22 06:44:59 -05:00
Chris Tate 3edec984d5 Compile JSX with the classic runtime (#1786) 2026-10-22 06:29:40 -05:00
Chris Tate 8ff58d2810 Match Node for generic arrow bindings used before their declarations (#1773) 2026-10-22 06:19:57 -05:00
Chris Tate df6de14c94 Support new tls.TLSSocket and tls.connect({ socket }) (#1791) 2026-10-22 06:01:47 -05:00
Chris Tate 1360830149 Support new tls.TLSSocket and tls.connect({ socket }) 2026-10-22 04:52:44 -05:00
Chris Tate c47a0e90ae Compile JSX with the classic runtime
Fixes #889
Co-authored-by: papack <33105506+papack@users.noreply.github.com>
2026-10-22 04:27:35 -05:00
Chris Tate b5fb4115e1 Merge main 2026-10-22 03:33:29 -05:00
Chris Tate 70697b45c3 Give generic arrow bindings Node's behavior before their declarations run
Generic arrow and function-expression bindings compile to shared
instances, and their declarations emit no store. Code that can run before
the declaration then diverged from Node.

- Inside a function, a nested function lowered before the declaration
  reads the binding through a TDZ box that nothing initialized. That
  covers closures above the declaration and hoisted function
  declarations. Such a read threw a ReferenceError even after the
  declaration ran. The declaration now stores the instance its declared
  signature pins into that box.
- At module scope, calls and pinned value reads went straight to the
  instances even when a hoisted function or an earlier closure ran them
  before the declaration. A binding with such a use now has a flag that
  its declaration sets.
  - Uses that can run first check the flag.
  - A let or const throws Node's ReferenceError before the arguments
    run.
  - A var runs the arguments and then throws "is not a function".
- Uses that follow the declaration stay direct calls: later statements,
  closures created after it, its own initializer, and function
  declarations that only such code calls.

A var binding inside a function that is called from a place that can run
before its declaration would need a per-call flag shared with its
closures. That shape now fails with SC1090 instead of calling the
instance.
2026-10-22 02:56:39 -05:00
Chris Tate 1773ffbef3 WebCrypto: the crypto global, node:crypto's webcrypto, and secret-key SubtleCrypto 2026-10-22 02:52:01 -05:00
Chris Tateandsaboursepideh 1a240fbf10 Support asymmetric keys, signatures and Ed25519 in node:crypto and WebCrypto
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-22 02:49:33 -05:00
Chris Tate 10820d488c Support SNICallback in tls.createServer (#1700) 2026-10-21 22:00:41 -05:00
Chris Tate ca446b2c30 Read the tls.createServer SNICallback in source order
The runtime call reads the cert and key options before the SNICallback,
so a cert or key initializer after the callback that reassigned the
callback's binding changed the callback the server used, and the server
could serve the wrong certificate. When SNICallback comes before a cert
or key entry, the callback is now saved just before the next of them is
evaluated. With the callback read in order, a callback expression with
side effects lowers instead of being refused.
2026-10-21 21:56:08 -05:00
Chris Tateandsaboursepideh a7e84cd48c Support SNICallback in tls.createServer
tls.createServer({ cert, key, SNICallback }) hands each ClientHello's
servername to the callback and serves the SecureContext it answers,
synchronously or later, or the default cert and key when it answers none.
An error answer closes that connection. This uses the SNI handling that
http2.createSecureServer already has. A callback expression with side
effects, or SNICallback combined with ALPNProtocols, minVersion, maxVersion
or the other options the runtime walk reads, fails with SC2020.

A tls.connect or https.request client whose peer closes or resets the
connection before the handshake completes now fails with Node's ECONNRESET
"Client network socket disconnected before secure TLS connection was
established" instead of closing without an error.

Refs #1438

Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-21 13:56:07 -05:00
Chris Tateandjlucaso1 d858d840f9 Accept file URL paths in synchronous fs functions
readFileSync, writeFileSync, appendFileSync, statSync, lstatSync,
readdirSync, mkdirSync, rmSync, rmdirSync, mkdtempSync, accessSync,
unlinkSync, chmodSync, chownSync, lchownSync, truncateSync, copyFileSync,
renameSync, realpathSync, realpathSync.native, openSync and opendirSync
accept a file: URL wherever they accept a path. The URL converts as
fileURLToPath converts it, with Node's errors for other schemes, hosts,
encoded separators and null bytes.

Arguments after a URL path that have side effects are evaluated into
hidden locals before the conversion, so they run even when it throws. A
URL path after a string path, or with options that are not literals,
fails with SC2020, because Node checks those arguments before it converts
the URL. existsSync and the fs.promises functions also fail with SC2020
for URL paths: existsSync answers false for a URL it cannot convert, and
fs.promises reports conversion errors as rejections.

Fixes #1616

Co-authored-by: jlucaso1 <55464917+jlucaso1@users.noreply.github.com>
2026-10-21 08:46:44 -05:00
Chris Tate 8b4e74d050 Type empty arrays filled by later writes inside functions by their reads (#1632) 2026-10-21 04:23:15 -05:00