2.3.1.2 Session Services
An execution session's services are rooted at an IRuntimeSession.
IRuntimeSession
IRuntimeSession exposes:
| Exposes | Description |
|---|---|
Environment.Is64Bit |
The environment bitness, on the session's IRuntimeEnvironmentProfile. It also sets the default value of the #If Win64 pre-compiler constant. #If VBA7 does not depend on it: the environment host defaults VBA7 to true in either bitness. A .rdproj #Const or a --define argument overrides these defaults. |
Environment.Culture |
The culture of the environment, which its Lcid selects; 0 is the invariant culture. Text compares in it (Option Compare Text, Like), and Format writes in it given a format. |
Environment.Regional |
The regional settings of the environment: a number or a date let-coerced to String is written in them, and a String let-coerced to a number or a date is read in them (RD-VBAL §5.5.1.2.4 Let-coercion to and from String). en-US unless the platform says otherwise. |
Environment.AnsiEncoding |
The Encoding of the environment's AnsiCodePage: what a character is as one or two bytes outside a String's own data, which the session's files are written and read in. Let-coercion between String and Byte() takes no code page (RD-VBAL §5.5.1.2.6 Let-coercion to and from resizable Byte()). |
References |
The workspace's project and library references (References, below). |
| The three session services | ISessionMemoryAllocator, ISessionSymbols and ISessionObjects (Services, below). |
Services
| Service | Responsibility | Members |
|---|---|---|
| ISessionMemoryAllocator | Allocates and frees blocks in the session's memory space, and reports allocation / fragmentation statistics. | TryAllocate, TryDeallocate, Info |
| ISessionSymbols | The session's symbol table. | TryDefine defines a Symbol in a scope. TryResolveValue resolves a name visible from a scope; TryResolveType is its type binding context counterpart. Resolver is the session's symbol resolver. |
| ISessionObjects | Object lifetime. | CreateObject; AddRef / RemoveRef (reference counting); TryRemoveObject removes an instance whose reference count has reached zero. |
ISessionMemoryAllocator is an accounting layer: it tracks sizes and addresses, in the manner of MSVBVM. It does
not hold the values themselves.
ISessionSymbols mirrors the ISymbolResolver.ResolveValue / ResolveType pair as TryResolveValue and
TryResolveType. ISessionSymbols.Resolver is a ScopeTreeSymbolResolver over the session's own symbols, rebuilt
as symbols are defined. See RD-VBAL §2.3.1.3 Name Resolution.
For object values and their lifetime, see RD-VBAL §2.5.2.1.4 Object Values.
References
IRuntimeSession.References is the workspace's project and library references, as an ordered
IReadOnlyList<ReferencePriorityInfo>. It is the runtime-facing view of the .rdproj RDCoreReference list
(RD-VBAL §2.2.3.2 RDCoreReference).
Each ReferencePriorityInfo entry carries only:
| Member | Description |
|---|---|
Name |
The reference's source-visible name. |
Priority |
The reference's rank in the IRuntimeSession.References list. |
The list is the reference priority order defined for global-scope name resolution (RD-VBAL §2.3.1.3 Name Resolution). It keeps the order in which the language server provides the references.
A referenced library's own members are not taken from this list: they are contributed by an
ISymbolProvider (see below)
and resolved through ISymbolResolver.
Name resolution across referenced projects and libraries shall consult the ordering to disambiguate a global-scope name. The name-resolution algorithm itself is a separate concern from the list (RD-VBAL §2.3.1.3 Name Resolution).
Name resolution takes that order from the libraries the project loads, not from this list: each symbol of a
referenced library carries the rank of its library (SymbolProperties.LibraryPriority, from
ReferencedLibraries.Priorities, which follows the order of the references), and ScopeTreeSymbolResolver ranks the
candidates of one tier of the lookup order by it, so a name that two libraries declare means the symbol of the one
referenced later (Reference priority). The symbols of two
libraries never share a rank. Only symbols of one tier that do, such as the members of two procedural modules of the
workspace that both declare the name, are reported as VBC09301 Ambiguous name (see
Duplicate and ambiguous names).
ISymbolProvider
ISymbolProvider exposes a single ProvideSymbols method, which yields the Symbols its source defines. The
composition root (RD-VBAL §2.3.1 Composition Root) then defines each provided Symbol:
| Context | Each provided Symbol is defined into |
|---|---|
| Static | The semantic layer. |
| Runtime | The session symbol table, through ISessionSymbols.TryDefine. |
ISymbolProvider is the abstraction behind the several symbol providers a session is composed from:
| Symbol provider source |
|---|
| Configuration flags. |
| AST declarations. |
| Reflected referenced libraries. |
| The environment host's own runtime and standard library (RD-VBAL §6.0 Standard Library). |
Heaps
The session's ISessionSymbols and ISessionObjects implementations should maintain the following internal
structures:
| Internal structure | Holds |
|---|---|
| Global heap | An IBindingHandle for any globally-scoped Symbol. |
| Workspace heap | An IBindingHandle for any workspace-scoped Symbol. |
| Static locals heap | An IBindingHandle for any module-scoped Symbol. |
| Object heap | The Symbol references and their associated bindings for any VBObjectValue. |
| Symbol table | A map of a Uri to its associated Symbol. |
| Name table | The current representation (casing) of all loaded symbols. |
A Symbol's scope kind determines how, and whether, the symbol is allocated in memory: a
ScopeKind.Global symbol lives in the globals heap, a ScopeKind.Module symbol in the workspace statics heap, and
a ScopeKind.Instance symbol in the object heap. A Static local's storage lives in the same heap tier a module
field uses; one of a member of a class lives in the object heap, with the fields of the object the member is called on
(RD-VBAL §5.4.3.1 Local Variable Declarations).
See RD-VBAL §2.5.1 Runtime Entities for ScopeKind and the allocation scopes.
What is at an address in these heaps, and what its bytes are, is the memory model below.
The order in which names are looked up in these heaps is specified in RD-VBAL §2.3.1.3 Name Resolution.
Memory addressing
ISessionMemoryAllocator reserves address space, never values: blocks carved from segments of 4 KiB (2 KiB in a 32-bit
environment) that begin at 0x10000, a block that is freed being reused for a later one that fits. The width of a pointer
is the environment's (IRuntimeEnvironmentProfile.Is64Bit): 8 bytes, or 4.
The memory map (MemoryAddress → IBindingHandle) is held by the session storage (SessionStorage, below):
it binds the IBindingHandle of a value to the
MemoryAddress reserved for it.
Note
Not implemented. The raw address map (MemoryAddress → Uri) is not implemented: what is at an address is
known by what is bound there, not by the name of a symbol.
Session storage
A value is not stored as bytes: what is bound at an address is its cell - an IBindingHandle - or a block the session
lays out for it (a BSTR, an interface, a SAFEARRAY), and the bytes at the address are what the value is in
memory, laid out as MS-VBA lays it out when they are read (PEEK) and read back into the value when one of them is
written (POKE). See the memory model.
A symbol's storage allocation is performed by SymbolAddressTable, which
ICallStackFrame and the module/global resolver
share (RD-VBAL §2.5.2.1.2 Array Values).
SymbolAddressTable.FreshBinding re-boxes a Variant's wrapped value into a fresh
VBRuntimeVariantValue on every store
(RD-VBAL §2.5.2.1.5 Variant Values).
Zero-size storage
ISessionMemoryAllocator refuses a zero-size allocation, as an enforced invariant: a 0-byte
bump-pointer/free-list allocation would hand the same address to the next caller.
For a non-positive size, SessionStorage.TryAllocate asks for a pointer's size instead - a VARIANT's, for a Variant - : a variable has an address
whatever it holds, which a program can take (VarPtr), and what holds nothing is a pointer in MS-VBA - a String or
a dynamic array whose pointer is 0. The following values have a non-positive storage size, and all of them take this
path:
| Value with a non-positive storage size |
|---|
vbNullString |
Nothing |
Null |
Empty |
| An uninitialized array |
An empty ParamArray array |
Zero-size storage is supported because a ParamArray call with nothing left over is the most common
ParamArray call shape: Call Callee(100) against a ParamArray rest() parameter with no arguments left over
collects an empty array whose Size is 0
(RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing).
Memory model
The session's memory is one address space, the one VarPtr, StrPtr, ObjPtr and AddressOf give addresses in, and
PEEK and POKE read and write. What is in it is laid out as MS-VBA lays it out (recorded in Excel), so that what a
program learns of it - an address, what is at an address - is what it would learn in MS-VBA, whatever the engine that
runs it.
What has an address
| What | Its address |
|---|---|
A variable: a module's, a Static local, a field of an object in its own code |
Its own, given when it is defined, the same for as long as it exists. |
A local, a parameter ByVal |
Its own, given the first time it is asked for: for the whole activation, whatever is assigned to it. |
A parameter ByRef |
The address of the variable it was given. |
| An element of an array | In the block the array's elements are laid out in, side by side, made the first time the address of one of them is asked for (VarPtr). |
| A field of a record | The record's address, plus the field's offset; a fixed-size array field's elements are in the record. |
The result variable of a Function or a Property Get, in its own code |
Its own, for the activation. |
What is no variable (a literal, an expression, a fixed-length String given to VarPtr) |
A temporary's, until the statement is over. |
The characters of a String |
In a BSTR (StrPtr). |
| An object | An interface of it, one for each it is asked for through (ObjPtr). |
| A dynamic array | Its SAFEARRAY (RD-VBAL §6.1.2.13). |
| A procedure of a standard module | Its own (AddressOf). |
What the bytes are
| A value of | Is, at its address |
|---|---|
A number, Boolean, Date, Currency |
Its bytes, little-endian: a Boolean two, -1 for True; a Date a Double; a Currency a 64-bit integer of ten-thousandths. |
A fixed-length String |
Its characters, two bytes each. |
A variable-length String |
The address of its characters (0 for vbNullString), which are in a BSTR: a Long of their length in bytes, the characters, a null character. |
| An object | The address of the interface its declared type says (0 for Nothing); the first pointer there is the class's. |
A Variant |
A VARIANT - 24 bytes, 16 in a 32-bit environment: its VarType in the first two, its data eight bytes in, a pointer for a String, an object, an array. |
| A dynamic array | The address of its SAFEARRAY (0 for one of no dimensions): its dimensions, features, element width, locks, the address of its elements, and each dimension's count and lower bound, the last first. |
| A record | Its fields, each at its offset - aligned as MS-VBA aligns it for the pointer width - each as its type is, and zeros between them. |
When what the session lays out is made and freed
| Block | Made | Freed |
|---|---|---|
| An array's elements | The first time the address of an element is asked for. | When the array is gone - ReDim'd, assigned another, a local of an activation that returned - the next time a block is made. |
A BSTR |
The first time the address of the characters is asked for, or the bytes of the String. |
When a String is assigned to what holds it, or what holds it is freed; not when a pointer POKEd over it lets go of it. |
| An interface | The first time its address is asked for. | When the object is destroyed. |
A SAFEARRAY |
The first time its address is asked for. | A dynamic array's when it holds no array (Erase), a Variant's when it holds another; not when a pointer POKEd over it lets go of it. |
| A procedure's address | The first time it is asked for. | Never: it lasts as long as the session. |
| A temporary | When it is passed. | When the statement is over. |
A block never takes the address of the one it replaces: a new BSTR, a new array block, is made before the old one is
freed, so that the two are never at one address (an array ReDim'd is elsewhere, as in MS-VBA). A block that is let
go of is freed the next time it is asked for, or when what holds it is freed - which is what a program can tell.
What a POKE writes
| A POKE into | Makes |
|---|---|
A number's, a Boolean's, a Date's, a fixed-length String's bytes |
The value its bytes say. |
A String's characters, a BSTR's length |
The String they say, at the same address, for everything that holds it. |
A String's, an object's, a dynamic array's pointer |
What it points at - another's BSTR, interface, SAFEARRAY - which the two then hold, without a reference of their own; 0 makes it vbNullString, Nothing, an array of no dimensions. A pointer is written a byte at a time, and one that points at nothing the session has leaves what it was. |
A VARIANT's type or data |
The value they say; its data, when it holds a String, an object or an array, is the pointer it is. |
A SAFEARRAY's lower bound, or count |
The array under those bounds, its elements where they are; never more elements than it has. |
An interface, a procedure's address, the rest of a SAFEARRAY |
Nothing: the POKE is refused. |
Implementation-defined
What MS-VBA leaves to the memory it runs in, and RD-VBA decides:
- The addresses are the session's. The address space begins at
0x10000, so that no address is0or a small number; where in it a block is depends on what was made and freed before it, as it does in MS-VBA, and a program that compares two addresses for anything but equality learns nothing it can rely on. - Native code does not share it. An address given to a
Declareis a number, not a pointer into memory the native code can read or write (🚧 *Ptr plan phase B: a mirror of the session's memory around a native call, and callbacks forAddressOf). - What MS-VBA leaves as it was is zeros. The bytes past what a block holds, an interface's table, a procedure's address, and the upper bytes of a
SinglepassedByValtoVarPtr(where MS-VBA leaves whatever the register held) read as zeros. - What would be memory that is not the session's is refused. A
POKEwhose result would point past a block - aBSTR's length, aSAFEARRAY's count - or into what only the session writes is refused, rather than leaving something that points nowhere. - A pointer
POKEd over what it pointed at leaks it, as it does in MS-VBA: theBSTRorSAFEARRAYstays where it was, and pointing at it again is the value again. A weak reference made so - an interface's addressPOKEd into an object variable - counts no reference, as in MS-VBA.
Call stack and file channels
The session's call stack, RuntimeCallStack, enforces a call-depth limit, in OnBeforeTryPush
(RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing).
Of the GoSub Resumption List, the SDK interface ICallStackFrame exposes only GoSubDepth, the number of its
entries; it does not expose the list's push/pop mutators (RD-VBAL §3.5.4 Execution).
All file statements run through one session-level shim, the IFileChannels / IFileChannel interfaces (RD-VBAL §5.4.5 File Statements).
Thread safety
The RDCore implementations (⚖️ GPLv3) of the session services are intended to be thread-safe.
RD-VBA normally executes on a single thread, but the RD-VBA runtime implementation is not inherently single-threaded. Whether an RD-VBA environment host supports the concurrent execution of RD-VBA execution threads is host-dependent.
⏮️ RD-VBAL §2.3.1.1 Execution Session | ⏭️ RD-VBAL §2.3.1.3 Name Resolution