Module: lib/core/trace/globals

Deterministic substitution for the sandbox's global whitelist.

This is the hinge the whole replay story turns on.

Avenx evaluates every template expression, computed property and action body as source text under with(sandbox), and the sandbox proxy's has trap claims every key. So every identifier those expressions resolve — Date, Math, state, anything — passes through one get trap, and that trap hands out globals from a single line. Substituting a recording clock there costs nothing and cannot be bypassed from inside a template. Substituting a replaying clock in the same place is what makes a recorded session reproducible.

Frameworks that compile expressions to closures have no equivalent point: by the time the code runs, Date is a free variable resolved against the real global scope, and the only way back in is to rewrite the program.

What this does not cover

Bridge modules and anything they import are ordinary ES modules. They are not sandboxed, so a Date.now() in a bridge action reaches the real clock and is invisible here. That is a genuine hole, and it is why recording does not get the final say on whether a trace is deterministic — replay verifies the claim by comparing observations. See replay.js.

Source:

Members

(inner, constant) markedReads :Set:.<string:>

Causal parents that have already been marked as reading a given non-deterministic global, so a loop reading Date.now() a thousand times produces one marker rather than a thousand.

Type:
  • Set:.<string:>
Source:

(inner, constant) overrides :Map:.<string:, any:>

Globals currently substituted for sandbox lookups, keyed by identifier.

Empty in normal operation, so resolveSandboxGlobal degenerates to a Map.size check and a property read.

Type:
  • Map:.<string:, any:>
Source:

Methods

(static) clearGlobalOverrides()

Removes every substitution, restoring the real globals.

Source:

(static) hasGlobalOverrides() → {boolean}

Whether any global is currently substituted.

Source:
Returns:
Type
boolean

(static) installRecordingGlobals(recorder)

Substitutes globals that log every non-deterministic value they hand out.

The values themselves are real — a recording session must behave exactly as an untraced one would — but each is appended to the recorder's global log so replay can hand the same sequence back.

Parameters:
Name Type Description
recorder object

The recorder to log into.

Source:

(static) installReplayGlobals(globals, onExhausted) → {Object}

Substitutes globals that replay a recorded sequence of values.

When the recorded sequence is exhausted the replay has consumed more non-determinism than the recording produced, which means it has already diverged. That is reported through onExhausted rather than papered over by looping or by falling back to the real clock, either of which would let a diverged replay finish looking successful.

Parameters:
Name Type Description
globals object

The globals block of a trace.

Properties
Name Type Attributes Description
now Array:.<number:> <optional>

Recorded Date.now() readings, in order.

random Array:.<number:> <optional>

Recorded Math.random() readings, in order.

onExhausted function

Called with the source name when a log runs out.

Source:
Returns:

Live read cursors, for assertions.

Type
Object

(static) resolveSandboxGlobal(key) → {any}

Resolves a whitelisted global for the expression sandbox.

The sandbox calls this instead of reading globalThis[key] directly, so a recording or replaying session can substitute a deterministic implementation without patching the page's real globals — which would leak into unrelated scripts and outlive the recording.

Parameters:
Name Type Description
key string

The whitelisted global identifier.

Source:
Returns:

The value template code should see.

Type
any

(inner) buildDate(readClock) → {function}

Builds a Date stand-in that reports a caller-supplied clock.

prototype is aliased to the real Date.prototype and construction delegates to the real constructor, so instances are genuine Date objects and x instanceof Date keeps working in application code.

Parameters:
Name Type Description
readClock function

Supplies the epoch milliseconds for a "now" reading.

Source:
Returns:

A drop-in replacement for Date.

Type
function

(inner) buildMath(readRandom) → {object}

Builds a Math stand-in whose random is supplied by the trace.

Created with Object.create(Math) so every other member — max, floor, PI — resolves through the prototype chain to the real implementation rather than being copied, which would silently drop anything a future engine adds.

Parameters:
Name Type Description
readRandom function

Supplies the next random value.

Source:
Returns:

A drop-in replacement for Math.

Type
object

(inner) markGlobalRead(kind)

Records that application code observed a non-deterministic global.

The values themselves go into the recorder's compact log; this marker exists so the causal graph knows where the read happened. Contract analysis then asks whether any ancestor of that point declared itself deterministic — which is how a build-time contract becomes a claim checked against what the code actually did. See trace/contracts.js.

Parameters:
Name Type Description
kind 'now' | 'random'

Which source was read.

Source: