# Run-time support

Some language features are implemented in the run-time library, including:

- exceptions and finalization
- coroutines
- memory management
- garbage collection
- postmortem history

XDS provides an integrated Modula-2 and Oberon-2 run-time library, taking into account the possibility that modules written in both languages are used in one project. As a rule, if you do not use a particular feature, the part of RTS that implements that feature will not be added to your executable program. For example, if your program is written entirely in Modula-2, the Oberon-2 part of RTS (garbage collector, meta-language facilities) will not included.

The integrated memory manager is described in Memory management. The section The oberonRTS module describes an interface to the Oberon-2 run-time support.

- Memory management
- Postmortem history
- The oberonRTS module

## Memory management

The XDS integrated memory manager implements

- default memory allocation and deallocation procedures for Modula-2 (See the option STORAGE);
- memory allocation procedures for Oberon-2;
- system memory allocation procedures for Oberon-2 (See NEW and DISPOSE);
- the garbage collector.

The compiler provides the option GCAUTO and the equation HEAPLIMIT to control the memory management. They should be set when the top-level module of the program is compiled We recommend to set them in the configuration file or a project file.. The compiler uses their values when generating the RTS initialization call.

The equation HEAPLIMIT specifies the maximum size of the heap in bytes. If that equation is set to zero, the run-time system automatically determines heap size at startup and dynamically adjusts it according to application's memory use and system load.

The option GCAUTO allows the garbage collector to be called implicitly. If the option is not set the garbage collector must be called explicitly (See The oberonRTS module). The garbage collector is called implicitly by the memory allocation procedure in the following cases:

- a memory block of the requested length cannot be allocated;
- the amount of busy memory exceeds the limit specified by the HEAPLIMIT equation (or the limit chosen by the run-ime system if HEAPLIMIT was set to zero during compilation);
- the amount of busy memory exceeds some limit set internally by the memory manager for optimum performance.

If the memory block still cannot be allocated after the call to the garbage collector, the exception `XEXCEPTIONS.noMemoryException` will be raised by the Oberon-2 memory allocation procedureIn Modula-2 it has to return `NIL` if failed to allocate a memory block..

Note: In a pure Modula-2 program, the garbage collector is never invoked, so you may set the HEAPLIMIT equation to a very large value.

## Postmortem history

If the option GENHISTORY was set ON when your program was compiled, the run-time system dumps a procedure call stack into a file called `errinfo.$$$`, which may then be read by the HIS utility to print each item with

- a file name
- a line number
- a program counter value
- a procedure name (sometimes)

Note: all modules constituting your program should be compiled with the option LINENO set ON.

To print the history, RTS scans the stack of the coroutine that caused an exception and tries to find procedure calls. This is not a trivial task because of the highly optimized code generated by the compiler. For example, not all procedures have a stack frame.

For each pointer to the code segment on the stack RTS checks the previous command. If this command is a call command, it assumes that this is a procedure call. It is unlikely that RTS misses a procedure call, but it can be cheated by something that looks like a procedure call. As a rule, it is caused by uninitialized local variables, especially character arrays.

The first line of the history is always correct. For each line, except the first one, we recommend to check that the procedure shown in the previous line is called from the given line.

From the other hand, if you turn the GENFRAME option on, the code will be a bit slower, but RTS will scan stack frames of the procedures and the history will show absolutely correct addresses and line numbers. Procedure names are almost always valid except the case of lack of debug information in some modules - probably compiled by foreign compilers or by XDS with not all debug flags set. So you should not rely on procedure names hard.

Turning the GENHISTORY option ON does not slow down your code, as it only adds an extra call to the initialization routine. It should be done when you compile the main module of your program, in its header, compiler command line, or project (we recommend the last approach).

The following example shows a sketch of a program and the procedure stack:

        PROCEDURE P1;
          (* uninitialized variable: *)
          VAR x: ARRAY [0..50] OF INTEGER;
        BEGIN
          i:=i DIV j;   (* line 50 *)
        END P1;

        PROCEDURE P2;
        BEGIN
          i:=i DIV j;   (* line 100 *)
        END P2;

        PROCEDURE P3;
        BEGIN
          P1;           (* line 150 *)
        END P3;

    #RTS: No exception handler #6: zero or negative divisor
    ------------------------------------------------------------
    Source file                        LINE  OFFSET  PROCEDURE
    ------------------------------------------------------------
    "test.mod"                           50 000000DE
    "test.mod"                          100 0000024C
    "test.mod"                          150 0000051D

It is obvious from the source text that the procedure `P1` cannot be called from `P2`. The second line is superfluous.

## The oberonRTS module

The run-time support (RTS) is an integral part of the Oberon-2 language implementation. It includes command activation, memory allocation, garbage collection and meta-language facilities. The module oberonRTS (written in Modula-2) provides an interface to these features.

- Types and variables
- Garbage collection
- Object finalization
- Meta-language facilities
- Module iterators

### Types and variables

        TYPE
          Module;  (* run-time data structure for a module *)
          Type;    (* run-time data structure for a data type *)
          Command = PROC; (* parameterless procedure *)
          CARDINAL = SYSTEM.CARD32;

        VAR
          nullModule: Module; (* Null value of type Module *)
          nullType: Type;     (* Null value of type Type *)

### Garbage collection

- Collect - Garbage Collector
- GetInfo - Get Memory Information

#### Collect - Garbage Collector

`PROCEDURE Collect;`

Invokes the garbage collector.

#### GetInfo - Get Memory Information

`PROCEDURE GetInfo(VAR objects, busymem: CARDINAL);`

