2.3 Application Host
Note
This specification may be incomplete at this time.
🎯 The RDCore platform shall provide with the rdc.exe CLI client, the ability to compose, host, analyze, run, and debug any RD-VBA application.
2.3.1 Composition Root
The environment host is responsible for composing a RD-VBA from its references, instructions, and symbols; configuring the host VBA environment implicit storage, and loading any application settings and additional workspace resources into the runtime environment.
It then proceeds to resolve an entry point, initiates an execution session, construct and push a ICallStackFrame to the evaluation engine that then proceeds to sequentially evaluate each instruction in the frame.
2.3.1.1 Execution Session
An execution session holds the state of the execution engine and exposes methods that advance execution steps:
| Member | Description |
|---|---|
State |
Describes the current mode of the session |
Frame |
Exposes the current stack frame |
GetCurrentStack |
Exposes the current call stack |
StepInto |
Advances execution by a single step |
StepOver |
Advances execution into the next statement |
StepOut |
Advances execution to the next statement in the current scope, stepping over any statements in-between |
2.3.1.2 Session Services
An execution session is rooted at an IRuntimeSession that exposes the environment bitness
(Is64Bit, which also determines the value of the #If Win64 and #If VBA7 pre-compiler directives),
the workspace's References (see below), and three services:
| Service | Responsibility |
|---|---|
ISessionMemoryAllocator |
Allocates and frees blocks in the session's memory space and reports allocation / fragmentation statistics (TryAllocate, TryDeallocate, Info). This is an accounting layer — it tracks sizes and addresses, MSVBVM-style, not the values themselves. |
ISessionSymbols |
The session's symbol table: TryDefine a Symbol in a scope, and TryResolve a name visible from a scope. |
ISessionObjects |
Object lifetime: CreateObject, AddRef / RemoveRef, and TryRemoveObject for an instance whose reference count has reached zero. |
IRuntimeSession.References is the workspace's project and library references as an ordered
IReadOnlyList<ReferencePriorityInfo> — the runtime-facing view of the .rdproj
RDCoreReference list, carrying only each
reference's source-visible Name and its Priority (the list rank). It is the reference-priority
order defined later in this section, preserved exactly as the language server provides it; a
referenced library's own members are contributed by an ISymbolProvider and resolved through
ISymbolResolver, not from this list. Name resolution across referenced projects and libraries
consults the ordering to disambiguate a global-scope name; the resolution algorithm itself is a
separate concern.
The read face used by the static and runtime semantic layers is ISymbolResolver:
| Member | Description |
|---|---|
Resolve |
Resolves a specified identifier name, as seen from the scope the symbol at a specified handle Uri belongs to, to a SymbolResolutionResult |
GetValue |
Gets the IBindingHandle currently bound to a specified Symbol |
TryRead |
Gets the IBindingHandle held at a specified MemoryAddress, if any |
Resolve returns a SymbolResolutionResult — the bound Symbol, an unbound result (the name is
declared nowhere visible), or one of two compile-time errors with the colliding declarations
attached: VBC09303 Duplicate declaration when the name is declared more than once within one
module or procedure, and VBC09301 Ambiguous name when it resolves in more than one enclosing
scope — members promoted from different modules or references — and the reference must qualify it.
The resolver reports the error kind; the caller, which knows where the reference is, builds the
located diagnostic.
The compile-time implementation is
ScopeTreeSymbolResolver: it walks
the ScopeTree described below and binds names only —
its GetValue / TryRead throw, since it holds no run-time bindings. The session exposes one over
its own symbols as ISessionSymbols.Resolver, rebuilt as symbols are defined. A design-time host
composes its own the same way: a first pass extracts every parsed module's declarations with an
intrinsic-only resolver, then a
CompositeSymbolResolver layers a
ScopeTreeSymbolResolver over the lot in front of the intrinsic one — so a module's As SomeType
binds to a sibling module's Type or Enum, not only to a reserved data-type name.
ISymbolProvider exposes a single ProvideSymbols method that yields the Symbols its source
defines; the composition root then defines each one into the semantic layer (static context) or the
session symbol table (runtime context, through ISessionSymbols.TryDefine). It is the abstraction
behind the several symbol providers a session is composed from — configuration flags, AST
declarations, reflected referenced libraries, and the environment host's own runtime and standard
library.
Note
Where a Symbol's bound value lives is being moved to an addressable session storage — a
contiguous byte block per allocation — so that array iteration and copy operations can run
directly against the underlying storage without materializing a VBTypedValue per element. Until
then, an IBindingHandle is itself the value.
Note
The RDCore implementations (⚖️GPLv3) of these services are intended to be thread-safe. While RD-VBA normally executes on a single thread, its runtime implementation is not inherently single-threaded and it is host-dependent whether a RD-VBA environment host supports the concurrent execution of RD-VBA execution threads. This concurrent execution capability is intended to be (optionally) used for eventual unit testing features.
The session's ISessionSymbols and ISessionObjects implementations should:
- Maintain an internal global heap to hold an
IBindingHandlefor any givenSymbolthat is globally-scoped; - Maintain an internal workspace heap to hold an
IBindingHandlefor any givenSymbolthat is workspace-scoped; - Maintain an internal static locals heap to hold an
IBindingHandlefor any givenSymbolthat is module-scoped; - Maintain an internal object heap to hold the
Symbolreferences and their respective associated bindings for any givenVBObjectValue; - Maintain an internal symbol table mapping a
Urito its associatedSymbol; - Maintain an internal name table holding the current representation (casing) of all loaded symbols.
The ISessionMemoryAllocator maintains an internal address pointer tracking the current memory offset. The memory map (MemoryAddress → IBindingHandle) and raw address map (MemoryAddress → Uri) are part of the forthcoming addressable session storage.
ScopeKind defines the allocation scopes.
The current memory address pointer should be incremented by a host-defined IntPtrSize that represents the size of a pointer in the current environment (32 or 64 bits).
The correctly-scoped allocation of all symbols upon their definition should then suffice to make symbol resolution automatically follow the MS-VBAL specification with regards to the order in which an identifier name is resolved, provided that lookups are done in the specified order:
- If a name refers to a symbol defined on the local stack frame, then the resolved symbol is locally scoped;
- If a name refers to a symbol defined in the static locals heap, then the resolved symbol is locally scoped but preserves its value between calls;
- If a name refers to a symbol defined in the workspace heap, then the resolved symbol is workspace-scoped;
- If a name refers to a symbol defined in the global heap, then the resolved symbol is globally-scoped.
- If multiple symbols match a specified name within one module or procedure scope, that is a VBC09303 Duplicate declaration; if they match across the project or global scope — members promoted from different modules or references — that is a VBC09301 Ambiguous name (the reference must qualify the name). An appropriate compile-time error should be issued in either case.
- If multiple symbols match a specified name within the global scope, then the name is disambiguated using the reference priority order of the referenced library a matching symbol is defined in. This priotity is determined by the order in which project references appear in the
.rdprojfile of a workspace folder.
Note
The VBA standard library always has the lowest priority (i.e. always appears first), meaning any other project reference that defines any identically-named class type or public/global member is always going to shadow the VBA library definitions; this shadowing should be detected in the semantic layer and reported through semantic flags so RDCore.Diagnostics can issue shadowed declaration diagnostics (see §2.6 Diagnostics).
The mechanism behind that ordered lookup is a
ScopeTree: a
ScopeTreeBuilder folds the composed symbols
into a tree of LexicalScopes, one per
LexicalScopeKind — the global scope at the
root, the project scope beneath it, one scope per module, and one per procedure body. Each symbol is
placed structurally from its ParentUri, concrete type, and access modifier: a standard module's
non-Private members (an explicit Public / Global / Friend, or an implicit procedure-like
member — MS-VBAL §5.2.3) are also declared in the project scope, so a sibling module resolves
them without qualification. Resolving a name from a scope walks SelfAndAncestors() outward: the
first scope that declares the name binds it; a name declared more than once in a single scope is the
ambiguous case above.
A module's LexicalScope also carries its ModuleDirectives
— today, whether it declares Option Explicit — reachable from any scope nested under it via
EnclosingModuleDirectives(). The static-semantics layer consumes this to decide whether an
unresolved simple name (below) is a deferred VBUnknownType or a VBC09302 Variable not
defined compile-time error; see §5.0.1.1 for how a SimpleNameExpression's declared type is
determined from a Resolve outcome.
Note
Still to come: ordering referenced projects and libraries by their .rdproj reference priority
within the global scope (the ordering is already carried on IRuntimeSession.References; nothing
consults it yet). Reporting an ambiguous or duplicate name as a coded compile-time error is done —
SimpleNameExpressionStaticSemantics (§5.0.1.1) is the first static-semantics rule to consume
Resolve's error outcomes.
2.3.2 Mode / State
Provided that the host application is able to respond to keyboard inputs, execution in running mode may be suspended at any point to enter break mode through what has traditionally been a Ctrl+Pause|Break keyboard shortcut in the Microsoft Visual Basic Editor, however the platform considers this an implementation detail of the environment host, that may offer the same functionality through different, implementation-defined means that may or may not be equivalent.
At any point in time, a VBA host environment may be in either one of the following modes / states:
Design: a static context exists and is actively being synchronized with the workspace source code being edited;Run: the host environment is actively executing instructions uninterrupted;Break: execution is halted at the current instruction either through a manual break, a semantic break (e.g. a failedAssertcall, or aStopkeyword was encountered), or an unhandled run-time error has occurred and instructions can be manually stepped over/into, or rewinded;
👉 The exact behavior of the host environment on error is implementation-defined: depending on the workspace application configuration, a failing workspace application may terminate the host process with an error code, or enter break mode and offer to debug at that location.
⏮️ RD-VBAL §2.2 RDPROJ Structure | ⏭️ RD-VBAL §2.4 Static Types