INTERFACEDefines the abstract Juno machine and the Juno run-time. See theJunoRT ;
JunoByteCode
interface for a description of the instructions understood
by the Juno machine.
The Juno machine maintains an internal Boolean variable that is set by some instructions and tested by others. It also maintains a run-time stack, which contains both procedure frames and Juno values.
All procedures in this interface are un-monitored.
IMPORT JunoRTError, JunoValue; IMPORT Atom; TYPE ByteCode = BITS 8 FOR [0..255]; ByteStream = REF ARRAY OF ByteCode; ExternalCode = BRANDED "JunoRT.ExternalCode" OBJECT METHODS invoke(): BOOLEAN END;If
ec
is an ExternalCode
in the external code table, then ec.invoke()
is called whenever ec
is invoked by a JunoByteCode.CALLEXT
bytecode.
The machine's internal cond
state variable is set to the return value
produced by ec.invoke()
. The result should be TRUE
in the event of
success, and FALSE
in the event of a run-time error.
VAR value_tbl: REF ARRAY OF JunoValue.T; code_tbl: REF ARRAY OF ByteStream; ext_code_tbl: REF ARRAY OF ExternalCode;The Juno machine has three global tables: one for values, one for compiled Juno procedures, and one for ``external'' Modula-3 procedures. Bytecode instructions can read and write values in the table
value_tbl
, they can
call Code
's in the table code_tbl
, and they can call ExternalCode
's
(Modula-3 procedures) in the table ext_code_tbl
. The entry code_tbl[0]
is reserved by the run-time.
Use only the three procedures below to manipulate the value and code tables.
PROCEDURE GetValueIndex(val: JunoValue.T): CARDINAL;
Return the value index previously allocated toval
, or allocate and return a value indexi
forval
(initializingvalue_tbl[i]
toval
) if one has not been allocated for it previously. Indices returned by this procedure reference read-only values.
PROCEDURE GetVarIndex(md, nm: Atom.T): CARDINAL;
Return the value index previously allocated to the qualified ID md . nm
,
or allocate and return a value index for that QID if one has not been
allocated for it previously.
TYPE ProcAttr = RECORD modName, name: Atom.T; sig: Sig END; Sig = RECORD outs, inouts, ins: CARDINAL END;If
p: ProcAttr
, p.name
must be non-NIL, but p.modName
may be
NIL to indicate an anonymous module.
PROCEDURE GetCodeIndex(READONLY pa: ProcAttr): CARDINAL;
Return the code index previously allocated to the internal procedure with the name inpa
, or allocate and return an index for that procedure if one has not been allocated for it previously. Requires thatpa
be valid.
PROCEDURE GetExtCodeIndex(READONLY pa: ProcAttr): CARDINAL;
Return the code index previously allocated to the external procedure with the name inpa
, or allocate and return an index for that procedure if one has not been allocated for it previously. Requires thatpa
be valid.
PROCEDURE GetProcAttr(s: CARDINAL): ProcAttr;
Return the attributes of the internal procedure in slot s
.
PROCEDURE GetExtProcAttr(s: CARDINAL): ProcAttr;
Return the attributes of the external procedure in slot s
.
TYPE PC = RECORD proc: CARDINAL; offset: CARDINAL END;If
pc: PC
, then pc
represents the program counter at the ByteCode
code_tbl[pc.proc, pc.offset]
.
Frame <: FramePublic; FramePublic = OBJECT METHODS down(): Frame; (* was "prev" *) up(): Frame; (* was "next" *) getLocal(i: INTEGER): JunoValue.T; setLocal(i: INTEGER; v: JunoValue.T); pc(): PC; setPC(pc: PC); END;The Juno machine maintains a stack of frames. Each frame corresponds to a bytestream; no frames are created when procedures in the
ext_code_tbl
are
called. A frame has two important attributes: an array of variables, and a
current PC.
The stack grows from the initial base frame upwards. Hence, the frame for the currently running procedure is the topmost frame. In the machine's initial state, the base and topmost frames are the same.
The down
and up
methods report the adjoining Frame
's in the stack.
The base frame always has f.pc() = PC{0,0}
and f.down() = NIL
. The
topmost frame always has f.up() = NIL
.
Local arguments in a frame (both procedure arguments and temporaries) can
be accessed with the getLocal
and setLocal
methods. See the description
in the JunoByteCode
interface for how local variables are indexed. It is
an unchecked run-time error to supply an index which is outside the valid
range for the procedure to which the frame corresponds. The value returned
by getLocal
for unitialized variables may be (Modula-3) NIL
.
The pc
method reports the frame's program counter. The setPC
method
sets the frame's program counter.
Various procedures cause Frame
objects to become invalid. Once a frame
becomes invalid, invoking any operation on it is an unchecked run-time
error.
PROCEDURE BaseFrame(): Frame;
Returns the initial base frame.
PROCEDURE TopFrame(): Frame;
Returns the topmost frame.
PROCEDURE PushFrame(new_pc: PC; size: CARDINAL);
Creates a new framef
withsize
arguments such thatf.down()
is the current topmost frame, andf.pc() = pc
. Initially,f.getArgument(i) = NIL
for eachi
in the range[0..size-1]
. If necessary, this procedure automatically enlarges the stack to hold thesize
arguments and the new frame pointer. This procedure setspc
tonew_pc
.
PROCEDURE PopFrame();
Invalidates the current topmost frame; the frame below the the old topmost frame becomes the topmost frame. It is a checked run-time error to invoke this procedure when the machine is in the initial state.
PROCEDURE ResetMachine();
Resets the Juno machine to its initial state, in which the base and top frames are the same.
PROCEDURE Save();
Saves the current machine state, including the entire stack. Doesn't currently save the value table, though maybe it should.
PROCEDURE Restore();
Restores the state to the one that was last saved by a call to Save(). It is a checked run-time error if no state has been previously saved.
PROCEDURE GetStackSize(): CARDINAL;
Reports the current size of the run-time stack.
PROCEDURE EnlargeStack();
Doubles the size of the run-time stack, while preserving all aspects of the machine state.
TYPE TrapCode = {NormalHalt, BreakPoint, Error, Interrupt, StackOverflow};A
NormalHalt
trap occurs when a procedure returns into a frame f
such
that f.pc() = PC{0,0}
. An Error
trap occurs in the event of a run-time
error (see the JunoRTError
interface for a list of possible run-time
errors). An Interrupt
trap occurs when the computation is interrupted by
the user. A StackOverflow
trap occurs when the run-time stack is
overflowed; calling EnlargeStack
followed by Exec
will continue the
computation.
TYPE ExecRes = RECORD trapCode: TrapCode; trapLoc: PC; extSlot: CARDINAL; errorCode: JunoRTError.Code END;The procedures in the module for running the machine return a result of type
ExecRes
. If er: ExecRes
, then er.trapCode
is the TrapCode
indicating why the machine stopped. If trapCode # NormalHalt
, then
er.trapLoc
is the program counter of the instruction causing a run-time
error if trapCode = Error
, or the location of the most recently executed
instruction otherwise. The field er.extSlot
is the slot number of the most
recently executed external procedure; it is only defined in the event that
the machine halted due to the failure of an external procedure. If
er.trapCode = TrapCode.Error
, er.errorCode
is the error code value.
PROCEDURE TrapMessage(READONLY execRes: ExecRes): TEXT;
Return a descriptive error message for the execution result execRes
.
PROCEDURE Interrupt();
Halt the currently running program at a convenient location with a
TrapCode.Interrupt
, and then return. This is a no-op if the machine
is not currently running.
PROCEDURE Exec(): ExecRes;
Executes any code at the currentpc
of the bottom stack frame until a trap is encountered, and returns the execution result. All existingFrame
objects become invalid once this procedure is invoked.
PROCEDURE ExecFromSlot(slot: CARDINAL; reset := TRUE): ExecRes;
Execute the code in the givencode_tbl
slot. Ifreset = TRUE
, then callResetMachine
before executing the specified code.
END JunoRT.