Skip to content

node:vm: pass the Node v26.3.0 vm test suite (+25 tests, ~65% → 97%) - #32018

Merged
cirospaciari merged 1 commit into
mainfrom
claude/node-vm-v26-tests
Jun 22, 2026
Merged

cirospaciari merged 1 commit into
mainfrom
claude/node-vm-v26-tests

Conversation

@cirospaciari

Copy link
Copy Markdown
Member

What does this PR do?

Brings node:vm to 98/101 (97%) of Node v26.3.0's vm test suite, up from 66/101 (~65%) on current main. Vendors 25 new upstream test files (plus 18 refreshed to verbatim v26.3.0), and fixes every runtime gap they surfaced. Every vendored vm test passes, including the two that previously hung and the one that crashed.

Pass rate

before after
Node v26.3.0 vm tests passing 66 / 101 (~65%) 98 / 101 (97%)
vendored vm tests in-tree 75 (9 failing) 100 (0 failing)

The 3 upstream tests not vendored:

  • test-vm-module-modulerequests.js, test-vm-module-linkmodulerequests.js — require source-phase imports (import source), which the engine parser does not support.
  • test-vm-dynamic-import-callback-missing-flag.js — asserts --experimental-vm-modules flag gating; bun intentionally supports vm modules unflagged, so gating would break existing users.

Runtime fixes

Module linking and the v26 API

  • New public API on SourceTextModule: linkRequests(), instantiate(), moduleRequests (with phase), hasTopLevelAwait(), hasAsyncGraph(); new ERR_MODULE_LINK_MISMATCH code.
  • link()/instantiate() now follow Node's status protocol: linking stores resolutions and leaves the module unlinked; instantiate() links the record graph, flips status, and propagates linked to dependency wrappers (fixes shared-dependency evaluation order and instantiate() crashing on dependency-free modules).
  • instantiate() without a prior link throws ERR_VM_MODULE_LINK_FAILURE("module is not linked") like Node.
  • Module sources are parsed eagerly in the constructor (SyntaxError surfaces at construction; moduleRequests available immediately).

Top-level await

  • Source text modules now use the spec-style record evaluate() so the async machinery runs to completion — previously the body after await never resumed and the namespace stayed in TDZ.

microtaskMode: 'afterEvaluate'

  • Contexts get their own microtask queue (per-realm promise watchpoints route reactions to it), so inner promises no longer leak progress to the outer loop, and the drain runs inside the watchdog scope — timeout now throws ERR_SCRIPT_EXECUTION_TIMEOUT where it previously hung (or panicked on older builds).

Evaluation semantics

  • evaluate() is no longer async: synchronously-completed evaluations return already-settled promises (util.inspect shows Promise { undefined }), sync throws map to rejected promises, and an errored module rejects with the same error instance on every re-evaluate.
  • SyntheticModule instantiates at construction like Node (so setExport()/evaluate() work immediately), uninitialized exports read as undefined instead of throwing TDZ, and re-entrant evaluate() from inside evaluation steps rejects with ERR_VM_MODULE_STATUS instead of recursing.
  • vm.measureMemory() implemented (summary/detailed, per-context entries, experimental warning).

Contextify interceptors

  • Object.defineProperty(this, …) inside a context forwards through the sandbox's [[DefineOwnProperty]], so Proxy sandbox traps fire exactly once.
  • Sandbox prototype-chain properties are no longer reported as own properties of the context global (still readable through normal lookup).
  • Reading a property whose value is the sandbox itself returns the context global (runInContext('window') === runInContext('this')).
  • Indexed access (this[1]) routes through the interceptor; previously it bypassed the sandbox.

Scripts

  • codeGeneration: { wasm: false } now also gates synchronous new WebAssembly.Module() (throws CompileError), not just the streaming entry points.
  • script.sourceMapURL is populated before the script first runs.
  • vm script errors carry Node's full decoration: <file>:<line>, the offending source line, and a caret marker.
  • compileFunction defaults filename to the empty string in both call shapes like Node (it previously flipped between two labels), and lineOffset ≥ 1 now lines up reported positions with the function body.

Crashes and races

  • SigintWatcher refcount was a plain integer mutated from concurrent worker_threads; lost increments tore the watcher down early and aborted the process (exit 134). Now guarded by a lock.
  • bun -e '…require(file)…' silently exited 0 when the required file failed to load (transpile error or module-not-found). The CJS entry path used the JSC::evaluate overload that swallows the exception; it now rethrows, so the error prints and the process exits 1.

Error messages

  • ERR_INVALID_ARG_TYPE renders free-form phrases like Node (must be an Array of unique strings), and validateBuffer produces Node's must be an instance of Buffer, TypedArray, or DataView.

Test-side deviations

Per the vendoring conventions, upstream files are verbatim except engine-inherent differences, each gated as typeof Bun === 'undefined' ? <node> : <bun> with a one-line comment: JSC attributes throws to different columns than V8 (affects frame columns and caret position), JSC's strict-mode/defineProperty message texts differ, columnOffset and the lineOffset: 0 wrapper line can't be compensated (the engine clamps zero/negative provider start positions), and the uncaught-error printer casing. All patched files still pass under real Node.

