(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* *) (* Last modified on Mon Oct 31 09:34:14 PST 1994 by heydon *) (* modified on Sun Jun 5 13:04:30 1994 by gnelson *) (* modified on Sat Aug 22 22:10:45 PDT 1992 by myers *) INTERFACE JunoRT; (* Defines the abstract Juno machine and the Juno run-time. See the "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 to "val", or allocate and return a value index "i" for "val" (initializing "value_tbl[i]" to "val") 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 in "pa", or allocate and return an index for that procedure if one has not been allocated for it previously. Requires that "pa" be valid. *) PROCEDURE GetExtCodeIndex(READONLY pa: ProcAttr): CARDINAL; (* Return the code index previously allocated to the external procedure with the name in "pa", or allocate and return an index for that procedure if one has not been allocated for it previously. Requires that "pa" 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 frame "f" with "size" arguments such that "f.down()" is the current topmost frame, and "f.pc() = pc". Initially, "f.getArgument(i) = NIL" for each "i" in the range "[0..size-1]". If necessary, this procedure automatically enlarges the stack to hold the "size" arguments and the new frame pointer. This procedure sets "pc" to "new_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 current "pc" of the bottom stack frame until a trap is encountered, and returns the execution result. All existing "Frame" objects become invalid once this procedure is invoked. *) PROCEDURE ExecFromSlot(slot: CARDINAL; reset := TRUE): ExecRes; (* Execute the code in the given "code_tbl" slot. If "reset = TRUE", then call "ResetMachine" before executing the specified code. *) END JunoRT.