Commit Graph
1433 Commits
Author SHA1 Message Date
Chris Tate 2e7db5da9e Encode messages into one block sized to their contents (#1760) 2026-10-22 01:03:36 -05:00
Chris Tate 5a414300a9 Store references to ProgramLifetime instances in 4 bytes on Linux (#1748) 2026-10-22 01:02:43 -05:00
Chris Tate 1a51223cd6 Say where the region's trap and memoryUsage accounting come from
Comments only: scr_trap is the runtime's failure path and the
standalone allocator test defines its own, and the region's chunks
count in process.memoryUsage() through scr_heap_stats.
2026-10-22 01:01:01 -05:00
Chris Tate f1e6addc81 Encode messages into one block sized to their contents
An encoded message allocated a node array (four nodes of 184 bytes to
start, trimmed when encoding ended), a property array per object, a copy
of every key and string, and the encoder's visit, identity and stack
arrays. A port that queues many messages kept all of it until delivery:
a {id, name} object took 15 allocations and 1.9 KB, and most of
postMessage's time went into page faults on that memory.

- The encoder builds nodes, properties and visits in arrays that start on
  the stack (8 entries), keeps borrowed references to keys and strings,
  and copies the result into one allocation: the header, the nodes, the
  properties grouped by node, the typed records and the text. A node is
  40 bytes; the per-kind fields share a union. ArrayBuffer contents stay
  in their own allocation.
- Decoding uses stack arrays for messages of up to 8 nodes and allocates
  the storage table only when the message has ArrayBuffer contents.
- Freeing a message frees the block, and walks the nodes only when the
  message holds ports, native handles or ArrayBuffer contents.
- The publication check reads the root before collecting the graph, so
  a message whose root is not frozen allocates nothing for it.

The same object message now takes 4 allocations and 302 bytes, and a
number 3 allocations and 129 bytes. bench/channel-messages runs in 161 ms
instead of 250 ms (Node: 340 ms), with a peak RSS of 45 MB instead of
115 MB, and bench/worker-messages in 273 ms instead of 348 ms.
2026-10-22 00:51:27 -05:00
Chris Tate 728dc6cd51 Count program-lifetime objects in memoryUsage and fix the runtime tests
- process.memoryUsage(): heapTotal counts the region's chunks that
  threads hold, minus the chunks discarded workers returned, and
  heapUsed counts them less the tails threads left unused when they
  moved to a new chunk.
- The region builds only where it can be used: Linux runtimes with
  the size-class allocator, which excludes the sanitizer runtimes
  (scriptc keeps 8-byte references there). The standalone allocator,
  cycle and young-nursery tests link again: the allocator test
  defines the scr_trap its region needs, and checks the region (below
  32 GiB, aligned, zeroed, chunks larger than one, heap statistics,
  threads).
- scr_plr_chunk has an initializer, like the other runtime globals.
2026-10-22 00:47:17 -05:00
Chris Tate 2d353e6bbe Compute Math.pow(2, n) for integer exponents from the result's bits 2026-10-22 00:33:54 -05:00
Chris Tate 63001cbc13 Await a suspending stackless callee without its promise 2026-10-21 23:55:37 -05:00
Chris Tate ecee5fdaaf Store references to ProgramLifetime instances in 4 bytes on Linux 2026-10-21 23:25:33 -05:00
Chris Tate 56880e110c Keep the slots of short known-count arrays in the array's block
An array constructed with a known element count of up to 16 (array
literals, copies, slices and the arrays the runtime builds) keeps its
slots in its own block, as empty arrays built for appends already do, so
it costs one allocation and one free.

scr_arr_make_ops is inlined into its two constructors, so the size class
of scr_arr_new_ops_small folds to a table index. It writes the element
ops id with the other header fields and records ops past the registry
in the array's ext after the storage is set up: the ext pointer precedes
the dense allocation, so inline slots must move out before the ext is
created.
2026-10-21 23:19:57 -05:00
Chris Tate 2a48d46da3 Key weak collections by net sockets and servers, and carry the socket in undici client diagnostics (#1742) 2026-10-21 22:14:27 -05:00
Chris Tate d0d132080c Skip candidate buffering for classes whose trace visits nothing (#1743) 2026-10-21 22:11:25 -05:00
Chris Tate 4100cd3c04 Never buffer instances of classes whose trace visits nothing
A class carries the collector header when any class in its hierarchy can
be on a cycle, so instances of classes whose own fields hold nothing
cycle-capable were buffered as candidates and walked by every level. Such
an object has no traced children and is never on a cycle.

Their descriptors now name scr_cyc_no_children, and they are allocated
with buf set to SCR_CYC_ACYCLIC. That value has the buffered bit set, so
a release that leaves the object alive makes no call; scr_cyc_is_buffered
is false for it, and an emitted death or publication mark takes the object
out of a candidate buffer only when buf is below -1. A pass that walks
such an object as a child restores the value when it survives.
2026-10-21 22:08:30 -05:00
Chris Tate e80be9c2e5 Keep properties untyped code adds to typed object literals (#1733) 2026-10-21 22:06:32 -05:00
Malte UblandClaude Opus 5.5 fc551c4a8b Key weak collections by net sockets and servers, and carry the socket in undici client diagnostics
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:06:28 -07:00
Chris Tate 71d5e92dc0 Address review feedback on properties untyped code adds to typed objects 2026-10-21 22:02:22 -05:00
Chris Tate 10820d488c Support SNICallback in tls.createServer (#1700) 2026-10-21 22:00:41 -05:00
Chris Tate 37cdd92755 Keep properties untyped code adds to typed object literals 2026-10-21 20:23:53 -05:00
Chris Tate 143c80eb1c Print class instances with their class name when instanceof sees unknown values 2026-10-21 20:10:43 -05:00
Chris Tate 4ab3850579 Keep named properties written to arrays through checked views (#1715) 2026-10-21 19:34:43 -05:00
Chris Tate 5bcd68f1ae Address the third review round on array named properties 2026-10-21 19:31:44 -05:00
Chris Tate 34f371f829 Address the second review round on array named properties 2026-10-21 18:43:03 -05:00
Chris Tate 4fee347ca0 Address review feedback on array named properties 2026-10-21 17:57:00 -05:00
Chris Tate dfdc610c59 Replace every match of a block in one-character replace chains
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.
2026-10-21 17:36:58 -05:00
Chris Tate 79a7120e5f Speed up JSON.parse and JSON.stringify scanning
- The number and whitespace scanners keep the cursor in locals and write
  p->pos once: the runtime builds without strict aliasing, so a loop on
  p->pos stored it after every byte.
- Typed arrays gather parsed elements in a 32-slot run and append them
  with scr_arr_push_many instead of one push per element.
- The string scan shared by parse and stringify takes the index of the
  first quote, backslash or control byte from the marked word on
  little-endian hosts, and checks the last 1-7 bytes of a string as one
  word, so a short plain string needs no byte loop.
- Typed record parsing computes once per record schema and parse which
  field names hold no byte JSON would escape, instead of testing the
  name on every direct key match.
- The mantissa converts to double through int64, and the positioned
  error report stays out of line.
2026-10-21 17:31:02 -05:00
Chris Tate 0950725949 Refuse elements on Array.prototype 2026-10-21 17:24:17 -05:00
Chris Tate 238b08352b Keep named properties written to arrays through checked views 2026-10-21 17:00:19 -05:00
Chris Tate ea4d570e59 Speed up split, indexOf, slice and Number() on ASCII fields
Number() of a string with an optional sign and 1 to 15 digits returns
the integer directly; every other string keeps the full StringToNumber
grammar, out of line.

split() with a separator collects its first batch of pieces before
creating the result, so the result is an inline array of exactly that
size, filled in place. A split's default limit skips the uint32
conversion, and pieces of a proven-ASCII string are created ASCII,
including the scratch pieces of split loops.

indexOf() from the start of a proven-ASCII string searches bytes
directly, and slice() of one skips the UTF-16 index map. The one-byte
case of the byte search reaches memchr without a stack frame.
2026-10-21 16:12:52 -05:00
Chris Tate 1c2b80040b Deliver queued worker messages before immediates, as Node does (#1682) 2026-10-21 15:05:13 -05:00
Chris Tate ca68d63c1c Finish a worker message batch before port messages, and count queued messages as they arrive
- 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.
2026-10-21 15:00:22 -05:00
Chris Tate c1b413e5cc Shorten the cycle collector's per-object paths
markGray's edge visitor kept a stack frame on every call, though most
edges end at an object that is skipped or already gray. Those now return
without one, and the first visit and skipped cross-generation edges
tail-call out of line, as the scan visitors already do.

The walks no longer call the dense-array walker for an empty array. The
routing of nursery candidates past bypassed levels inlines its bypass
test, and the end of each pass tests its work vectors' capacity inline.
What is collected, and when, is unchanged.
2026-10-21 14:25:23 -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 Tate 3a139a8d40 Negotiate ALPN and bound the TLS version in tls.connect and tls.createServer (#1664) 2026-10-21 13:52:22 -05:00
Chris Tate 1d6f26a071 Give arrays of a known final length exact dense capacity 2026-10-21 13:26:50 -05:00
Chris Tate a4d32a8e38 Use the default TLS version bound for null minVersion and maxVersion in tls.createServer
tls.createServer validated minVersion and maxVersion with its own loop,
which threw ERR_TLS_INVALID_PROTOCOL_VERSION for null. It now uses the
same check as tls.connect, where null and undefined select the default
bound, as in Node.
2026-10-21 13:22:56 -05:00
Chris Tate 96f31cbc18 Shrink the array header to 32 bytes
An array's element ops are a registry id in `spare` instead of a pointer
field: descriptors get their id on their first array (emitted descriptors
are writable for it), and a program with more element types than the id
holds keeps the rest in the array's out-of-line storage.

The out-of-line storage (sparse indices, noncanonical numeric properties,
match metadata) is no longer a header field. An array that has it keeps
its pointer in the 8 bytes before its dense allocation and sets
SCR_ARR_HAS_EXT; such an array always has a dense allocation of its own
(inline slots move out, an empty array gets the prefix alone), and every
allocation, reallocation and free of the dense storage accounts for the
prefix.

Loops over elements look the ops up once, and the state lookup below the
dense capacity reads no out-of-line storage.
2026-10-21 13:04:07 -05:00
Chris Tate d8265b2209 Link scr_bytes_dyn.c into libraries 2026-10-21 12:35:58 -05:00
Chris Tate d59ec3251e Grow small map entry arrays through 1, 2, 3, 4, 6, 8, 12 and 16 slots 2026-10-21 12:06:08 -05:00
Chris Tate b45b8e0898 Call an object's own or inherited toString in template literals and join (#1655) 2026-10-21 11:50:36 -05:00
Chris Tate 04435a70ac Address the second round of review comments on prototype toString conversions 2026-10-21 11:44:13 -05:00
Chris Tate d45f331907 Return the pages faulted in past a parked arena's frontiers (#1668) 2026-10-21 11:36:27 -05:00
Chris Tate 23febaddf8 Collect the frontend's cycles when the native compiler disposes it (#1671) 2026-10-21 11:30:00 -05:00
Chris Tate cdcf69c728 Give ProgramLifetime class instances a 32-bit header and packed fields (#1651) 2026-10-21 11:21:22 -05:00
Chris Tate 9a504b6f0d Share one listener list between typed and untyped uses of an EventEmitter (#1563) 2026-10-21 11:18:16 -05:00
Chris Tate e34e5edfd5 Run Buffer methods on Buffers held as checked-dynamic values (#1659) 2026-10-21 11:15:06 -05:00
Chris Tate f2f0617c79 Support fs.promises.rm options (#1673) 2026-10-21 11:08:38 -05:00
Chris Tate e80aea730e Convert string index arguments without libm calls (#1672) 2026-10-21 11:04:26 -05:00
Chris Tate aea358eed5 Freeze, seal and query functions through their property tables (#1657) 2026-10-21 11:01:40 -05:00
Chris Tate 858328703b Name typed functions' property tables and keep runtime freezes data-only
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.
2026-10-21 10:34:21 -05:00
Chris Tate 96c82719b6 Deliver queued worker messages before immediates, as Node does
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.
2026-10-21 10:07:56 -05:00
Chris Tate 94f040b3df Address review comments on Buffer methods for checked-dynamic values 2026-10-21 09:57:56 -05:00