Skip to content

Route more packages and values through Node islands (NI-4c–4g) - #1904

Open
cramforce wants to merge 24 commits into
ni/i6-subclassesfrom
ni/i7-routing
Open

cramforce wants to merge 24 commits into
ni/i6-subclassesfrom
ni/i7-routing

Conversation

@cramforce

@cramforce cramforce commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #1903. Seventh of nine Node islands PRs.

What it does

  • Fences make islands. Runtime and object-model fences (by the NI-2 report class) and module-init fences pick islands. If the island build fails, the build retries with only the packages whose fences surely fire. If that also fails, the native build carries SC8023 naming the packages and the island build's first error, instead of silently keeping a build that dies at module init. --npm-static=<pkg> keeps a package native. A failed lowering retries with the report-marked packages as islands.
  • A native package requiring an island package. Top-level const x = require("<island>") in a compiled module binds module.exports.
  • Routing follows the value. The nodeIsland.get/set/invoke/apply/construct adapters take the checked-dynamic path when the receiver is not an island handle. Examples: a native object typed by an island interface, or a native error in an island .catch. scr_dyn_invoke.c is now linked for island programs.
  • Object literals with spreads, methods, accessors or computed keys cross by reference. string | Buffer reads count as copies, and .toString("utf8") on that union is lowered. CommonJS namespace members are snapshotted.
  • util.types.* and Array.isArray answer for island values.
  • Uncaught island errors print Node's report: the source line and arrow (from Exception::CreateMessage), the stack and Node.js v<version>. This adds ErrorInfo.arrow, bridge ABI 9. The error is released after the report.
  • Severity: SC8020 renders as a warning, and SC8001/SC8021 as notes. Pure CommonJS helper packages are duplicated without a warning.
  • util.types is released at exit (RC audit).

Evidence

  • Corpus programs, byte-identical with Node:
    • 6818-node-island-mixed: a native importer, island interfaces on native objects, spread and method options, string|Buffer, a .catch read.
    • 6819-node-island-fenced: an esbuild getter prologue as an auto island.
    • 6825-node-island-init-fence: a module-init fence that picks an island; with --node-islands=off the program dies at init.
    • 6826-node-island-undefined-args: undefined arguments and fields, native number[], ?. on island-typed native objects, cwd and env changed natively.
  • npm harness: about fifteen cases that were deferred refusals now run as islands and match Node. Examples: misc-shims, node24-deep-equality, node24-env, node24-mime(-errors), node24-parseargs, node24-util-types, stream-shims, util-shims, bytes-exit, dotfolder, fetch-destructure, node24-esm-metadata. Island stderr now compares exactly, except that each run of stack frames is cut to the frames Node and native share.

Note on jose

The report-class rule picks jose (6331), run-node (6610) and fs.realpath (6570) as islands, because each has object-model markers and some fences. 6331 and 6610 then differ from Node; 6570 matches. This contradicts the decision that jose stays native. Those two corpus programs now pin their packages with "native" in a corpus package.json. A follow-up should decide the default: per-function reachability, or no report-class rule for builds that succeed natively.

Known shortcut

Gate results

  • Island set in a Vercel Sandbox, with the libnode dev pack rebuilt from this branch's bridge (node-island*.test.ts, npm.test.ts, packages/compiler/src/node-islands, differential 68xx): plain 223/223 + diff 68/68; sanitized 222/223 (node24-deep-equality exits 1 at exit, the TLS exit race that Exit cleanly with Node loaded and keep nested entries on Node's stack (NI-7c, NI-3b) #1905 fixes) + 68/68.
  • pnpm lint, pnpm format:check and pnpm test:ts7 --baselines-only pass.
  • The self-hosted compiler (packages/compiler/src/native/cli.ts --emit=llvm) builds.
  • Zero-cost gate: 2835 identical. The DIFFERS is the same nondeterministic corpus/fs-extra-package. Three npm corpus programs change by design under auto routing: 6331 (jose) and 6610 (run-node) are pinned native in their corpus package.json on this branch and stay identical, and 6570 (fs.realpath) now runs as an island and matches Node.

CI on the first push found failures outside the focused island set: the ABI in bridge.test.ts, the runtime ABI header declarations, the effect gate on the ts7 allowlist, weak collections in the compiler, the pipeline scout and the Node version line. The fixes are folded into the PRs that introduce each piece, and they are re-checked in a Sandbox on the final tree.

Stack

#1898
#1899
#1900
#1901
#1902
#1903
→ #1904
#1905
#1906
#1907
#1908

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scriptc Ready Ready Preview, v0 Oct 27, 2026 12:43pm UTC

Comment thread packages/compiler/src/frontend/lowering/lower-node-island.ts
Comment thread packages/compiler/src/frontend/lowering/lower-node-island.ts Outdated
Comment thread packages/compiler/src/frontend/lowering/lower-node-island.ts
Node islands stack, part 7 of 11 (scriptc-perf/results/islands-prs/PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cramforce and others added 3 commits October 22, 2026 19:08
… run-node native without pins

A gap fence inside a function body may never run, so it no longer makes its
package a Node island under --node-islands=auto: only fences in top-level
module code and fences that need the JavaScript runtime do. The package's
island class still decides after a failed build. Typed packages like jose
stay native (DECISIONS.md, 2026-10-21), so the corpus pins of jose and
run-node are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plain object a program passes into an island is an object-template
instance, which inherits from its implicit constructor's prototype object;
island code that checks for plain objects (jose's isObject:
Object.getPrototypeOf(o) === Object.prototype) rejected it. The instance now
inherits Object.prototype directly. Corpus 6826 checks it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eferred-refusal expectations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	packages/compiler/src/diagnostics/diagnostic.ts
…erics

When a native build failed and the packages it compiled with runtime fences
then failed as islands too, the native build's diagnostics were reported.
That build types those packages from their JavaScript, where declared-only
types (interfaces, `Static<T>`-style aliases) are `any`, so typed user code
reported cascades the island build does not have (a schema library's `TSchema`).
The island build's errors are reported instead.

In a generic function instance over island types (`<T extends TSchema>`),
a `T` value bound to dyn now crosses into the island the way `any` does,
instead of being refused as a native object.

Corpus 6829 types user code with an island package's interfaces, generic
aliases and a generic function; node-island.test.ts pins the reported
diagnostics of a failed build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	tests/harness/node-island.test.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's lookup for builtin globals read through the global object lowers
every global's value, and its fences have no source position, so they
were marked as module-init fences and made packages like jose islands.
The lookup runs when the program calls it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — b97b161c Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants