Skip to content

WIP: Re-apply Replay Node.js instrumentation onto Node 27 canary (V8 15.1) - #126

Open
BLamy wants to merge 40 commits into
node27-canary-basefrom
node27-m151
Open

WIP: Re-apply Replay Node.js instrumentation onto Node 27 canary (V8 15.1)#126
BLamy wants to merge 40 commits into
node27-canary-basefrom
node27-m151

Conversation

@BLamy

@BLamy BLamy commented Jun 6, 2026

Copy link
Copy Markdown

WIP: Re-apply Replay Node.js instrumentation onto Node 27 canary (V8 15.1)

replayio/node forked at v16.14.2 (442e84a3). This re-applies Replay's Node fork
instrumentation onto nodejs/node-v8 canary (d746551f = Node 27.0.0-canary / V8 15.1.42).

Why the canary? To match the V8 the Chromium/V8 port targets (V8 15.1, M151). No released or
nightly Node ships V8 15.1 (Node 26.3 = V8 14.6); node-v8's canary floats on V8 tip at 15.1.42 —
one patch off replayio/chromium-v8 PR nodejs#282's 15.1.43 — so deps/v8 lines up between the two.

Landed (replay-portnode27-m151, ~240 files, +8.8k/-2.3k)

  • Node-core (src/uv/openssl/lib/icu): 20 new files (incl. lib/internal/recordreplay/*.js) +
    163 modified. 171/184 hunks auto-placed (93%) via the kit's anchor applier; 13 drifted
    conflicts hand-resolved
    (see below). 0 unanchored.
  • deps/v8: Replay V8 instrumentation integrated from replayio/chromium-v8 PR build: Warn when using snapshot on Armv6 nodejs/node#282, plus
    the node↔V8 recordreplay contract completed for Node 27.
  • New Node-27 surfaces profiled/guarded: ncrypto::CSPRNG randomness chokepoint; unsupported
    new surfaces guarded with InvalidateRecording.

Hand-resolved drift (highlights — full table in CONFLICTS-RESOLVED.md)

  • node_perf: timeOrigin* globals → performance_process_start*, now cached in Environment ctor.
  • node_credentials.cc: SafeGetenv refactor → wrapped uid/gid check in !HasDivergedFromRecording().
  • crypto_random.cc: DeriveBits now uses ncrypto::CSPRNG → capture rv + AssertBytes.
  • libuv async.c/fs.c: rewritten to C11 atomics / new iovec paths → re-instrumented ordered locks.
  • OpenSSL 1.1.1n→3.x: err_prn.c tid handling re-done; provider-model RNG hooks deferred.

Open follow-ups (need Replay-team judgment — flagged with TODO(replay-port))

  1. InitPerformance() call site — must run before the first Environment is constructed when
    recording/replaying; the fn + writable globals are in place but the call isn't wired.
  2. OpenSSL-3 RNG hooksdrbg_lib.c/rand_unix.c gone in the provider model; decide whether to
    re-instrument at the provider layer or rely on higher-level crypto_random.cc determinism.
  3. libuv async/fs — re-instrumented but not compile/replay-tested; threading-determinism
    semantics need review.

Base is node27-canary-base (d746551f); diff is exactly the replay re-application. WIP —
auto-placed hooks need build/replay validation.

BLamy and others added 30 commits June 6, 2026 15:38
- node_perf.{cc,h,common.h}: re-anchor timeOrigin->performance_process_start
  (non-const + InitPerformance); TODO: wire InitPerformance() call site
- node_credentials.cc: SafeGetenv divergence guard (refactored to linux_at_secure)
- crypto_random.cc: AssertBytes on ncrypto::CSPRNG output
- node_platform.cc: ordered-lock arg on TaskQueue
- deps/uv/async.c: re-instrument ordered locks for C11-atomics rewrite
- deps/uv/fs.c: re-instrument uv__fs_read (uv__fs_preadv removed)
- openssl/err_prn.c: constant tid (OpenSSL 3.x rewrite)
- README.md, node_version.h: dropped (cosmetic/obsolete)
…hokepoint

Covers all new randomness consumers (keygen, QUIC CIDs/tokens, DTLS, WebCrypto,
randomUUID) at one point. See NEW-SURFACES.md for the full inventory of new
non-deterministic subsystems (undici/fetch, QUIC, node:sqlite, FFI, fs.watch, ...)
and recommended instrumentation/priority.
node:sqlite (DatabaseSync::Open), fs.watch (FSEventWrap::Start), QUIC
(Endpoint::Listen/Connect): invalidate the recording rather than diverge
silently. Each has a TODO(replay-port) for real support. See NEW-SURFACES.md.
… (V8 15.1/M151)

Re-pathed the PR's base..head diff to deps/v8/ and applied cleanly (git apply
exit 0) onto Node 27's V8 15.1.42 — only 1 build off the PR base (15.1.43).
127 files (+6126/-101), incl. the recordreplay runtime (include/replayio.h,
src/replay/*). This provides the v8::recordreplay API + V8RecordReplay* C shims
that every node-core/libuv/ncrypto hook depends on.
- sync V8 symbol restorations from chromium-v8 v8-m151-node-symbols into deps/v8
  (OnAnnotation C++ method, AssertScriptedCaller stub, V8RecordReplayIsRecording/
   IsReplaying + variadic V8RecordReplayDiagnostic)
- node_platform.cc: AutoPassThroughEvents moved to v8::replayio:: (PR nodejs#282), add
  replayio.h include
All node/libuv/ncrypto recordreplay symbols now resolve against this V8 (0 missing).
…de 27)

The anchor applier had dropped InitializeRecordReplay(&argc,&argv) inside
RecordReplayFinishRecording() where argc/argv aren't in scope (compile error)
and before its own definition. Move it to StartInternal(), after uv_setup_args
and before InitializeOncePerProcessInternal/V8 init — so the driver attaches
before V8 and any Environment, and command-line args are recorded.

Also wire InitPerformance() there (guarded by IsRecordingOrReplaying): Node 27
caches performance_process_start[_timestamp] into Environment at construction,
so they must be re-captured deterministically before the first Environment.
Resolves follow-up #1 in CONFLICTS-RESOLVED.md.
node's deps/v8 (V8 15.1.42) carries the same Replay instrumentation as the
chromium-v8 fork (15.1.0) and therefore the same V8 15.1 compile bugs. Rather
than re-derive, cross-ported the verified chromium-v8 fixes (PR nodejs#282) into
node's deps/v8, preserving node-specific V8 patches:

- Mutex(const char* ordered_name) ctor: update mutex.cc to match the ported
  header (was the first CI failure: "no declaration matches Mutex::Mutex()").
- Universal V8 15.1 renames: Script::WITH_OFFSET -> Script::OffsetFlag::kWithOffset;
  OperandType::kIdx -> kUImm + GetIndexOperand -> GetUnsignedImmediateOperand
  (kIdx was an upstream 10.8 type removed in 15.1); LoadRegisterTagged ->
  LoadRegister; GetAccumulatorTagged -> GetAccumulator.
- Structural fixes cross-ported across 41 files (adversarially verified): orphaned
  replay statements relocated into their function scope; AutoDisallowEvents missing
  includes; Cast<JSAny> on Object::Get/SetProperty; MaybeDirectHandle; maglev
  VisitRecordReplay* moved to file scope + ReduceResult; call_head_token_position
  and KeyIterationParams threading; weak-ref intrinsics; dup/dead removals.
- Missing backing declarations node's port had dropped:
  * opcodes.h: register V(IncrementAndCheckProgressCounter) (the replay opcode the
    simplified-operator/verifier/maglev refs require; dropped on the 15.1.42 import).
  * js-objects.h: add TaggedMember<Smi> record_replay_bookmark_ backing the
    DECL_INT_ACCESSORS + js-objects.tq field (JSMessageObject is hand-laid-out).
  * keys.cc: thread KeyIterationParams* params through GetFastEnumPropertyKeys and
    GetOwnKeysWithElements (bodies referenced params; signatures weren't updated).

Brace balance verified (comment/string-aware) on all 51 touched files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…untime/exec)

Second wave of node's deps/v8 fixes — the node-SPECIFIC replay hooks (not shared
with chromium-v8, so not covered by the cross-port) that used removed V8 10.8 APIs.
All adversarially verified.

api.cc:
- FLAG_xxx -> v8_flags.xxx (13; FLAG_scavenge_task -> minor_gc_task)
- Isolate exception API unified: has/pending/scheduled_exception -> has_exception/
  exception/clear_internal_exception/set_exception (scheduled slot folded in)
- Map/Set::AsArray + Object::PreviewEntries: thread KeyIterationParams* (match hdrs)
- JSMessageObject::GetIsolate removed -> EnterV8NoScriptNoExceptionScope;
  JavaScriptFrameIterator -> JavaScriptStackFrameIterator; Summarize() returns
  FrameSummaries by value; Handle<Script>::cast -> i::Cast<i::Script>.

debug.cc / runtime-debug.cc / execution.cc / debug-stack-trace-iterator.cc /
parse-info.cc / pending-compilation-error-handler.cc / replay-runtime-weak-refs.cc:
- X::cast(y) -> i::Cast<X>(y); member .IsScript()/.IsJSFunction() -> free IsScript()/
  IsJSFunction(); Tagged '.' -> '->' fixes.
- RecordReplayHasRegisteredScript unified to Tagged<Script> across decl/def/calls.
- runtime-debug.cc GetStackLocation: CommonFrame::Summarize(&frames) (removed
  out-param API) -> FrameSummaries summaries = ...Summarize().
- execution.cc: deref *function->shared() into const SharedFunctionInfo&; add
  file-scope externs (gProgressCounter, gRecordReplayEnableDependencyGraph,
  RecordReplayIsDivergentUserJSWithoutPause).
- weak-refs.h/.cc: ReplayWeakRefPins::Pin/Unpin take Tagged<HeapObject>.

Brace balance verified (delta vs HEAD) on all 11 files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compiler::GetWrappedFunction's 'replaced source contents while replaying'
block (ReplayingReplaceScriptContents) referenced 10.8-era locals that don't
exist in 15.1 (parsing_while_debugging/restriction/parameters_end_pos/outer_info,
context->IsNativeContext, and built a JSFunction into a DirectHandle<SFI>).
Rewrote it to mirror the function's own 15.1 wrapped-compile path (NewScript +
CompileToplevel, IsNativeContext(*context), v8_flags.lazy, kWrapped syntax) and
replace |result| with the recompiled SharedFunctionInfo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Execution::Call now takes base::Vector<const DirectHandle<Object>> (not
isolate,fn,recv,argc,argv): callArgs arrays -> DirectHandle<Object>,
calls -> base::VectorOf(callArgs) / {} for the 0-arg case. Add missing
json-parser.h + json-stringifier.h includes (JsonParser<uint8_t>::Parse and
JsonStringify calls were already 15.1-correct, just undeclared).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e.cc

The anonymous namespace opened at the stack-walk helpers (VisitStack /
VisitStack_ForCallSiteBuilder) was never closed, so
Isolate::RecordReplayInvokeApiInterruptCallbacksAtProgress (and the rest of the
file's Isolate members) ended up defined inside it -> 'definition not in
namespace enclosing Isolate'. Add the missing '}  // namespace' before the
first Isolate:: member. (depth check: final brace depth 1 -> 0.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Followup to the prior anon-namespace fix: closing the anon namespace before
RecordReplayInvokeApiInterruptCallbacksAtProgress left the trailing stack-trace
helpers + the original '}  // namespace' (now over-closing 'internal', dropping
CaptureAndSetErrorStack et al into public 'namespace v8'). Reopen 'namespace {'
after the RecordReplay member so the helpers stay in the anon namespace and the
existing close balances. Verified: CaptureAndSetErrorStack back at v8::internal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e link)

V8 fully COMPILED but torque (a libbase-only build tool) failed to LINK:
'Undefined symbols: v8::recordreplay::IsARMRecording()'. The libbase platform
files reference recordreplay:: functions defined in v8_base (api.cc), which
torque/mksnapshot don't link. Reference the C ABI (V8RecordReplayIsARM /
V8IsRecordingOrReplaying) weakly (V8_WEAK) so those build tools resolve them to
null (a build tool never records); libv8 still binds the strong api.cc defs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previous commit put 'extern "C" V8_WEAK ...' inside function bodies, which is
illegal ('expected unqualified-id before string constant'). Move the weak C-ABI
declarations (V8RecordReplayIsARM / V8IsRecordingOrReplaying) to namespace scope
in platform-darwin.cc / platform-linux.cc; the call sites keep the weak null-check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aybeHandle

15.1 MaybeHandle<T> has no implicit DirectHandle<T> conversion; 'return keys;'
(keys is DirectHandle<FixedArray>, fn returns MaybeHandle<FixedArray>) failed.
Use indirect_handle(keys, isolate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d in 15.1)

V8 15.1 removed src/base/optional.h (migrated to std::optional). The replay
AutoMaybeDisallowEvents helper included it + used base::Optional. Switch to
<optional> / std::optional.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The promise-adoption hook (RecordReplayShouldCallOnPromiseHook /
AddPromiseDependencyGraphAdoption) was relocated into JSPromise::Resolve, but the
forward decls sit ~600 lines later -> 'not declared in scope'. Add the decls
before JSPromise::Resolve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ved in 15.1)

The WriteString two-byte-widening replay hook used base::ScopedVector<uc16>,
removed in V8 15.1. Use base::OwnedVector<uc16>::New(n) (.size(), .as_vector()).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ries/internalProperties defs

The .h declarations + callers carry 'const v8::KeyIterationParams* params' but
the .cc definitions of V8Debugger::collectionsEntries/internalProperties were
still 2-arg ('no declaration matches'). Add the param to both defs and thread it
(PreviewEntries(&isKeyValue, params); collectionsEntries(context, value, params)).
(chromium-v8 already had this; node's port was behind.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Uint

The baseline VisitRecordReplay* handlers used the 10.8 SaveAccumulatorScope(&basm_)
ctor (now (this, &basm_)) and Index(0) (now Uint(0), since the RR bytecodes use
kUImm). Fix both across the 5 handlers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tubs

bytecode-graph-builder: JSGraph::Constant(uint32_t) was removed in 15.1;
the RecordReplay instrumentation visitors now use ConstantNoHole (resolves
to the double overload, matching sibling coverage/offset constants).

platform-{darwin,linux}: replace weak *declarations* of the recordreplay
C-ABI hooks with weak *definitions* returning false — a weak undefined ref
links on ELF but not on macOS Mach-O ('symbol not found' from torque/
mksnapshot, which link libbase only and never record). The strong api.cc
definition still overrides these for libv8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 3-way patch apply stranded RecordReplayAssertFromC calls at file scope:

- o_str.c: the openssl_strerror_r assert landed between two functions;
  moved it inside openssl_strerror_r (its target function still exists).

- rand_lib.c: 4 asserts for rand_drbg_get_entropy were dumped at file
  scope referencing undefined drbg/buffer. That function was removed in
  OpenSSL 3.x's DRBG->providers refactor (no equivalent drbg->parent path
  in ossl_prov_get_entropy), so the asserts never compiled in this port.
  Retired them (and the now-unused extern decl) -- RNG determinism is
  enforced by the driver intercepting the OS entropy source; these were
  diagnostic asserts for the old 1.1.1 code structure. Matches the port
  plan's 'retire obsolete hooks' provision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
node_js2c (and other host tools) link libuv.a but not libv8, leaving
libuv's V8RecordReplay* hook calls undefined at link. Add weak no-op
definitions of all 14 referenced hooks to uv-common.c (compiled into
every libuv.a); the strong libv8 (api.cc) defs are statically linked into
the real node binary and override them. Tools never record, so no-ops are
correct. Guarded to GCC/Clang non-Windows where these unix hooks are used.

Also add the missing DLLEXPORT to api.cc V8RecordReplayAssert so it matches
its 13 siblings (export consistency for shared/embedder builds).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RecordReplayAssertFromC was only declared+called in o_str.c; its canonical
definition lived in OpenSSL 1.1.1's crypto/rand/rand_unix.c (+ drbg_lib.c),
both of which moved into providers/ or were removed in node 27's OpenSSL 3.x,
so the definition hunk never landed and the symbol was undefined everywhere.
This broke linking of every target that pulls in libcrypto's o_str.o without
the recordreplay runtime -- the node binary AND standalone tools like
openssl-cli.

Restore the canonical Replay shim (dlsym RTLD_DEFAULT 'RecordReplayAssert' at
runtime, no-op when the driver isn't loaded) next to its sole remaining
caller. Always defined, so all targets link; forwards only when recording.
Windows-guarded since o_str.c (unlike the original unix-only home) is
cross-platform.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… alloc)

node_errors.h: the v8::recordreplay::Assert("ThrowException %s", message)
hook was stranded at namespace scope between the error-message list and the
THROW_##code macro -- 'message' is undeclared there and a qualified
recordreplay::Assert can't be declared inside namespace node, which broke
EVERY TU including this header (environment.cc, exceptions.cc, etc). Moved it
inside the THROW_##code(isolate) macro body, matching the printf-style
THROW_##code template that already places the same assert correctly.

environment.cc: node 27 refactored NodeArrayBufferAllocator::Allocate to
always zero-fill (dropping the old zero_fill_field_/zero_fill_all_buffers
conditional), so the ported hook referenced a removed member and left a
malformed if (no else, possibly-uninitialized ret, missing COUNT_GENERIC_
USAGE). Restored Allocate to pristine and relocated the determinism hook to
AllocateUninitialized -- the only remaining uninitialized path -- where it
zero-fills during record/replay so recordings stay deterministic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mechanical hook apply landed two v8::recordreplay::Assert calls at a
bad line within multi-line constructs:

- cares_wrap.cc AfterGetNameInfo: the assert was injected into the middle
  of the function's parameter list (between 'int status,' and the next
  param), so it parsed as a declaration -> 'no type named Assert in
  v8::recordreplay'. Moved it to the first statement of the body.

- env.cc RunAndClearNativeImmediates: the assert was injected between
  'while (auto head = queue.Shift())' and its body 'head->Call(this);',
  making the assert the loop body and pushing head->Call() out of head's
  scope. Moved it to a statement just before the loop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 'ordered mutex' determinism hook added
native_immediates_threadsafe_mutex_(/* ordered */ true) to the
Environment::Environment member-initializer list but terminated it with
' {', prematurely opening the constructor body and orphaning the real final
initializer thread_name_(thread_name). The parser then read
thread_name_(thread_name) as a statement calling a std::string, cascading
into ~20 'function definition not allowed here' errors through the rest of
env.cc. Change the stray ' {' to ',' so the mutex initializer is a normal
list member and thread_name_(thread_name) remains the body opener. (This
also resolves the file's real 308/307 brace imbalance.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Environment ctor: list thread_name_ before native_immediates_threadsafe_
  mutex_ to match declaration order (env.h 1188 vs 1228), silencing
  -Werror,-Wreorder-ctor.

- The Environment::GetNow time-determinism hooks (Assert + RecordReplayBytes
  recording 'now') had been dropped into the middle of a MakeCallback(...)
  argument list in CheckImmediate, where 'now' is undeclared. Removed them
  from there (restoring the clean MakeCallback call) and placed them in
  Environment::GetNow() right after 'uint64_t now = GetNowUint64();', so the
  clock value is recorded/replayed deterministically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When removing the misplaced GetNow hooks from this MakeCallback arg list in
the previous commit, I also dropped the 5th argument (argv = nullptr) -- the
botched hooks had overwritten that line. node 27's MakeCallback takes
(isolate, recv, fn, argc, argv, async_context); restore the nullptr argv to
match the pristine call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BLamy and others added 10 commits June 8, 2026 07:17
node.cc: the entire Replay definitions block (gRecordReplay* fn-pointer
decls, 'namespace recordreplay {}', RecordReplayFinishRecording,
OpenDriverHandle, InitializeRecordReplay) had been inserted INSIDE
InitializeNodeWithArgsInternal -- between the 'node_is_initialized' comment
and the node_is_initialized=true epilogue -- so 'namespace recordreplay'
was being defined in function scope (error) and gBuildId/InitializeRecord
Replay appeared undeclared. Moved the function's 3-line epilogue above the
block so the function closes after the comment and the Replay defs sit at
namespace scope (InitializeRecordReplay is still called later at startup).

node_api.cc (napi_module_register_by_symbol): restored the missing
'napi_value _exports = nullptr;' declaration, removed the leftover duplicate
env->CallIntoModule() (the replay if/else replaces it), and updated the two
v8::recordreplay::RegisterPointer calls to M151's (name, ptr) signature
(the fork's original 1-arg form predates the named driver API).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Replay print_build_id block used the old result.exit_code = 0 /
result.early_return = true form. In node 27 'result' is a
shared_ptr<InitializationResultImpl> with renamed members and an ExitCode
enum, so use result->exit_code_ = ExitCode::kNoFailure / result->early_
return_ = true, matching the sibling print_version block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pile

The 3-way apply mis-anchored this hook onto AddExternalizedBuiltin, wrapping
its source-loading block in if(!IsRecordingOrReplaying()) -- which left
'resource' uninitialized on the R/R path (-Werror,-Wsometimes-uninitialized).
That hook belongs on the code-cache path (originally node_native_module.cc::
LookupAndCompile, renamed to node_builtins.cc in node 27): reverted
AddExternalizedBuiltin to pristine and wrapped BuiltinLoader::LookupAndCompile's
cached_data lookup block instead. There cached_data stays default-constructed
during R/R (has_cache=false), so the builtin compiles fresh -- the safe,
intended behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce::Run

The hook had been dropped into the middle of the assignment
'*exit_code = SpinEventLoopInternal(env).FromMaybe(...)', so it parsed as
'*exit_code = RecordReplayFinishRecording()' (void -> 'assigning to ExitCode
from void'). Per the original fork, it belongs after the if(*exit_code ==
kNoFailure) block (right where the old code called it before ResetStdio).
Restored the assignment and moved the call there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
node_os.cc: three 'if (MaybeMarkUnavailable(args)) return;' entry guards had
been dumped together at namespace scope after GetCPUInfo. Per the kit each
OS-info accessor gets one; the three belong to GetFreeMemory, GetTotalMemory
and GetUptime (the three that were left without a guard). Removed the
stranded block and added a guard to the top of each.

node_platform.cc: the non-deterministic-task check + Assert (both referencing
'task') were stranded between PostIdleTaskImpl and PostTaskImpl; 'task' is a
param of PostTaskImpl, so moved them to the top of PostTaskImpl's body.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mechanical apply badly interleaved two Replay blocks with node 27's
refactored code in node_process_methods.cc:
 - a block of env->SetMethod(target, "recordReplay*", ...) calls was stranded
   at namespace scope after HrtimeBigIntImpl;
 - a block of registry->Register(recordReplay*) calls was dumped inside
   BindingData::SlowHrtime's body, leaving that function unclosed.

Removed both stranded blocks (restoring SlowHrtime/SlowHrtimeBigInt to
pristine) and re-added the 14 record/replay methods to their correct node 27
homes: CreatePerIsolateProperties (converted env->SetMethod(target, ...) to
the SetMethod(isolate, target, ...) API node 27 uses there) and the free
RegisterExternalReferences (registry->Register).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mechanical apply inserted the RecordReplayDouble helper and the Replay
'#define V' over SetHeapSnapshotNearHeapLimit's function signature, orphaning
its body at namespace scope (while loop/'args' undeclared cascade), and left
UpdateHeapStatisticsBuffer with the pristine (non-instrumented) V macro.

Restored SetHeapSnapshotNearHeapLimit's signature+body, kept RecordReplayDouble
between it and UpdateHeapStatisticsBuffer, and gave UpdateHeapStatisticsBuffer
the RecordReplayDouble V macro (matching the already-correct Space/Code
statistics siblings) so heap-statistics doubles are recorded deterministically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hook declared 'Worker* worker' inside an early if(!IsRecordingOrReplaying())
block (with a stale int Exit(1,...) call), leaving 'worker' out of scope for
the rest of the function ('unexpected namespace name worker') and the body's
worker->Exit(ExitCode,...) unreachable-by-name. Restored 'worker' to function
scope per pristine, dropped the stale early Exit(1,...), and wrapped the single
OOM worker->Exit(ExitCode::kGenericUserError, ...) in the R/R guard -- which is
the hook's actual intent (don't force workers to exit at non-deterministic
points while recording/replaying).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
inspector/node_string.cc had a stranded function body (the istringstream->
strtod 'workaround when replaying') with no signature, referencing undefined
buffer/length/ok. The StringUtil::toDouble/parseDouble it instrumented was
removed in node 27's inspector refactor (pristine goes straight from
fromUTF16 to fromUTF8, and no such method is declared anywhere), so the body
never compiled. Retired it, restoring pristine structure. (If inspector
double-parse determinism is still needed, it belongs against node 27's actual
crdtp parsing, as a separate change.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…idation

This file is normally GENERATED (and .gitignored) by the Replay build flow,
which embeds the downloaded driver binary + build id. node's standard GitHub
Actions CI (coverage / shared-lib matrix) doesn't run that generation step, so
the libnode_base.a archive was failing on the missing node_record_replay_
driver.o ('ar: ... No such file or directory').

Commit a force-added placeholder defining the symbols node.cc references
(gRecordReplayDriver / gRecordReplayDriverSize / gBuildId) with empty values so
the standard CI can link and the rest of the port stays compile-validated. The
real build flow overwrites this file with the embedded driver, so functional
record/replay builds are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant