Table of Contents

3.5.4 Execution

RDCore.Runtime.Execution.ProcedureExecutor executes a procedure's instructions in a fetch/decode loop. ProcedureExecutor.Run is that loop: the evaluation engine that "sequentially evaluate[s] each instruction in the frame" (RD-VBAL §2.3.1 Composition Root). The environment host constructs an ICallStackFrame and pushes it to the evaluation engine, which evaluates each instruction in that frame in turn.

The runtime semantics of each statement are defined on the statement's own page, linked from the dispatch table below. This page describes the loop, how it dispatches, and the state it keeps per activation.

The fetch/decode loop

ProcedureExecutor.Run is given a session, an activation, and the activation's InstructionList. It then:

  1. Reads the instruction at the activation's program counter, ICallStackFrame.Pc.
  2. Dispatches the instruction by its InstructionKind (see Dispatch by instruction kind).
  3. Reacts to what running the instruction produced: it continues at the next offset, branches, or stops.
  4. Repeats from step 1.

When the program counter reaches Items.Length, execution falls off the end of the instruction list. The activation then completes "as if execution had reached the end of the body" (the wording of MS-VBAL §5.4.2.17 Exit Sub Statement; RD-VBAL §5.4.2.17 Exit Sub Statement): the same outcome as an explicit Exit.

ICallStackFrame.Pc is read-only on the SDK interface. Only the executor mutates it, through the concrete CallStackFrame (RD-VBAL §3.5.5 Placement and Licensing).

For every InstructionKind other than Simple, the control effect is already pre-resolved on the Instruction itself. The loop decides whether to branch without any statement semantics needing to know about the program counter; a statement's own runtime semantics evaluate operands and return a result, and never mutate control state (RD-VBAL §3.5.2 Instruction).

Whether to refuse to run a body that lowered with errors is a decision for the executor, not for lowering (RD-VBAL §3.5.3 Lowering Block Statements).

Dispatch by instruction kind

InstructionKind Executor action Semantics
Simple Dispatches the statement by its own type (see Simple). The statement's own page (see Simple).
Jump Branches unconditionally to Target. RD-VBAL §5.4.2.12 GoTo Statement
ExitLoop Branches to Target, in the same way as Jump: the executor uses the same case arm for both. RD-VBAL §5.4.2.5 Exit For Statement, RD-VBAL §5.4.2.7 Exit Do Statement
JumpTable Evaluates the selector through RDCore.Runtime.Execution.JumpTableEvaluator, then branches to one of Targets or falls through. RD-VBAL §5.4.2.13 On...GoTo Statement
GoSubTable As JumpTable, plus a push onto the GoSub Resumption List on a successful branch. RD-VBAL §5.4.2.16 On...GoSub Statement
GoSub Pushes onto the GoSub Resumption List, then branches to Target. RD-VBAL §5.4.2.14 GoSub Statement
Return Pops the GoSub Resumption List and branches there. RD-VBAL §5.4.2.15 Return Statement
ConditionalBranch An If/ElseIf header, a single-line If, or a pre-test loop header: evaluates a Boolean condition (see ConditionalBranch). A Case header (Instruction.Matching set instead of a Boolean condition): matches against the selector that the enclosing Select stored as block state, through RDCore.Runtime.Execution.CaseMatchEvaluator. Falls through on true or match; otherwise branches to Else. RD-VBAL §5.4.2.8 If Statement, RD-VBAL §5.4.2.9 Single-line If Statement, RD-VBAL §5.4.2.2 While Statement, RD-VBAL §5.4.2.6 Do Statement, RD-VBAL §5.4.2.10 Select Case Statement
LoopBack Evaluates the condition; branches back to Target when the loop continues, and falls through when it ends. RD-VBAL §5.4.2.6 Do Statement
ForOpener, ForNext Opens and advances a For loop, through the For loop state. RD-VBAL §5.4.2.3 For Statement
ForEachOpener, ForEachNext Opens and advances a For Each loop, through the For Each state. RD-VBAL §5.4.2.4 For Each Statement
With Stores the With target as block state, then falls through into the body. RD-VBAL §5.4.2.21 With Statement
Select Stores the selector as block state, then falls through. RD-VBAL §5.4.2.10 Select Case Statement
OnErrorGoTo, OnErrorDisable, OnErrorResumeNext Sets the activation's error-handler state. RD-VBAL §5.4.4.1 On Error Statement
ResumeCurrentStatement, ResumeNext, ResumeLabel Resumes from the activation's active error. RD-VBAL §5.4.4.2 Resume Statement
RaiseError Raises a run-time error. RD-VBAL §5.4.4.3 Error Statement
ExitProcedure Completes the activation. RD-VBAL §5.4.2.17 Exit Sub Statement, RD-VBAL §5.4.2.18 Exit Function Statement, RD-VBAL §5.4.2.19 Exit Property Statement
Halt Stops the run (End). RD-VBAL §5.4.2.22 End Statement
Break Stops the run (Stop). RD-VBAL §5.4.2.11 Stop Statement

Simple

