Commit Graph
1859 Commits
Author SHA1 Message Date
Chris Tate 03af6fb9ff Iterate classes with object-literal iterators and close them on early exits (#1860) 2026-10-24 20:30:36 -05:00
Chris Tate f1bc74f934 Give the module namespace handle its own tag (#2161) 2026-10-24 20:29:29 -05:00
Chris Tate 304612b7e2 Inline destructured literal results and read ASCII slice ranges inline (#2151) 2026-10-24 20:11:44 -05:00
Chris Tate 50421e1cbd Merge main 2026-10-24 20:05:11 -05:00
Chris Tate 53bddc6ed7 Give the module namespace handle its own tag 2026-10-24 18:46:36 -05:00
Chris Tate 9e55835014 Compile more npm and CommonJS JavaScript dependencies statically (#2122) 2026-10-24 18:30:56 -05:00
Chris Tate 6997d8b211 Allocate stream ticks from the small-block allocator, and emit stream events by immortal names (#2152) 2026-10-24 18:22:03 -05:00
Chris Tate 29e5bacb9c Pump untyped iterable pipeline sources (#2111) 2026-10-24 18:03:36 -05:00
Chris Tate e25d871a34 Allocate stream ticks from the small-block allocator, and emit stream events by immortal names
Each deferred stream step (afterWrite, emitReadable, resume and the
others) queued a ScrStreamTick and a process.nextTick entry, both from
libc calloc and free. They now come from the runtime's small-block
allocator, as do the nextTick entries scheduled through
scr_next_tick and scr_process_write_callback.

The stream unit emitted each event with a newly allocated name string.
The names it emits ("data", "end", "drain", "finish" and the others)
are now immortal static strings, so an emit allocates no name.
2026-10-24 17:45:59 -05:00
Chris Tate 43ff8a1122 Inline destructured literal results and read ASCII slice ranges inline
- A function whose whole body is `return [a, b]` or `return { a, b }`,
  with number, boolean or string fields, is now inlined where its result
  is only destructured (`const [head, tail] = split(text, at)`). The
  parameters are declared from the arguments, then each field straight
  into its destructured binding, in the literal's order; an unread field
  still evaluates into a binding of its own. No record is built, and a
  slice field read only through its length or char codes becomes a
  scalar slice that copies nothing. Arguments may own their value, but
  as for numeric records nothing inside an argument may hold a
  temporary of its own.
- A scalar slice of a proven-ASCII string computes its range inline:
  the bounds clamp over the byte length exactly as scr_str_slice_range
  does, and no boundary can split a surrogate pair. Other strings keep
  the runtime call, which gains the same ASCII fast path.
- value-records' tuples section, `[head, tail] = split(...)` read only
  through lengths, goes from two heap slices per step to none.
2026-10-24 17:35:25 -05:00
Chris Tate aa9664bf8a Propagate exceptions by unwinding from directly called functions on Linux (#2115) 2026-10-24 17:15:36 -05:00
Chris Tate bc9fdb9c08 Answer stream, StringDecoder and handle-object members in untyped code as Node does (#2105) 2026-10-24 17:11:01 -05:00
Chris Tate 8d9705e22d Run CommonJS top-level return, untyped assert forms, and untyped instanceof Object as Node does (#2101) 2026-10-24 17:01:39 -05:00
Chris Tate c37963b8aa Let objects inherit from JavaScript class prototypes (#2078) 2026-10-24 16:58:49 -05:00
Chris Tate 54cb7af31b Settle BigInt quotients from the top limbs, and vectorize limb shifts (#2144) 2026-10-24 16:50:56 -05:00
Chris Tate 12e16cdb32 Find weak collection entries through hashed indexes (#2004) 2026-10-24 16:40:37 -05:00
Chris Tate e22cdf358b Decode an untyped stream unshift's string chunk with its encoding
`s.unshift("e9", "hex")` on a native stream held as an untyped value
dropped the encoding argument and put the string's UTF-8 bytes at the
front. A byte-mode stream now takes a string chunk with an encoding as the
bytes Buffer.from(chunk, encoding) holds, as Node's
readableAddChunkUnshift converts it, and an unknown encoding throws
ERR_UNKNOWN_ENCODING. Object-mode streams keep the string as the entry.
2026-10-24 16:28:43 -05:00
Chris Tate e00d3fd911 Check comparison operands and walk instanceof chains as Node does
- The assert comparison methods read as function values (equal,
  strictEqual, deepEqual, partialDeepStrictEqual and their negations)
  throw Node's ERR_MISSING_ARGS TypeError, `The "actual" and "expected"
  arguments must be specified`, when called with fewer than two
  arguments, where `[assert.equal][0]()` passed.
- Untyped `instanceof Object` starts at the value's prototype, so
  Object.prototype itself, whose prototype is null, is not an Object.
- Untyped `instanceof Function` follows the whole prototype chain through
  objects and functions: an object whose prototype is a function, or a
  function whose prototype was set to such an object, is a Function.
2026-10-24 16:22:03 -05:00
Chris Tate 6a8ba89d9f Make Dates mutable objects with Node's setters and conversions (#2072) 2026-10-24 16:21:06 -05:00
Chris Tate 190fb8d58e Mix weak-collection hashes in 64 bits on 32-bit targets
uintptr_t is 32 bits on wasm32, where the weak-collection hashes shifted it by 33 and 56 bits. The hashes now widen keys, owners and kinds to uint64_t before mixing; 64-bit targets compute the same values as before.
2026-10-24 16:01:33 -05:00
Chris Tate d4fcd787a8 Reference the unwinder weakly from the runtime's raise
scr_unwind.c is linked into every Linux executable, and its references
to _Unwind_ForcedUnwind and _Unwind_GetCFA pulled the unwinder out of
libgcc_eh (or Zig's libunwind) even for programs that never raise, which
grew a static hello world past its size ceiling. The references are now
weak: programs that raise reference the personality routine from their
landing pads, and its unit links the unwinder in.
2026-10-24 15:38:37 -05:00
Chris Tate ab341be202 Settle BigInt quotients from the top limbs, and vectorize limb shifts
A quotient-only BigInt division with a one-limb quotient returns Knuth's
estimate without a pass over the operands when the top limbs prove it:
with rhat * B + u0 - qhat * d0 at least qhat, a - qhat * b stays positive
whatever the lower limbs hold, since they subtract less than
qhat * B^(n-2). Otherwise one pass settles the estimate as before.

The add and subtract loops process eight limbs a pass, which rebuilds
the carry half as often. Left and right shifts, including the operand
normalization of the general division, form each limb from two source
limbs in a restrict-qualified helper with no dependency between limbs,
so the loops vectorize.
2026-10-24 15:19:33 -05:00
Chris Tate ea495056d8 Answer 'in' on native Node handles from their declared members (#1949) 2026-10-24 14:51:03 -05:00
Chris Tate 3896957230 Report JSON.parse syntax errors with V8's messages and positions (#2129) 2026-10-24 13:34:12 -05:00
Chris Tate c244ddc4c5 Answer 'send', 'disconnect' and 'channel' in a child process from its IPC channel
A child process has these members only when it was created with an IPC
channel (fork, or an "ipc" stdio entry), and keeps them after the channel
closes and after the child exits. The 'in' test on native handles answered
true for every declared member, so a child spawned without IPC reported
them and entered IPC-only branches. The three names now answer from the
runtime: scr_child_ipc_present reports whether the child was created with
a channel.

Corpus 8290 tests a forked and a spawned child before and after
disconnect and exit.
2026-10-24 13:18:19 -05:00
Chris Tate 4f0975cbbe Compile more npm and CommonJS JavaScript dependencies statically 2026-10-24 12:01:26 -05:00
Chris Tate ff70c33e38 Divide one-limb BigInt quotients in one pass and chain limb carries
Dividing BigInts of the same limb count, where the quotient is one limb,
takes Knuth's quotient estimate from the top limbs as they read
normalized and settles it with one pass of a - q * b over the operands,
instead of copying both operands shifted and running algorithm D. The
remainder, when wanted, is that pass's result.

The limb loops for add, subtract, multiply by a limb, and multiply-add
and multiply-subtract by a limb process four limbs a pass, with the
carries in add-with-carry chains (__builtin_addcll and __builtin_subcll
where the compiler has them).
2026-10-24 11:59:33 -05:00
Malte UblandClaude Opus 5.5 d037c66e8c Report JSON.parse syntax errors with V8's messages and positions
JSON.parse's SyntaxError messages were shaped like V8's but differed from
Node's in several ways:
- Every positional message now ends with V8's "(line L column C)", where
  \n, \r and \r\n end lines.
- Positions are UTF-16 offsets, as in Node, not byte offsets.
- An object or array cut off after '{', a property name, a value or an
  array element reports the token V8 expected ("Expected property name or
  '}'", "Expected ':' after property name", "Expected ',' or '}' after
  property value", "Expected ',' or ']' after array element") instead of
  "Unexpected end of JSON input".
- After a comma in an object the message is "Expected double-quoted
  property name".
- An unterminated string reports the end of the input, and a truncated
  \u escape is a "Bad Unicode escape" there.
- A leading zero followed by a digit is an "Unexpected number".
- A misspelled literal names the first differing character, or the end of
  the input.
- An unexpected character is named with the source as V8 does: whole when
  it is under 21 units, otherwise ten units of context on each side; the
  sources "NaN", "Infinity", "undefined" and "[object Object]" name only
  themselves; a quote or a number there is an "Unexpected string" or
  "Unexpected number".

The new corpus program 7993-json-parse-error-messages pins 76 inputs
against Node, including non-ASCII text before the error, line breaks, and
long sources with split surrogate pairs at the context edges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 09:55:05 -07:00
Chris Tate 2967a1b8c0 Propagate exceptions by unwinding from directly called functions on Linux
Emitted functions report an exception by returning with the thread's
pending flag set, and every caller tests the flag after each call that
may throw. On Linux executables (glibc and musl), functions reached only
by direct calls now propagate exceptions by unwinding the native stack
instead:

- unwindAbiFunctions selects the may-throw functions that are not async
  or generators, constructors, virtually called or overridden methods,
  closure targets, the entry, library exports, module initializers or
  callees of coroutine bodies.
- A native unwind costs about 2 µs a raise and 0.1 µs a frame, so functions that
  exceptions are likely to cross keep the flag: those with a `throw` of
  their own, those up to three calls above one, and recursive functions,
  counting calls through methods, closures and callbacks.
- Their propagating exits call scr_unwind_raise (scr_unwind.c), a forced
  unwind through _Unwind_ForcedUnwind with the exception still in the
  active cell.
- Direct calls to them are `invoke`s without a pending check. The
  landing pad runs the frame's usual unwind path: it releases the frame's
  values and then enters the enclosing try handler or resumes the unwind.
- Remaining plain calls to them (wrappers, adapters, runtime-facing entry
  points) become invokes whose landing pad continues on the pending flag,
  or resumes when the wrapper is itself invoked. Every other call is
  marked `nounwind`, so inlining a raising function at an invoke keeps its
  calls plain.
- When emitted text breaks these rules (a raising function's address is
  taken, or an invoked function returns with the flag pending), the
  module is emitted again with those functions on the pending flag.

Emitted code does not declare scr_unwind_raise `noreturn`, so sanitized
builds do not run the sanitizer's no-return hook, which needs stack
bounds that fiber stacks do not report. Instead, scr_unwind_raise clears
the shadow of the frames it unwinds.

Executables link a static unwinder: libgcc_eh with the host toolchain and
the glibc runtime packs, and Zig's libunwind with Zig and the musl packs.
macOS, Windows, Android, WASI, libraries and debug builds keep the
pending flag everywhere and emit the same code as before.

The throw-chains runtime workload measures exceptions caught 2, 5 and 20
recursive calls above the throw, through direct calls and a callback,
and a parser that backtracks by throwing.
2026-10-24 11:03:34 -05:00
Chris Tate 53baad4ebe Pump untyped iterable pipeline sources
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.
2026-10-24 10:12:06 -05:00
Chris Tate aa1c26845c Cache untyped property access in programs that declare classes (reland) (#2102) 2026-10-24 09:55:54 -05:00
Chris Tate 905a290817 Copy typed array sets of the same element kind on shared memory as bytes (#2094) 2026-10-24 09:07:57 -05:00
Chris Tate f2e710d65f Answer stream, StringDecoder and handle-object members in untyped code as Node does 2026-10-24 09:05:16 -05:00
Chris Tate 427e383b5e Cache untyped property access in programs that declare classes (reland)
Reapplies #2017, which #2038 reverted after effect's observability-otlp
fixture crashed in plain builds. The crash was the candidate-buffer
corruption that the previous commit fixes: the cached property sites
change when the collector runs, and with enough environment variables a
pass buffered a string box that was later pooled while still buffered.
2026-10-24 08:22:30 -05:00
Chris Tate d9f1457cd5 Run CommonJS top-level return, untyped assert forms, and untyped instanceof Object as Node does 2026-10-24 08:07:52 -05:00
Chris Tate 7ba3827bd4 Merge main 2026-10-24 06:54:25 -05:00
Chris Tate ead125a5c5 Merge main 2026-10-24 06:47:32 -05:00
Chris Tate 38c30d2e91 Merge main 2026-10-24 06:45:47 -05:00
Chris Tate 314024a8c7 Copy typed array sets of the same element kind on shared memory as bytes
TypedArray.prototype.set with a SharedArrayBuffer view as its source or
target converted every element through a double, even when both arrays
had the same element kind. That cost about 5 ns per byte, and it changed
NaN payloads: a Float32Array signaling NaN came back quieted (7fa00001
became 7fe00001), where Node copies the bytes.

The same element kind now copies the source's bytes with the relaxed
shared stores. A source that overlaps the target range, a view of the
same buffer in a program without workers, is copied out first. Different
element kinds still convert each element.
2026-10-24 06:29:47 -05:00
Chris Tate 8a02468073 Take number and string boxes out of the candidate buffers before pooling them
scr_dyn_release returned the last reference of a number or string box to
the free list without scr_cyc_dead, because the release paths never buffer
those boxes. A collector pass can: capture boxes trace their payload, and
scr_xg_release, which settles the cross-generation edges a restricted pass
skipped, calls scr_cyc_on_release on the target whatever its kind. The
pooled block then stayed in its candidate buffer, and the next box that
reused it carried the stale buffer position. Its death swapped another
object into that slot, which corrupted the buffers and crashed a later
scr_cyc_on_dead.

The fast path now takes a buffered box out of its buffer first. The
sanitized runtime does not pool boxes, so only plain builds were affected.
2026-10-24 05:57:26 -05:00
Chris Tate 7278c3c467 Read a union's discriminant once in checked casts (#2073) 2026-10-24 04:51:36 -05:00
Chris Tate 0f90e07603 Process non-ASCII URL hosts with UTS #46 as Node does (#2088) 2026-10-24 02:48:24 -05:00
Chris Tate 1e90263517 Support Symbol.toStringTag class fields and getters (#2037) 2026-10-24 01:21:32 -05:00
Chris Tate 20339d9c27 Process non-ASCII URL hosts with UTS #46 as Node does 2026-10-24 01:13:00 -05:00
Chris Tate 2764288376 Dispatch untyped calls on plain arrays without the method ladder
An untyped call of an array method went through the whole dispatch ladder
on every call: the receiver-kind tests for handles, proxies, typed refs,
objects, functions and primitives, then the array section's prologue
(the hole scan and the frozen, sealed and extensibility tables) before
the method itself.

A dense array without holes, named properties or a prototype of its
own, whose length and elements are writable, now runs push, pop, shift,
at, includes and indexOf directly from the call site's cached method id.
An argument that could run user code (a non-number index, a fromIndex)
still takes the ladder, as does every other receiver. A statement-position
push calls an entry that does not box the new length.

Calls on arrays that the direct path refuses now also follow Node:
- an array's own method, or one on its custom prototype chain
  (Object.setPrototypeOf), runs instead of the builtin;
- push on a sealed or non-extensible array throws "Cannot add property
  N, object is not extensible", and pop or shift on a sealed array
  "Cannot delete property 'N' of [object Array]".
2026-10-24 00:43:56 -05:00
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 de5557ce5a Answer Error.prototype for Object.getPrototypeOf of an Error (#2058) 2026-10-24 00:04:29 -05:00
Chris Tate cbb87e8812 Address review comments 2026-10-23 23:49:40 -05:00
Chris Tate 3b951ef4e2 Let objects inherit from JavaScript class prototypes 2026-10-23 23:43:30 -05:00
Chris Tate fe3fd0de44 Parse URL hosts and paths as the URL Standard does, and inspect URLs as Node does (#2071) 2026-10-23 23:38:03 -05:00