Returns the number of allocated objects and the total size of the allocated memory.

### Object finalization

A system with garbage collection has some specific features. Its main difference from systems without garbage collection is that deallocation of any system resource must be postponed until garbage collection. For example, let some data structure contain descriptors of open files. To close a file (i.e. to destroy its descriptor), one needs to know that there are no references to that file. This information becomes known only in the course of garbage collection. The same argument also holds for other kinds of resources.

One immediate implication is that there must be some finalization mechanism: the ability to perform certain operations with an object when there are no more references to it.

XDS allows a finalization procedure to be attached to any dynamically allocated object.

- Finalizer - Type of a finalization procedure
- InstallFinalizer - Set a finalizer to an object

#### Finalizer - Type of a finalization procedure

`TYPE Finalizer = PROCEDURE (SYSTEM.ADDRESS);`

#### InstallFinalizer - Set a finalizer to an object

`PROCEDURE InstallFinalizer(f: Finalizer;`
`obj: SYSTEM.ADDRESS);`

The procedure sets the finalization procedure `f` for the object `obj`. That procedure will be called when the object becomes unreachable.

Note: a finalizer is called on the GC stack (stack size is limited).

##### Example

        TYPE
          Obj = POINTER TO ObjDesc;
          ObjDesc = RECORD
            file: File; (* file handler *)
          END;

        PROCEDURE Final(x: SYSTEM.ADDRESS);
          VAR o: Obj;
        BEGIN
          o:=SYSTEM.CAST(Obj,x);
          IF o.file # NIL THEN Close(file) END;
        END Final;

        PROCEDURE Create(): Obj;
          VAR o: Obj;
        BEGIN
          NEW(o);
          o.file:=NIL;
          oberonRTS.InstallFinalizer(Final,o);
          TryOpen(o.file);
        END Create;

### Meta-language facilities

The meta-programming operations can be used to retrieve the type of an object, to create an object of the given type, to get the name of a type and a type by its name, etc.

- Search - Search a Module by its Name
- NameOfModule - Name of Module
- ThisCommand - Get Command by its Name
- ThisType - Get Type by its Name
- SizeOf - Size of Type
- BaseOf - Base of Type
- LevelOf - Level of Type Extension
- ModuleOf - Module of Type
- NameOfType - Name of Type
- TypeOf - Type of Object
- NewObj - Create Object

#### Search - Search a Module by its Name

`PROCEDURE Search(name: ARRAY OF CHAR): Module;`

Returns a module by its `name` or `nullModule`.

#### NameOfModule - Name of Module

`PROCEDURE NameOfModule(m: Module;`
`VAR name: ARRAY OF CHAR);`

Returns the `name` of the `Module`.

#### ThisCommand - Get Command by its Name

        PROCEDURE ThisCommand(m: Module;
                           name: ARRAY OF CHAR;
                             ): Command;

Returns the command (parameterless procedure) named "`name`" in the module `m` or `NIL`, if the command does not exist.

#### ThisType - Get Type by its Name

        PROCEDURE ThisType(m: Module;
                        name: ARRAY OF CHAR): Type;

Returns the type named "`name`" declared in the module `m` or `nullType`, if there is no such type.

#### SizeOf - Size of Type

`PROCEDURE SizeOf(t: Type): INTEGER;`

Returns the size (in bytes) of an object of the type `t`.

#### BaseOf - Base of Type

`PROCEDURE BaseOf(t: Type; level: INTEGER): Type;`

Returns the level-th base type of `t`.

#### LevelOf - Level of Type Extension

`PROCEDURE LevelOf(t: Type): INTEGER;`

Returns a level of the type extension.

#### ModuleOf - Module of Type

`PROCEDURE ModuleOf(t: Type): Module;`

Returns the module in which the type `t` was declared.

#### NameOfType - Name of Type

`PROCEDURE NameOfType(t: Type; VAR name: ARRAY OF CHAR);`

Returns the `name` of the record type `t`.

#### TypeOf - Type of Object

`PROCEDURE TypeOf(obj: SYSTEM.ADDRESS): Type;`

Returns the type of the object `obj`.

#### NewObj - Create Object

`PROCEDURE NewObj(type: Type): SYSTEM.ADDRESS;`

Creates a new object of the type `type`.

### Module iterators

The module `oberonRTS` provides procedures which can be used to iterate all loaded modules, all commands, and all object types (i.e., exported record types).

- NameIterator - Iterator Type
- IterModules - Iterate all Modules
- IterCommands - Iterate Commands
- IterTypes - Iterate Record Types

#### NameIterator - Iterator Type

        TYPE
          NameIterator = PROCEDURE (
                           (*context:*) SYSTEM.ADDRESS,
                           (*name:*) ARRAY OF CHAR
                         ): BOOLEAN;

A procedure of type `NameIterator` is called by an iterator on each iterated item. An iterator passes the name of the item along with the so-called context word. This allows some context information to be passed to the user-defined procedure (e.g., a file handler). If the procedure returns FALSE, the iteration is terminated.

#### IterModules - Iterate all Modules

        PROCEDURE IterModules(context: SYSTEM.ADDRESS;
                                 iter: NameIterator);

The procedure iterates all Oberon-2 modules.

#### IterCommands - Iterate Commands

        PROCEDURE IterCommands(mod: Module;
                           context: SYSTEM.ADDRESS;
                              iter: NameIterator);

Iterates all commands implemented in the module `mod`.

#### IterTypes - Iterate Record Types

         PROCEDURE IterTypes(mod: Module;
                         context: SYSTEM.WORD;
                            iter: NameIterator);

Iterates all record types declared in the module `mod`.