A Simple instruction's statement is dispatched by its own C# type through RDCore.Runtime.Semantics.Statements.IStatementRuntimeSemanticsProvider. This is the statement analogue of RuntimeExpressionEvaluator's expression dispatch. The executor dispatches Let-assignment and Set-assignment statements, among others, to their statement runtime semantics:

Statement Node Semantics
Let-assignment AssignmentStatementNode (implicit or explicit Let) RD-VBAL §5.4.3.8 Let Statement
Set-assignment AssignmentStatementNode (Set) RD-VBAL §5.4.3.9 Set Statement
LSet, RSet AssignmentStatementNode (LSet, RSet) RD-VBAL §5.4.3.6 LSet Statement, RD-VBAL §5.4.3.7 RSet Statement
Mid, Mid$, MidB, MidB$ MidStatementNode RD-VBAL §5.4.3.5 Mid/MidB/Mid$/MidB$ Statement
Call, bare call CallStatementNode RD-VBAL §5.4.2.1 Call Statement
Debug.Assert DebugAssertStatementNode RD-VBAL §5.4.2.23 Assert Statement
Debug.Print DebugPrintStatementNode —
Open OpenStatementNode RD-VBAL §5.4.5.1 Open Statement
Close, Reset KeywordStatementNode RD-VBAL §5.4.5.2 Close and Reset Statements
Seek KeywordStatementNode RD-VBAL §5.4.5.3 Seek Statement
Lock, Unlock FileLockStatementNode RD-VBAL §5.4.5.4 Lock Statement, RD-VBAL §5.4.5.5 Unlock Statement
Line Input # KeywordStatementNode RD-VBAL §5.4.5.6 Line Input Statement
Width # KeywordStatementNode RD-VBAL §5.4.5.7 Width Statement
Print #, Write # PrintStatementNode RD-VBAL §5.4.5.8 Print Statement, RD-VBAL §5.4.5.9 Write Statement
Input # KeywordStatementNode RD-VBAL §5.4.5.10 Input Statement
Put KeywordStatementNode RD-VBAL §5.4.5.11 Put Statement
Get KeywordStatementNode RD-VBAL §5.4.5.12 Get Statement
Name KeywordStatementNode RD-VBAL §5.4.5.13 Name Statement

A statement that the statement provider does not recognize reports InternalError, and the run stops.

InternalError is never reported for a language-level condition that a well-formed program meets; such a condition raises a run-time error (RD-VBAL §2.6.3 Runtime Errors).

ConditionalBranch

A ConditionalBranch instruction's condition is forced to Boolean by RDCore.Runtime.Execution.ConditionEvaluator (MS-VBAL §5.5.1.2.2 Let-coercion to and from Boolean; RD-VBAL §5.5.1.2 Runtime semantics).

ConditionEvaluator calls VBBooleanLetCoercionRuntimeSemantics directly, rather than through an operator node. A condition has no operator of its own in source: If x Then let-coerces x without any (...).

A plain condition's truth test never writes parentheses around the coerced expression, so it does not use the "__c()_op" explicit let-coercion operator (RD-VBAL §3.3.1 Unary Operators). The coercion bypasses the operator pipeline entirely: the condition passes its own expression through to the Boolean let-coercion strategy, with no synthetic node standing in for an operator that is not present in source.

JumpTableEvaluator coerces the On…GoTo/On…GoSub selector in the same way: it calls the let-coercion strategy directly, rather than through the provider.

Per-activation state

The executor keeps the following state on each activation. Each item is read through the SDK interface ICallStackFrame and written only by the executor, through CallStackFrame.

State Holds Keyed by Read (ICallStackFrame) Written (CallStackFrame) Used by
Program counter The offset of the instruction to fetch. — (one value per activation) Pc Pc Every instruction.
Block state A single value: the Select selector, or the With target. The opener's offset. TryGetBlockState SetBlockState Select, With, Case headers, with-expressions.
For loop state A ForLoopState: counter symbol, counter expression, end and step. The ForOpener's offset. TryGetForLoopState SetForLoopState ForOpener, ForNext
For Each state A ForEachState, an enumeration cursor: control symbol/expression, the array, and a flat index. The ForEachOpener's offset. TryGetForEachState SetForEachState ForEachOpener, ForEachNext
GoSub Resumption List A LIFO stack of return offsets. — (a plain stack) GoSubDepth (a count) PushGoSubReturn, TryPopGoSubReturn GoSub, GoSubTable, Return
Error handler An ErrorHandlerState: the error-handling mode, the handler target, the active error, and the fault-statement offset. — (one value per activation) ErrorHandler ErrorHandler On Error, Resume, error interception.
Function result The function result variable, a single slot. — (one value per activation) ReturnValue ReturnValue RD-VBAL §5.3.1.6 Subroutine and Function Declarations

Block state

TryGetBlockState holds a single hidden value per block-opening instruction, keyed by that instruction's offset. A single value is enough for With's target and Select Case's selector, which are held through ICallStackFrame.TryGetBlockState / CallStackFrame.SetBlockState.

Neither Select nor With has a separate closer instruction to remove the stored state on exit.

Loop state

A For loop's state is more than the single value that TryGetBlockState holds: it is a counter symbol, a counter expression, an end and a step. ForLoopState is therefore a second, richer hidden-state mechanism alongside TryGetBlockState, with its own SDK type, RDCore.SDK.Runtime.Shared.ForLoopState.

A For Each loop's state is an enumeration cursor (control symbol/expression, the array, a flat index), with its own type, ForEachState.

For and For Each state each get their own parallel TryGetXState/SetXState pair, rather than extending TryGetBlockState's single-value shape to hold all three.

GoSub Resumption List

The GoSub Resumption List is a per-activation LIFO stack of return offsets, manipulated through CallStackFrame.PushGoSubReturn and CallStackFrame.TryPopGoSubReturn (RD-VBAL §5.4.2.14 GoSub Statement, RD-VBAL §5.4.2.15 Return Statement). It is a further per-activation mechanism alongside the block state (TryGetBlockState), the For loop state (TryGetForLoopState) and the For Each state (TryGetForEachState).

Unlike those three, which hold a single value per offset in a per-offset dictionary, the GoSub Resumption List is a plain stack. Nothing about which GoSub pushed an entry matters to Return; only the order of entries matters.

ICallStackFrame exposes only GoSubDepth, a count of the list's entries, and not the push/pop mutators.

Error handler

activation.ErrorHandler, of type ErrorHandlerState, is a single mutable value per activation, like Pc. It holds the error-handling mode, the handler target, the active error, and the fault-statement offset.

The error-handler state is not per-offset hidden state the way With, Select, For and For Each state is. An On Error statement changes the activation's error-handling policy going forward; it is not scoped to one block (RD-VBAL §5.4.4.1 On Error Statement).

Evaluation context

Every Simple and ConditionalBranch instruction's RuntimeEvaluationContext is computed anew before dispatch, from Instruction.EnclosingWith. EnclosingWith is a purely lexical fact about the instruction, not state carried over from whichever With last ran (RD-VBAL §3.5.3 Lowering Block Statements).

With-expression target resolution therefore works however control reached the instruction, including via GoTo (RD-VBAL §5.6.15 With Expressions).

RuntimeProcedureInvoker sets RuntimeEvaluationContext.Scope to the invoked procedure's own Uri for the whole activation (RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing).

Error interception

On Error and Resume (MS-VBAL §5.4.4 Error Handling Statements; RD-VBAL §5.4.4 Error Handling Statements) work by interception, not by dedicated per-error branch logic:

  1. Every executor dispatch that can yield an Error outcome routes it through ProcedureExecutor.InterceptError, before the loop decides whether to stop.
  2. InterceptError is the one place every run-time error passes through, and the point at which every run-time error reaches the session's error state. The line number Erl reports and the error's stack trace are captured there (RD-VBAL §6.1.2.7 Information, RD-VBAL §6.1.3.2 Err Class).
  3. When the activation's error handler catches the error, execution continues where the handler says (RD-VBAL §5.4.4.1 On Error Statement). Otherwise the loop stops.

Because every error passes through this point, every runtime error the executor can raise (TypeMismatch, SubscriptOutOfRange, ForLoopNotInitialized, or any other, from any subsystem) is catchable by an error handler, with no change needed at any individual error-raising site.

An error that propagates out of an activation (no handler caught it) is returned as the ProcedureExecutor.Run call's own return value. In a called procedure, that value reaches the caller as the result of the call, and propagates in the caller in the same way as any other runtime error (RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing).

Procedure invocation

A called procedure runs through the same ProcedureExecutor, in a new activation. The mechanics are described on the pages below.

Topic See
Call and bare-call statements RD-VBAL §5.4.2.1 Call Statement
Invoking a procedure: the callee's frame, ByVal/ByRef parameter binding (CallStackFrame.PushByRef; CallStackFrame.ReleaseAll never deallocates the address of a ByRef alias), named and Optional arguments, ParamArray, the call-depth limit RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing
The function result variable (ICallStackFrame.ReturnValue) RD-VBAL §5.3.1.6 Subroutine and Function Declarations
Static procedures RD-VBAL §5.3.1.2 Static Procedures
Hoisted Dim and Static locals RD-VBAL §5.4.3.1 Local Variable Declarations
Local Const RD-VBAL §5.4.3.2 Local Constant Declarations
A bare name or an index expression that invokes a procedure RD-VBAL §5.6.10 Simple Name Expressions, RD-VBAL §5.6.13 Index Expressions
Wiring RuntimeExpressionEvaluator.ProcedureInvoker RD-VBAL §2.3.1 Composition Root
Zero-size storage RD-VBAL §2.3.1.2 Session Services
Array and Variant values round-tripping through storage RD-VBAL §2.5.2.1.2 Array Values, RD-VBAL §2.5.2.1.5 Variant Values

⏮️ RD-VBAL §3.5.3 Lowering Block Statements | ⏭️ RD-VBAL §3.5.5 Placement and Licensing