How was this verified?

  • All 100 vendored vm tests (97 parallel + 3 sequential) pass with the debug build: test-vm-* via the node-test config, 0 failures, 0 timeouts, across repeated full-suite runs.
  • test/js/node/vm/ (bun's own vm tests): 285 tests, 0 failures; one white-box inline snapshot updated to the new link/instantiate contract and one stack snapshot regenerated for the added error decoration.
  • Blast-radius sweeps for the shared-layer changes: buffer/path/querystring/crypto/string_decoder/stream node tests (30/30), test/cli/run/run-eval.test.ts (33/33), test/js/node/module (64/64), test/js/bun/resolve (276 tests), node-crypto.test.js (150 tests) — all passing.
  • The -e fix verified against both regressions it could affect: plain throws still report and exit 1; valid scripts unaffected.

@robobun

robobun commented Jun 9, 2026 •

Copy link
Copy Markdown
Collaborator
Updated 4:25 PM PT - Jun 22nd, 2026

❌ @cirospaciari, your commit 2a65eba has 2 failures in Build #63976 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 32018

That installs a local version of the PR into your bun-32018 executable, so you can run:

bun-32018 --bun

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Found 6 issues this PR may fix:

  1. Instability with vm.runInNewContext(, { breakOnSigint: true }) #31885 - SigintWatcher race condition fix directly addresses the segfault/instability when using breakOnSigint: true
  2. node:vm microtaskMode: 'afterEvaluate' doesn't seem to be respected #20145 - microtaskMode: 'afterEvaluate' fix directly addresses microtasks not draining before returning to caller
  3. Support await in eval and node:vm #14873 - Top-level await in SourceTextModule now works (body after await previously never completed)
  4. Breakpoints inside Vite SSR modules aren't triggered #17366 - sourceMapURL now populated before first run, fixing debugger source mapping for Vite SSR modules
  5. jest error TypeError: undefined is not an object (evaluating '_global.hasOwnProperty("queueMicrotask")') #11075 - Sandbox prototype-chain property resolution fixes address incorrect hasOwnProperty behavior on vm context globals
  6. happy-dom window eval "is not a function" in Bun #16363 - Contextify interceptor fixes for property forwarding address eval being undefined on vm context globals

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #31885
Fixes #20145
Fixes #14873
Fixes #17366
Fixes #11075
Fixes #16363

🤖 Generated with Claude Code

@cirospaciari

Copy link
Copy Markdown
Member Author

Pushed a follow-up commit (caa06e9) with fixes for edge cases the vendored suite doesn't cover, found in self-review:

  • top-level await in a dependency module no longer trips a release assert (the root record's spec-style evaluate drives async deps; verified import './dep' with await in dep evaluates like Node)
  • codeGeneration.wasm:false now also gates WebAssembly.compile (bypassed JSC's flag), and the WebAssembly.Module replacement preserves imports/exports/customSections, the prototype, and pins prototype.constructor (the original compiling constructor was reachable through it)
  • microtaskMode is validated natively, so script.runInNewContext({}, { microtaskMode: 'bogus' }) rejects with ERR_INVALID_ARG_VALUE like Node instead of being silently ignored
  • vm error stack decoration is idempotent via a private marker — nested vm boundaries previously prepended one decoration block per layer
  • the measureMemory context registry prunes dead WeakRefs amortized on createContext, not only inside measureMemory

Known remaining divergence: WebAssembly.instantiate(precompiledModule) inside a wasm-disabled context rejects in bun (JSC's setWebAssemblyEnabled gates the whole instantiate entry point) where Node allows it, since no code generation is involved. Fixing that needs a JSC-side change.

Full vm suite (100 files) and test/js/node/vm/ re-verified green after the fixes.

@cirospaciari
cirospaciari marked this pull request as ready for review June 22, 2026 19:28
@cirospaciari
cirospaciari force-pushed the claude/node-vm-v26-tests branch from 5b9ebc6 to eb22819 Compare June 22, 2026 19:34
@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Extends node:vm compatibility with a new ERR_MODULE_LINK_MISMATCH error code, a working measureMemory implementation, SourceTextModule linkRequests/instantiate/moduleRequests API, top-level-await and async-graph detection, afterEvaluate per-context microtask queues, WebAssembly codegen gating, error decoration, property interception fixes, SigintWatcher thread-safety, and a large accompanying test suite.

Changes

node:vm Compatibility

Layer / File(s) Summary
Error codes, validator, and builtin name infrastructure
src/jsc/ErrorCode.rs, src/jsc/bindings/ErrorCode.cpp, src/jsc/bindings/ErrorCode.ts, src/jsc/bindings/NodeValidator.cpp, src/js/builtins/BunBuiltinNames.h
Inserts ERR_MODULE_LINK_MISMATCH at discriminant 155, updates ERR_INVALID_ARG_TYPE message formatting for phrase-shaped expected types, switches validateArrayBufferView to INVALID_ARG_INSTANCE, and registers the vmErrorDecorated builtin private identifier.
JS createContext tracking and measureMemory implementation
src/js/node/vm.ts
Adds microtaskMode validation and WeakRef context tracking in createContext, implements measureMemory with mode/execution validation and per-context detailed reporting, changes validateModule to ERR_INVALID_THIS, and fixes the dynamic import phase argument.
JS Module, SourceTextModule, and SyntheticModule API changes
src/js/node/vm.ts
Rewrites Module.link status validation and Module.evaluate to return mapped promises without await, adds eager #moduleRequests storage, exposes linkRequests/instantiate/moduleRequests on SourceTextModule, instantiates SyntheticModule eagerly with a validated no-op link.
NodeVM global/context runtime behavior
src/jsc/bindings/NodeVM.cpp, src/jsc/bindings/NodeVM.h
Adds ownMicrotaskQueue and filenameProvided option flags, per-global microtask queue creation and draining, wasm-disallow host functions overriding WebAssembly.Module/compile, idempotent error decoration with caret generation, indexed getOwnPropertySlotByIndex, split Get-vs-other-method slot logic, method-table-dispatched defineOwnProperty, exception-to-rejected-promise wrapping in moduleLoaderImportModule, and compile-function filename normalization.
NodeVMModule evaluation reconciliation and async graph detection
src/jsc/bindings/NodeVMModule.cpp, src/jsc/bindings/NodeVMModule.h
Adds reconcileEvaluationState to align wrapper status with cyclic-record evaluation state, reworks evaluate for afterEvaluate draining and caller-realm promise copying, handles EvaluatingAsync top-level-await capability promises, adds hasAsyncGraph/propagateLinked traversal helpers, and exposes hasTopLevelAwait/hasAsyncGraph as prototype methods.
SourceTextModule, SyntheticModule, and Script lifecycle updates
src/jsc/bindings/NodeVMSourceTextModule.cpp, src/jsc/bindings/NodeVMSourceTextModule.h, src/jsc/bindings/NodeVMSyntheticModule.cpp, src/jsc/bindings/NodeVMScript.cpp, src/jsc/bindings/NodeVMScript.h
Changes link to set m_linkCalled without status transition, requires m_linkCalled before instantiate, propagates linked status after record->link, adds m_hasTopLevelAwait from parse features, removes m_evaluationException from SourceTextModule, initializes synthetic exports to undefined, guards evaluate on Evaluating status, and adds lazy sourceMapURL parsing with afterEvaluate microtask draining.
SigintWatcher thread-safety hardening
src/jsc/bindings/vm/SigintWatcher.cpp, src/jsc/bindings/vm/SigintWatcher.h
Adds m_refCountMutex and applies locking in ref/deref around install/uninstall transitions to prevent races from concurrent worker threads.
VM test allowlists, options, and basic behavior validation
test/js/node/test/common/globals.js, test/js/node/test/parallel/test-vm-basic.js, test/js/node/test/parallel/test-vm-codegen.js
Expands global object allowlists for intrinsics and Web IDL exposure, adds compileFunction option type validation tests, and validates code-generation mode restrictions.
VM context and global property semantics tests
test/js/node/test/parallel/test-vm-*.js
Adds tests for context assignment behavior (strict/non-strict), global identity verification, property redefinition message variations, and extensive prototype-chain and property-enumerator coverage including custom prototypes, accessors, and symbol properties.
VM measureMemory implementation and mode coverage
test/js/node/test/parallel/test-vm-measure-memory*.js
Adds lazy and eager mode tests with multi-context validation, experimental warning expectations, and summary/detailed shape assertions.
VM module basic API and status tests
test/js/node/test/parallel/test-vm-module-basic.js, test/js/node/test/parallel/test-vm-module-errors.js, test/js/node/test/parallel/test-vm-module-dynamic-import.js
Updates module status expectations, invalid-this error codes, dynamic import callback argument expectations, and SyntheticModule/SourceTextModule inspect assertions.
VM module evaluation semantics and lifecycle tests
test/js/node/test/parallel/test-vm-module-evaluate-*.js
Adds comprehensive SourceTextModule and SyntheticModule evaluation tests covering synchronous errors, successful resolutions, top-level-await promise settlement, repeated evaluation idempotency, namespace access timing, and rejection handling.
VM module linking, requests, and instantiation tests
test/js/node/test/parallel/test-vm-module-linkmodulerequests-*.js, test/js/node/test/parallel/test-vm-module-instantiate.js, test/js/node/test/parallel/test-vm-module-link.js, test/js/node/test/parallel/test-vm-module-link-shared-deps.js
Adds tests for linkRequests API, instantiate preconditions, circular graphs, deep linking chains, re-export scenarios, and shared dependency deduplication.
VM module top-level-await and async-graph detection tests
test/js/node/test/parallel/test-vm-module-hastoplevelawait.js, test/js/node/test/parallel/test-vm-module-hasasyncgraph.js
Adds tests for hasTopLevelAwait and hasAsyncGraph APIs, status preconditions, and dependency graph traversal for async detection.
VM module dynamic import and callback behavior tests
test/js/node/test/parallel/test-vm-module-dynamic-import.js, test/js/node/test/parallel/test-vm-module-dynamic-import-promise.js, test/js/node/test/parallel/test-vm-module-referrer-realm.mjs
Adds dynamic import tests from vm.Script and SourceTextModule evaluation, validates importModuleDynamically callback arguments, and tests exception handling during import resolution.
VM module re-entrancy and afterEvaluate microtask checkpoint tests
test/js/node/test/parallel/test-vm-module-evaluate-while-evaluating.js, test/js/node/test/parallel/test-vm-module-after-evaluate.js
Adds tests for Module.evaluate re-entrancy error handling and afterEvaluate microtask queue checkpointing in SourceTextModule evaluation.
VM property interception and getter behavior tests
test/js/node/test/parallel/test-vm-property-definer-interception.js, test/js/node/test/parallel/test-vm-property-not-on-sandbox.js, test/js/node/test/parallel/test-vm-module-synthetic.js
Adds tests for Object.defineProperty trap invocation, getter-backed property access, and synthetic module setExport availability.
VM error diagnostics, stack formatting, and source mapping tests
test/js/node/test/parallel/test-vm-run-in-new-context.js, test/js/node/test/parallel/test-vm-source-map-url.js, test/js/node/test/parallel/test-vm-syntax-error-stderr.js, test/js/node/test/parallel/test-vm-api-handles-getter-errors.js
Refactors stack assertions for unified formatting, adds sourceMapURL parsing tests, updates syntax error stderr handling, and updates getter error expectations.
VM timeout escape and SIGINT concurrency regression tests
test/js/node/test/parallel/test-vm-timeout-escape-promise-2.js, test/js/node/test/sequential/test-vm-break-on-sigint.js, test/js/node/test/sequential/test-vm-timeout-escape-promise-module-2.js, test/js/node/test/parallel/test-vm-script-after-evaluate.js
Adds timeout-escape and SIGINT concurrency tests ensuring Promise microtasks respect VM timeouts with afterEvaluate mode.
VM module test snapshot and vm.test.ts updates
test/js/node/vm/vm.test.ts
Updates inline snapshot expectations for SourceTextModule instantiation status and module linking behavior.

Ancillary test infrastructure updates

Layer / File(s) Summary
fs.watch cycle helper and dotenv fixture
test/js/node/watch/fs.watch.test.ts, test/napi/node-napi-tests/test/fixtures/dotenv/uv-threadpool.env
Refactors macOS fs.watch leak test iterations through an async cycle helper with microtask draining and GC, and adds UV_THREADPOOL_SIZE=4 to a napi dotenv fixture.

Possibly related PRs

  • oven-sh/bun#31169: Both PRs touch the VM module loading path—main PR adjusts NodeVM's dynamic import / moduleLoaderImportModule exception/evaluation behavior, while the retrieved PR changes the moduleLoaderImportModule host-hook signature by adding a deferred parameter.
  • oven-sh/bun#31624: Both PRs modify src/jsc/bindings/NodeVMSourceTextModule.cpp around instantiate()/linking to enforce safer "module graph is linked" preconditions (throwing ERR_VM_MODULE_LINK_FAILURE instead of crashing) before calling record->link().
  • oven-sh/bun#32414: Both PRs modify src/jsc/bindings/NodeVM.cpp's getOwnPropertySlot/globalThis handling (main PR adjusts globalThis/contextify property slot behavior, retrieved PR changes the globalThis property-name comparison to use the interned identifier).

Suggested reviewers

  • Jarred-Sumner
  • alii
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: bringing node:vm test suite compliance from 65% to 97% by adding 25 new tests.
Description check ✅ Passed The description comprehensively documents what the PR does (brings test suite from 66/101 to 98/101), how it was verified (all 100 vendored tests pass, blast-radius sweeps for shared layers), and key runtime improvements with detailed explanations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands.

@cirospaciari
cirospaciari force-pushed the claude/node-vm-v26-tests branch 2 times, most recently from 20ce579 to befb2b7 Compare June 22, 2026 19:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/js/node/vm.ts`:
- Around line 468-494: The linkRequests method lacks validation for modules with
status === "errored", while the async [kLink] method explicitly checks and
rejects such modules. Add a status validation check in the linkRequests method's
validation loop (after the existing isModule and context checks) to ensure that
each module passed to linkRequests does not have an errored status, and throw an
appropriate error if one is found to maintain consistency with the [kLink]
method's behavior.

In `@src/jsc/bindings/NodeVM.cpp`:
- Line 186: The subtraction `position.m_line.zeroBasedInt() - 1` at the
TextPosition constructor call in the wrappedPosition initialization is unsafe
and can cause signed integer overflow or pass negative values to
OrdinalNumber::fromZeroBasedInt(), which expects non-negative ordinal values.
Before performing the subtraction, add a guard to ensure
position.m_line.zeroBasedInt() is at least 1, either by clamping the value to a
minimum of 1 or by validating it before the operation. Apply the same fix
pattern to the similar subtraction at lines 527-529 to ensure consistency, and
consider whether a wider integer type with bounds-checking would be more
appropriate to prevent overflow when lineOffset can be as extreme as INT_MIN.

In `@src/jsc/bindings/NodeVMModule.cpp`:
- Around line 729-754: The jsNodeVmModuleHasAsyncGraph host function does not
validate the module status before calling hasAsyncGraph(), which relies on
m_resolveCache populated during the link() operation. Add a status precondition
check in jsNodeVmModuleHasAsyncGraph before calling hasAsyncGraph() to ensure
the module is properly linked, preventing silent incorrect returns on unlinked
modules. This validation should follow the defensive pattern used in
jsNodeVmModuleHasTopLevelAwait and align with JSC binding conventions for
validating preconditions at the native layer.

In `@src/jsc/bindings/NodeVMScript.cpp`:
- Around line 288-292: The code block checking
vm.hasPendingTerminationException() violates binding-safety by explicitly
calling clearException() on the DECLARE_TOP_EXCEPTION_SCOPE. Refactor this
control flow to eliminate the direct clearException() call and instead let the
new ERR_SCRIPT_EXECUTION_* error that is thrown afterwards naturally replace the
pending termination exception, ensuring the exception handling follows the
binding-safety guidelines without the intermediate clear step.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4dddf8ae-58d8-4036-8c2a-bfb220cf8c37

📥 Commits

Reviewing files that changed from the base of the PR and between 452139e and befb2b7.

⛔ Files ignored due to path filters (1)
  • test/js/node/vm/__snapshots__/vm-sourceUrl.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (65)
  • src/js/builtins/BunBuiltinNames.h
  • src/js/node/vm.ts
  • src/jsc/ErrorCode.rs
  • src/jsc/bindings/ErrorCode.cpp
  • src/jsc/bindings/ErrorCode.ts
  • src/jsc/bindings/NodeVM.cpp
  • src/jsc/bindings/NodeVM.h
  • src/jsc/bindings/NodeVMModule.cpp
  • src/jsc/bindings/NodeVMModule.h
  • src/jsc/bindings/NodeVMScript.cpp
  • src/jsc/bindings/NodeVMScript.h
  • src/jsc/bindings/NodeVMSourceTextModule.cpp
  • src/jsc/bindings/NodeVMSourceTextModule.h
  • src/jsc/bindings/NodeVMSyntheticModule.cpp
  • src/jsc/bindings/NodeValidator.cpp
  • src/jsc/bindings/vm/SigintWatcher.cpp
  • src/jsc/bindings/vm/SigintWatcher.h
  • test/js/node/test/common/globals.js
  • test/js/node/test/parallel/test-inspector-enabled.js
  • test/js/node/test/parallel/test-vm-api-handles-getter-errors.js
  • test/js/node/test/parallel/test-vm-basic.js
  • test/js/node/test/parallel/test-vm-codegen.js
  • test/js/node/test/parallel/test-vm-context-dont-contextify.js
  • test/js/node/test/parallel/test-vm-context.js
  • test/js/node/test/parallel/test-vm-global-contextual-store.js
  • test/js/node/test/parallel/test-vm-global-identity.js
  • test/js/node/test/parallel/test-vm-global-non-writable-properties.js
  • test/js/node/test/parallel/test-vm-global-property-enumerator.js
  • test/js/node/test/parallel/test-vm-global-property-interceptors.js
  • test/js/node/test/parallel/test-vm-global-property-prototype.js
  • test/js/node/test/parallel/test-vm-global-setter.js
  • test/js/node/test/parallel/test-vm-measure-memory-lazy.js
  • test/js/node/test/parallel/test-vm-measure-memory-multi-context.js
  • test/js/node/test/parallel/test-vm-measure-memory.js
  • test/js/node/test/parallel/test-vm-module-after-evaluate.js
  • test/js/node/test/parallel/test-vm-module-basic.js
  • test/js/node/test/parallel/test-vm-module-dynamic-import-promise.js
  • test/js/node/test/parallel/test-vm-module-dynamic-import.js
  • test/js/node/test/parallel/test-vm-module-errors.js
  • test/js/node/test/parallel/test-vm-module-evaluate-source-text-module.js
  • test/js/node/test/parallel/test-vm-module-evaluate-synthethic-module-rejection.js
  • test/js/node/test/parallel/test-vm-module-evaluate-synthethic-module.js
  • test/js/node/test/parallel/test-vm-module-evaluate-while-evaluating.js
  • test/js/node/test/parallel/test-vm-module-hasasyncgraph.js
  • test/js/node/test/parallel/test-vm-module-hastoplevelawait.js
  • test/js/node/test/parallel/test-vm-module-instantiate.js
  • test/js/node/test/parallel/test-vm-module-link-shared-deps.js
  • test/js/node/test/parallel/test-vm-module-link.js
  • test/js/node/test/parallel/test-vm-module-linkmodulerequests-circular.js
  • test/js/node/test/parallel/test-vm-module-linkmodulerequests-deep.js
  • test/js/node/test/parallel/test-vm-module-referrer-realm.mjs
  • test/js/node/test/parallel/test-vm-module-synthetic.js
  • test/js/node/test/parallel/test-vm-property-definer-interception.js
  • test/js/node/test/parallel/test-vm-property-not-on-sandbox.js
  • test/js/node/test/parallel/test-vm-run-in-new-context.js
  • test/js/node/test/parallel/test-vm-script-after-evaluate.js
  • test/js/node/test/parallel/test-vm-source-map-url.js
  • test/js/node/test/parallel/test-vm-strict-assign.js
  • test/js/node/test/parallel/test-vm-syntax-error-stderr.js
  • test/js/node/test/parallel/test-vm-timeout-escape-promise-2.js
  • test/js/node/test/sequential/test-vm-break-on-sigint.js
  • test/js/node/test/sequential/test-vm-timeout-escape-promise-module-2.js
  • test/js/node/vm/vm.test.ts
  • test/js/node/watch/fs.watch.test.ts
  • test/napi/node-napi-tests/test/fixtures/dotenv/uv-threadpool.env
💤 Files with no reviewable changes (1)
  • test/js/node/test/parallel/test-inspector-enabled.js

Comment thread src/js/node/vm.ts
Comment thread src/jsc/bindings/NodeVM.cpp Outdated
Comment thread src/jsc/bindings/NodeVMModule.cpp
Comment thread src/jsc/bindings/NodeVMScript.cpp
Comment thread src/js/node/vm.ts Outdated
Comment thread src/js/node/vm.ts
@cirospaciari
cirospaciari force-pushed the claude/node-vm-v26-tests branch from befb2b7 to eceb256 Compare June 22, 2026 20:24

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/js/node/vm.ts`:
- Around line 122-128: In the emitExperimentalWarning function, replace the
regular Set methods with their tamper-resistant dollar-sign prefixed equivalents
to prevent user code from tampering with these methods. Replace the call to
emittedExperimentalWarnings.has(feature) with
emittedExperimentalWarnings.$has(feature), and replace the call to
emittedExperimentalWarnings.add(feature) with
emittedExperimentalWarnings.$add(feature). This follows the established pattern
used elsewhere in the codebase such as in src/js/node/fs.ts.

In `@src/jsc/bindings/NodeVMModule.cpp`:
- Around line 197-216: After the call to
nodeVmGlobalObject->drainOwnMicrotasks() which can run user code and potentially
throw exceptions, add an exception gate check using RETURN_IF_EXCEPTION or
appropriate JSError propagation before the subsequent code that accesses
innerPromise->status() and transforms the result. This prevents the switch
statement and promise wrapping logic from executing if an exception or
termination occurred during microtask drainage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d2cdf808-ce52-4995-a345-1a879ba0a9bb

📥 Commits

Reviewing files that changed from the base of the PR and between befb2b7 and eceb256.

⛔ Files ignored due to path filters (1)
  • test/js/node/vm/__snapshots__/vm-sourceUrl.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (65)
  • src/js/builtins/BunBuiltinNames.h
  • src/js/node/vm.ts
  • src/jsc/ErrorCode.rs
  • src/jsc/bindings/ErrorCode.cpp
  • src/jsc/bindings/ErrorCode.ts
  • src/jsc/bindings/NodeVM.cpp
  • src/jsc/bindings/NodeVM.h
  • src/jsc/bindings/NodeVMModule.cpp
  • src/jsc/bindings/NodeVMModule.h
  • src/jsc/bindings/NodeVMScript.cpp
  • src/jsc/bindings/NodeVMScript.h
  • src/jsc/bindings/NodeVMSourceTextModule.cpp
  • src/jsc/bindings/NodeVMSourceTextModule.h
  • src/jsc/bindings/NodeVMSyntheticModule.cpp
  • src/jsc/bindings/NodeValidator.cpp
  • src/jsc/bindings/vm/SigintWatcher.cpp
  • src/jsc/bindings/vm/SigintWatcher.h
  • test/js/node/test/common/globals.js
  • test/js/node/test/parallel/test-inspector-enabled.js
  • test/js/node/test/parallel/test-vm-api-handles-getter-errors.js
  • test/js/node/test/parallel/test-vm-basic.js
  • test/js/node/test/parallel/test-vm-codegen.js
  • test/js/node/test/parallel/test-vm-context-dont-contextify.js
  • test/js/node/test/parallel/test-vm-context.js
  • test/js/node/test/parallel/test-vm-global-contextual-store.js
  • test/js/node/test/parallel/test-vm-global-identity.js
  • test/js/node/test/parallel/test-vm-global-non-writable-properties.js
  • test/js/node/test/parallel/test-vm-global-property-enumerator.js
  • test/js/node/test/parallel/test-vm-global-property-interceptors.js
  • test/js/node/test/parallel/test-vm-global-property-prototype.js
  • test/js/node/test/parallel/test-vm-global-setter.js
  • test/js/node/test/parallel/test-vm-measure-memory-lazy.js
  • test/js/node/test/parallel/test-vm-measure-memory-multi-context.js
  • test/js/node/test/parallel/test-vm-measure-memory.js
  • test/js/node/test/parallel/test-vm-module-after-evaluate.js
  • test/js/node/test/parallel/test-vm-module-basic.js
  • test/js/node/test/parallel/test-vm-module-dynamic-import-promise.js
  • test/js/node/test/parallel/test-vm-module-dynamic-import.js
  • test/js/node/test/parallel/test-vm-module-errors.js
  • test/js/node/test/parallel/test-vm-module-evaluate-source-text-module.js
  • test/js/node/test/parallel/test-vm-module-evaluate-synthethic-module-rejection.js
  • test/js/node/test/parallel/test-vm-module-evaluate-synthethic-module.js
  • test/js/node/test/parallel/test-vm-module-evaluate-while-evaluating.js
  • test/js/node/test/parallel/test-vm-module-hasasyncgraph.js
  • test/js/node/test/parallel/test-vm-module-hastoplevelawait.js
  • test/js/node/test/parallel/test-vm-module-instantiate.js
  • test/js/node/test/parallel/test-vm-module-link-shared-deps.js
  • test/js/node/test/parallel/test-vm-module-link.js
  • test/js/node/test/parallel/test-vm-module-linkmodulerequests-circular.js
  • test/js/node/test/parallel/test-vm-module-linkmodulerequests-deep.js
  • test/js/node/test/parallel/test-vm-module-referrer-realm.mjs
  • test/js/node/test/parallel/test-vm-module-synthetic.js
  • test/js/node/test/parallel/test-vm-property-definer-interception.js
  • test/js/node/test/parallel/test-vm-property-not-on-sandbox.js
  • test/js/node/test/parallel/test-vm-run-in-new-context.js
  • test/js/node/test/parallel/test-vm-script-after-evaluate.js
  • test/js/node/test/parallel/test-vm-source-map-url.js
  • test/js/node/test/parallel/test-vm-strict-assign.js
  • test/js/node/test/parallel/test-vm-syntax-error-stderr.js
  • test/js/node/test/parallel/test-vm-timeout-escape-promise-2.js
  • test/js/node/test/sequential/test-vm-break-on-sigint.js
  • test/js/node/test/sequential/test-vm-timeout-escape-promise-module-2.js
  • test/js/node/vm/vm.test.ts
  • test/js/node/watch/fs.watch.test.ts
  • test/napi/node-napi-tests/test/fixtures/dotenv/uv-threadpool.env
💤 Files with no reviewable changes (1)
  • test/js/node/test/parallel/test-inspector-enabled.js

Comment thread src/js/node/vm.ts
Comment thread src/jsc/bindings/NodeVMModule.cpp
Comment thread src/jsc/bindings/NodeVM.cpp Outdated
@cirospaciari
cirospaciari force-pushed the claude/node-vm-v26-tests branch from eceb256 to dcb252c Compare June 22, 2026 21:36
Comment thread src/jsc/bindings/NodeVM.cpp Outdated
Comment thread src/jsc/bindings/NodeVM.cpp
@cirospaciari
cirospaciari force-pushed the claude/node-vm-v26-tests branch from dcb252c to 64da844 Compare June 22, 2026 21:58
Vendor the Node v26.3.0 vm tests (25 new files, 18 refreshed to match
upstream) and fix the runtime gaps they surface:

- v26 module API: linkRequests(), instantiate(), moduleRequests,
  hasTopLevelAwait(), hasAsyncGraph(); link()/instantiate() now follow
  Node's status protocol (link stores resolutions, instantiate flips
  status and propagates across the graph)
- top-level-await modules: switch to JSC's spec-style record evaluate so
  the async machinery runs to completion (namespace no longer TDZs)
- microtaskMode 'afterEvaluate': per-context microtask queue, drained
  inside the watchdog scope so timeouts fire instead of hanging
- vm.measureMemory(): implemented (summary/detailed, context tracking)
- SyntheticModule: instantiated at construction like Node; exports
  initialize to undefined instead of TDZ; re-entrant evaluate rejects
- evaluate() returns settled promises for sync completions and rethrows
  the recorded error for errored modules (same instance every time)
- contextify interceptors: defineProperty forwards to Proxy sandboxes,
  prototype-chain properties are no longer reported as own, sandbox
  identity maps to the context global, indexed access intercepts
- codeGeneration.wasm:false now gates synchronous WebAssembly.Module
- script.sourceMapURL available before first run
- vm script errors carry Node's source line + caret decoration
- compileFunction: filename default matches Node in both call shapes,
  lineOffset lines up with the body for offsets >= 1
- SigintWatcher: refcount race with worker_threads fixed (lock)
- ERR_INVALID_ARG_TYPE phrasing for free-form phrases now matches Node;
  validateArrayBufferView reports the instance list like validateBuffer;
  new ERR_MODULE_LINK_MISMATCH code

98 of Node's 101 vm tests pass (97%, from 66 before). Not vendored:
2 tests needing source-phase imports (unsupported by the engine parser)
and 1 asserting --experimental-vm-modules flag gating that bun
deliberately does not have. Test-side deviations are limited to
engine-inherent message text and column attribution, each marked with
an inline comment.
Comment thread src/jsc/bindings/NodeVMModule.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/jsc/bindings/NodeVMScript.cpp (1)

284-292: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle pending termination exceptions even when the request bit is already clear.

drainOwnMicrotasks() can leave a pending termination exception after the afterEvaluate checkpoint; this helper only enters when hasTerminationRequest() is true, so a pending-only termination can bypass the documented ERR_SCRIPT_EXECUTION_* conversion and hit the later exception barrier.

🐛 Proposed fix
-    if (vm.hasTerminationRequest()) {
+    if (vm.hasTerminationRequest() || vm.hasPendingTerminationException()) {
         vm.drainMicrotasksForGlobalObject(globalObject);
         // The termination may have fired inside an afterEvaluate microtask
         // checkpoint, leaving the termination exception pending; clear it so

Also applies to: 365-391

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/jsc/bindings/NodeVMScript.cpp` around lines 284 - 292, The
checkForTermination function only checks for pending termination exceptions
inside the hasTerminationRequest() conditional block. However,
drainMicrotasksForGlobalObject() can leave a pending termination exception even
after the termination request bit is cleared, allowing a pending-only
termination to bypass the ERR_SCRIPT_EXECUTION_* error conversion. Move the
pending termination exception check (the if statement checking
vm.hasPendingTerminationException() and calling clearException) outside and
after the hasTerminationRequest() block so it handles both cases: when there is
an active termination request AND when there is only a pending termination
exception with no active request. Apply this same fix to all other locations
mentioned in the comment where similar patterns exist.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/js/node/vm.ts`:
- Around line 590-593: The link() method in the SyntheticModule class currently
returns undefined synchronously, but it overrides the async Module.link() method
which returns a Promise. This breaks callers that use .then() chaining or rely
on Promise-based error handling. Update the link() method to return a resolved
Promise instead of returning undefined, ensuring it maintains the Promise-shaped
interface of the parent Module.link() method while still being a no-op for
synthetic modules.
- Around line 299-314: The timeout default value is set to -1 when
options.timeout is undefined, but the native NodeVMModule::evaluate method
treats any non-zero timeout as an active watchdog timer. When -1 crosses the
unsigned integer boundary, it becomes 4294967295, which can override a shorter
enclosing watchdog. Change the default assignment from timeout = -1 to timeout =
0 in the conditional block where timeout === undefined to properly signal that
no timeout is set.

In `@src/jsc/bindings/NodeVM.cpp`:
- Around line 500-508: The idempotency guard that checks
vmErrorDecoratedPrivateName() is currently positioned too late in the error
handling flow, after materializeErrorInfoIfNeeded() and errorInstance->get()
calls, allowing a previously decorated error to still execute user-defined
getters or fail before the marker is honored. Move the decoration marker check
using vmErrorDecoratedPrivateName() and errorInstance->getDirect() to execute
immediately after the ErrorInstance cast, before any calls to
materializeErrorInfoIfNeeded() or reading the stack property, so that decorated
errors are properly rejected early and undecorated errors proceed with normal
stack formatting.
- Around line 1318-1332: The code currently performs a preflight probe by
calling contextifiedObject->getPropertySlot() to check if a property is declared
on the sandbox before dispatching defineOwnProperty for data descriptors. This
preflight check can trigger Proxy traps prematurely, violating the "observe
exactly once" requirement stated in the comment. To fix this, remove the
isDeclaredOnSandbox check and the conditional block that depends on it (around
the getPropertySlot call and the subsequent if statement checking
isDeclaredOnSandbox and !isDeclaredOnGlobalProxy), and instead dispatch directly
to contextifiedObject->methodTable()->defineOwnProperty() without preflighting,
matching the approach already used for accessor descriptors in the earlier if
block.

In `@src/jsc/bindings/NodeVMModule.cpp`:
- Around line 95-120: The fast path that calls drainOwnMicrotasks() on
nodeVmGlobalObject does not properly mirror the full evaluation safeguards. Wrap
the drainOwnMicrotasks() call with a SIGINT holder (similar to the breakOnSigint
pattern used during initial evaluation) to respect SIGINT handling.
Additionally, after the microtask drain completes, check for any non-termination
exceptions that may have been left pending by the checkpoint operation and
propagate them using RETURN_IF_EXCEPTION before returning
m_evaluationResult.get(), ensuring exceptions are validated and handled
consistently with the coding guidelines.

---

Outside diff comments:
In `@src/jsc/bindings/NodeVMScript.cpp`:
- Around line 284-292: The checkForTermination function only checks for pending
termination exceptions inside the hasTerminationRequest() conditional block.
However, drainMicrotasksForGlobalObject() can leave a pending termination
exception even after the termination request bit is cleared, allowing a
pending-only termination to bypass the ERR_SCRIPT_EXECUTION_* error conversion.
Move the pending termination exception check (the if statement checking
vm.hasPendingTerminationException() and calling clearException) outside and
after the hasTerminationRequest() block so it handles both cases: when there is
an active termination request AND when there is only a pending termination
exception with no active request. Apply this same fix to all other locations
mentioned in the comment where similar patterns exist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7d6701c4-71ff-491d-9a61-a1a657413649

📥 Commits

Reviewing files that changed from the base of the PR and between dcb252c and 2a65eba.

⛔ Files ignored due to path filters (1)
  • test/js/node/vm/__snapshots__/vm-sourceUrl.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (65)
  • src/js/builtins/BunBuiltinNames.h
  • src/js/node/vm.ts
  • src/jsc/ErrorCode.rs
  • src/jsc/bindings/ErrorCode.cpp
  • src/jsc/bindings/ErrorCode.ts
  • src/jsc/bindings/NodeVM.cpp
  • src/jsc/bindings/NodeVM.h
  • src/jsc/bindings/NodeVMModule.cpp
  • src/jsc/bindings/NodeVMModule.h
  • src/jsc/bindings/NodeVMScript.cpp
  • src/jsc/bindings/NodeVMScript.h
  • src/jsc/bindings/NodeVMSourceTextModule.cpp
  • src/jsc/bindings/NodeVMSourceTextModule.h
  • src/jsc/bindings/NodeVMSyntheticModule.cpp
  • src/jsc/bindings/NodeValidator.cpp
  • src/jsc/bindings/vm/SigintWatcher.cpp
  • src/jsc/bindings/vm/SigintWatcher.h
  • test/js/node/test/common/globals.js
  • test/js/node/test/parallel/test-inspector-enabled.js
  • test/js/node/test/parallel/test-vm-api-handles-getter-errors.js
  • test/js/node/test/parallel/test-vm-basic.js
  • test/js/node/test/parallel/test-vm-codegen.js
  • test/js/node/test/parallel/test-vm-context-dont-contextify.js
  • test/js/node/test/parallel/test-vm-context.js
  • test/js/node/test/parallel/test-vm-global-contextual-store.js
  • test/js/node/test/parallel/test-vm-global-identity.js
  • test/js/node/test/parallel/test-vm-global-non-writable-properties.js
  • test/js/node/test/parallel/test-vm-global-property-enumerator.js
  • test/js/node/test/parallel/test-vm-global-property-interceptors.js
  • test/js/node/test/parallel/test-vm-global-property-prototype.js
  • test/js/node/test/parallel/test-vm-global-setter.js
  • test/js/node/test/parallel/test-vm-measure-memory-lazy.js
  • test/js/node/test/parallel/test-vm-measure-memory-multi-context.js
  • test/js/node/test/parallel/test-vm-measure-memory.js
  • test/js/node/test/parallel/test-vm-module-after-evaluate.js
  • test/js/node/test/parallel/test-vm-module-basic.js
  • test/js/node/test/parallel/test-vm-module-dynamic-import-promise.js
  • test/js/node/test/parallel/test-vm-module-dynamic-import.js
  • test/js/node/test/parallel/test-vm-module-errors.js
  • test/js/node/test/parallel/test-vm-module-evaluate-source-text-module.js
  • test/js/node/test/parallel/test-vm-module-evaluate-synthethic-module-rejection.js
  • test/js/node/test/parallel/test-vm-module-evaluate-synthethic-module.js
  • test/js/node/test/parallel/test-vm-module-evaluate-while-evaluating.js
  • test/js/node/test/parallel/test-vm-module-hasasyncgraph.js
  • test/js/node/test/parallel/test-vm-module-hastoplevelawait.js
  • test/js/node/test/parallel/test-vm-module-instantiate.js
  • test/js/node/test/parallel/test-vm-module-link-shared-deps.js
  • test/js/node/test/parallel/test-vm-module-link.js
  • test/js/node/test/parallel/test-vm-module-linkmodulerequests-circular.js
  • test/js/node/test/parallel/test-vm-module-linkmodulerequests-deep.js
  • test/js/node/test/parallel/test-vm-module-referrer-realm.mjs
  • test/js/node/test/parallel/test-vm-module-synthetic.js
  • test/js/node/test/parallel/test-vm-property-definer-interception.js
  • test/js/node/test/parallel/test-vm-property-not-on-sandbox.js
  • test/js/node/test/parallel/test-vm-run-in-new-context.js
  • test/js/node/test/parallel/test-vm-script-after-evaluate.js
  • test/js/node/test/parallel/test-vm-source-map-url.js
  • test/js/node/test/parallel/test-vm-strict-assign.js
  • test/js/node/test/parallel/test-vm-syntax-error-stderr.js
  • test/js/node/test/parallel/test-vm-timeout-escape-promise-2.js
  • test/js/node/test/sequential/test-vm-break-on-sigint.js
  • test/js/node/test/sequential/test-vm-timeout-escape-promise-module-2.js
  • test/js/node/vm/vm.test.ts
  • test/js/node/watch/fs.watch.test.ts
  • test/napi/node-napi-tests/test/fixtures/dotenv/uv-threadpool.env
💤 Files with no reviewable changes (1)
  • test/js/node/test/parallel/test-inspector-enabled.js

Comment thread src/js/node/vm.ts
Comment thread src/js/node/vm.ts
Comment thread src/jsc/bindings/NodeVM.cpp
Comment thread src/jsc/bindings/NodeVM.cpp
Comment thread src/jsc/bindings/NodeVMModule.cpp
@cirospaciari
cirospaciari merged commit 0672e7d into main Jun 22, 2026
75 of 77 checks passed
@cirospaciari
cirospaciari deleted the claude/node-vm-v26-tests branch June 22, 2026 23:26
cirospaciari added a commit that referenced this pull request Jun 22, 2026
…ompat

ErrorCode.rs: #32018 inserted ERR_VM_MODULE_DIFFERENT_CONTEXT at 304 and
shifted everything after by +1; renumber WORKER_MESSAGING_* and the
appended tail (318-331), COUNT=332.
@alii

alii commented Jun 23, 2026

Copy link
Copy Markdown
Member

🎉

cirospaciari added a commit that referenced this pull request Jun 23, 2026
…es (#32608)

## What

`S3DownloadStreamWrapper::on_stream_cancelled` only set
`signal_store.aborted = true` and relied on the HTTP thread to
eventually deliver a final `has_more == false` callback (via
`opaque_callback` → `callback`'s scopeguard) to free the heap-allocated
wrapper and the `S3HttpDownloadStreamingTask`. On an idle socket — the
server has sent at least one chunk and then gone quiet without closing,
exactly the scenario `test/js/bun/s3/s3-stream-cancel-leak.test.ts` sets
up — the HTTP thread is parked in its event loop and never re-checks the
flag, so the terminal callback never arrives and both the wrapper and
its `path: Box<[u8]>` leak.

ASAN on Debian 13 x64 reports it as:
```
SUMMARY: AddressSanitizer: 39731 byte(s) leaked in 84 allocation(s).
```
with the path-clone allocation traced through `s3/client.rs:1265 →
ReadableStream::from_blob_copy_ref → Blob::get_stream`.

## Fix

Pair the abort flag with
`bun_http::http_thread().schedule_shutdown((*task).http)` so the HTTP
thread wakes up, observes the abort, closes the socket, and invokes the
terminal callback — same pattern `FetchTasklet::abort_task` already
uses. Taken under `(*task).mutex` because `update_state` writes
`task.http` concurrently.

## Why this isn't in #32018

The vm PR doesn't touch any of
`src/runtime/webcore/{s3,Blob,ReadableStream}.rs`; this leak reproduces
on `main` independently.
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.

3 participants