Skip to content

This site describes solve-engine as it is on main: 2.43.0, which npm does not have yet. npm installs 2.40.0, so a page may show an answer that version does not give yet.

VMCheckpointer

Defined in: packages/engine/src/vm/VMCheckpoints.ts:88

Manages VM state checkpoints for the three-tier evaluation strategy.

Checkpoint creation: After a variable-definition line executes (Tier 1 or Tier 3), snapshot() records the current values of the written variables. The checkpoint is linked via prototypal inheritance to the previous checkpoint, so only changed variables consume memory.

Checkpoint restoration: Before evaluating a viewport whose start line is not line 1, restoreTo(lineNumber) resets the VM and replays all variable definitions up to and including that line. This avoids re-evaluating the entire document from line 1 on every scroll.

Thread safety: Checkpoints are created synchronously on the main thread during evaluation. They are immutable after creation (Value is an immutable type), so no synchronization is needed.

Integration with Phase 5.2e: setViewport() will use getNearestCheckpoint() to find the checkpoint just before the new viewport start, then call restoreTo() to set up the VM before evaluating only the visible lines. This is the key to O(visible lines) scrolling instead of O(document).

new VMCheckpointer(vm): VMCheckpointer;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:107

ParameterType
vmVM

VMCheckpointer

get count(): number;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:747

Number of checkpoints stored.

number


get isEmpty(): boolean;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:752

Returns true if no checkpoints have been created.

boolean


get syncedLine(): number | null;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:713

The line syncTo last put the VM at, or null when that is not known. A diagnostic for tests.

number | null


get vmInstance(): VM;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:757

The associated VM instance.

VM

applyCheckpointAt(lineNumber): boolean;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:364

Apply the bindings recorded AT lineNumber, leaving the rest of the VM alone.

restoreTo rebuilds the whole prefix, which costs the chain every time it is called. A caller moving forward through the document already holds the prefix up to the line before, and needs only what this line added: restoring once and then applying each line in turn as it is passed costs the chain once rather than once per line.

ParameterTypeDescription
lineNumbernumber1-based line whose own bindings are applied.

boolean

Whether a checkpoint existed at that line.


clear(): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:609

Clear all checkpoints. The underlying VM is NOT reset, call vm.reset() separately if needed.

void


desync(): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:705

Forget which line the VM is at, after something outside the chain changed it wholesale (another document ran on the same engine). The next syncTo sets every name the chain records.

void


dropCheckpointAt(lineNumber): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:402

Drop the entry a line holds, because the line no longer writes anything.

A line’s entry is replaced when the line writes again and left alone otherwise, so a definition edited into an expression with no definition in it (:v3 = 44 edited to 7 + 7) kept saying v3 = 44 in the chain. The lines below it that asked what the prefix holds were told 44, where a pass from scratch has nothing there. The entry after it is re-parented to the one before, the same way snapshot keeps the links straight.

ParameterTypeDescription
lineNumbernumber1-based line whose entry, if any, goes.

void


forget(names): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:443

ParameterType
namesreadonly string[]

void


getAllCheckpoints(): readonly VMCheckpoint[];

Defined in: packages/engine/src/vm/VMCheckpoints.ts:490

Get the entire checkpoint chain from root to the last checkpoint. Useful for debugging and serialization.

readonly VMCheckpoint[]


getCheckpointAt(lineNumber):
| VMCheckpoint
| undefined;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:477

Get a specific checkpoint by its line number.

ParameterType
lineNumbernumber

| VMCheckpoint | undefined

The checkpoint, or undefined if not found.


getNearestCheckpoint(lineNumber): VMCheckpoint | null;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:468

Find the nearest checkpoint at or before the given line number.

Uses linear scan (checkpoints are sorted by lineNumber and the list is short, typically < 20 for Obsidian documents). Can be upgraded to binary search if needed for documents with 1000+ variable defs.

ParameterType
lineNumbernumber

VMCheckpoint | null

The nearest checkpoint, or null if none exists before the line.


indexAgreesWithChain(): boolean;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:583

Whether the per-name index says exactly what the chain says: for every name, the checkpoints holding it in their own bags, in document order. A diagnostic for tests; linear in the chain.

boolean


lookupFunctionBefore(name, lineNumber): UserFunctionDef | undefined;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:506

The function a name was bound to at the end of the line before lineNumber, or undefined if no line above it had defined one.

lookupVariableBefore, for the functions bag: a function definition is a definition, and one edited away or failed leaves the name as the lines above left it just as a variable does.

ParameterTypeDescription
namestringThe function name.
lineNumbernumberThe 1-based line whose own entry is to be excluded.

UserFunctionDef | undefined

The definition the lines above made, or undefined.


lookupVariable(name): Value | undefined;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:549

The value a name holds at the end of the chain, or undefined if no checkpoint holds it. Read from the index, as lookupVariableBefore.

Note: This queries the checkpointer’s snapshot, not the VM. The VM may have been modified since the last snapshot (e.g., by Tier 2 execution of non-variable-def lines that don’t create checkpoints).

ParameterType
namestring

Value | undefined


lookupVariableBefore(name, lineNumber): Value | undefined;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:537

The value a name held at the end of the line before lineNumber, or undefined if no line above it had set one.

What a definition that failed leaves behind. A pass from scratch skips the store when the right-hand side errors, so the name keeps whatever the lines above it had put there: :x = 1 then :x = zz leaves x at 1, and :x = zz on its own leaves it undefined. The incremental path holds the value from the previous pass instead, which for the line that failed is its own old answer, so the evaluator asks here what the prefix holds and puts that back.

Read from the per-name index, not by following parent. The chain from the nearest checkpoint back to the root visits exactly the entries of the array before it, in reverse, so the first one found holding the name is the last entry at or before the line whose own bag holds it. The index lists those entries per name in document order, and a binary search finds that one directly: O(log k) in the number of lines that wrote the name. Following the links cost the distance to the root for a name no line above defines, which is every name in an ordinary list of assignments, so a repeat pass over bare assignments was quadratic in the document (#712).

ParameterTypeDescription
namestringThe variable.
lineNumbernumberThe 1-based line whose own entry is to be excluded.

Value | undefined

The value the lines above set, or undefined.


noteLineRan(lineNumber): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:682

Record that lineNumber has just run on a VM syncTo had put at the line before it, and recorded what it wrote, so the VM now holds the state at the end of lineNumber. Any other order leaves the line unknown, and the next syncTo sets every name.

ParameterTypeDescription
lineNumbernumberThe 1-based line that ran.

void


renumber(positionOf): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:424

Follow every line to its new position after a structural edit.

An insert or a delete moves every line below it, and the chain is read by position. It used to be cleared instead and rebuilt as lines ran, but a clean line above the viewport never runs, so its entry was gone for good and a line below asking what the prefix held was told nothing: :a = 5 above the viewport was reported as undefined by :b = a + c under it. Each entry follows its line by id, the entry of a deleted line goes, and the parents are linked again in the new order.

ParameterTypeDescription
positionOf(lineId) => numberThe 1-based position a line id has now, or -1 for a line that is gone.

void


restoreTo(lineNumber): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:240

Restore the VM to the state at or just after the given line number.

Finds the nearest checkpoint whose lineNumber <= targetLineNumber, then replays all variable definitions from root → that checkpoint into the VM via setVar(). The VM’s stack is also reset.

If no checkpoint exists at or before the target line, the VM is fully reset (empty scope, empty stack).

Performance: O(total variable definitions before the line), since each checkpoint in the chain contributes only the names its own line wrote.

ParameterTypeDescription
lineNumbernumberTarget 1-based line number. The VM will have the state that existed AFTER evaluating lines up to lineNumber.

void


resync(names): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:694

Put names back to what the chain holds for them at the line the VM is at, after something outside the chain changed them (the evaluator clearing running totals at the start of a pass). Nothing to do while that line is not known: the next syncTo sets every name.

ParameterTypeDescription
namesIterable<string>The names changed.

void


snapshot(
lineNumber,
lineId,
variableNames,
functionNames?
): VMCheckpoint | null;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:134

Create a checkpoint at the current line, recording the VM values of the specified variables.

Records only what this line wrote; the rest is reached through the parent link rather than copied.

A line that is snapshotted again drops every checkpoint at or after it first, so the list stays in document order and the new checkpoint inherits from the line before it rather than from one after it. See the body for what went wrong without that.

ParameterTypeDescription
lineNumbernumber1-based line position.
lineIdnumberPersistent line ID from DocumentModel.
variableNamesstring[]Names of variables that were written at this line.
functionNames?ReadonlySet<string>Which of those names the line defined as functions, from the bytecode it ran. Without it the VM is asked, which cannot tell a line that defined f from one that set the variable f under a function of that name defined above.

VMCheckpoint | null

The new checkpoint, or null if no variable names provided.


syncTo(lineNumber): void;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:643

Put the VM into the state the document has at the end of lineNumber, for every name the chain records, by moving from the line it was last put at rather than rebuilding it.

The evaluator runs a line against the VM, and the VM holds whatever the lines that ran last wrote. A pass that skips the clean lines above the viewport, or runs only the viewport, therefore handed a line the values of lines below it: x * 2 above x = 5 answered 10 from the second pass on, and x + 100 between :x = 1 and :x = 99 answered 199 once the viewport started below line 1, where a pass from scratch answers x is undefined and 101.

Moving down applies the entries passed on the way, in document order; moving up sets each name an entry passed on the way wrote back to what the lines above lineNumber left it, or removes it. Either costs the entries between the two lines, so an edit or a scroll costs the distance it moves rather than the length of the document. When the line is not known (the first call, after renumber, or after another caller applied entries of its own) every name the chain records is set from it instead. A name the chain does not record, a host’s own for example, is left as it is, and so is anything else the VM holds (a stored equation).

ParameterTypeDescription
lineNumbernumberThe 1-based line, 0 for the state before line 1.

void


updateCheckpointAt(lineNumber, variableNames): boolean;

Defined in: packages/engine/src/vm/VMCheckpoints.ts:330

Record what lineNumber has just written, without disturbing the chain after it.

snapshot drops every checkpoint at or after the line, which is right for a pass running forward in document order: it re-takes them as it goes. A caller re-running a few lines out of a document does not, and dropping the entries for lines it will never visit would lose the very bindings it is sweeping through them to collect. So this overwrites in place instead.

Safe against the prototype chain, because it replaces only the checkpoint’s OWN bindings: a later checkpoint that also writes the name holds its own copy and goes on shadowing this one.

ParameterTypeDescription
lineNumbernumber1-based line whose recorded bindings are refreshed.
variableNamesstring[]The names it wrote.

boolean

Whether a checkpoint existed at that line to update.