Code generation / compilation complete.
parent
65e5c05c70
commit
ab84203b61
|
@ -1,71 +0,0 @@
|
||||||
package tester;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/* Automated regression tester for Checkpoint 1 tests
|
|
||||||
* Created by Max Beckman-Harned
|
|
||||||
* Put your tests in "tests/pa1_tests" folder in your Eclipse workspace directory
|
|
||||||
*/
|
|
||||||
public class Checkpoint1 {
|
|
||||||
|
|
||||||
static ExecutorService threadPool = Executors.newCachedThreadPool();
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
|
||||||
File testDir = new File(System.getProperty("java.class.path") + "/../tests/pa1_tests");
|
|
||||||
System.out.println(System.getProperty("java.class.path"));
|
|
||||||
int failures = 0;
|
|
||||||
for (File x : testDir.listFiles()) {
|
|
||||||
int returnCode = runTest(x);
|
|
||||||
if (x.getName().indexOf("pass") != -1) {
|
|
||||||
if (returnCode == 0)
|
|
||||||
System.out.println(x.getName() + " passed successfully!");
|
|
||||||
else {
|
|
||||||
failures++;
|
|
||||||
System.err.println(x.getName()
|
|
||||||
+ " failed but should have passed!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (returnCode == 4)
|
|
||||||
System.out.println(x.getName() + " failed successfully!");
|
|
||||||
else {
|
|
||||||
System.err.println(x.getName() + " did not fail properly!");
|
|
||||||
failures++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println(failures + " failures in all.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int runTest(File x) throws IOException, InterruptedException {
|
|
||||||
ProcessBuilder pb = new ProcessBuilder("java", "miniJava.Compiler", x.getPath()).directory(new File(System.getProperty("java.class.path")));
|
|
||||||
Process p = pb.start();
|
|
||||||
threadPool.execute(new ProcessOutputter(p.getInputStream(), false));
|
|
||||||
p.waitFor();
|
|
||||||
return p.exitValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ProcessOutputter implements Runnable {
|
|
||||||
private Scanner processOutput;
|
|
||||||
private boolean output;
|
|
||||||
|
|
||||||
public ProcessOutputter(InputStream _processStream, boolean _output) {
|
|
||||||
processOutput = new Scanner(_processStream);
|
|
||||||
output = _output;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while(processOutput.hasNextLine()) {
|
|
||||||
String line = processOutput.nextLine();
|
|
||||||
if (output)
|
|
||||||
System.out.println(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package mJAM;
|
||||||
|
|
||||||
|
public class Assembler {
|
||||||
|
// TBD
|
||||||
|
}
|
|
@ -0,0 +1,309 @@
|
||||||
|
/**
|
||||||
|
* mJAM instruction format
|
||||||
|
* @author prins
|
||||||
|
* @version COMP 520 V2.2
|
||||||
|
*/
|
||||||
|
package mJAM;
|
||||||
|
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disassemble the mJAM object code
|
||||||
|
* from input file xxx.mJAM
|
||||||
|
* into output file xxx.asm
|
||||||
|
*
|
||||||
|
* @author prins
|
||||||
|
* @version COMP 520 v2.2
|
||||||
|
*/
|
||||||
|
public class Disassembler {
|
||||||
|
|
||||||
|
private String objectFileName;
|
||||||
|
private String asmName;
|
||||||
|
private FileWriter asmOut;
|
||||||
|
private boolean error = false;
|
||||||
|
private Map<Integer, String> addrToLabel;
|
||||||
|
|
||||||
|
public Disassembler(String objectFileName) {
|
||||||
|
this.objectFileName = objectFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the r-field of an instruction in the form "l<I>reg</I>r", where
|
||||||
|
* l and r are the bracket characters to use.
|
||||||
|
* @param leftbracket the character to print before the register.
|
||||||
|
* @param r the number of the register.
|
||||||
|
* @param rightbracket the character to print after the register.
|
||||||
|
*/
|
||||||
|
private void writeR(char leftbracket, int r, char rightbracket) {
|
||||||
|
asmWrite(Character.toString(leftbracket));
|
||||||
|
asmWrite(Machine.intToReg[r].toString());
|
||||||
|
asmWrite(Character.toString(rightbracket));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a void n-field of an instruction.
|
||||||
|
*/
|
||||||
|
private void blankN() {
|
||||||
|
asmWrite(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the n-field of an instruction.
|
||||||
|
/**
|
||||||
|
* Writes the n-field of an instruction in the form "(n)".
|
||||||
|
* @param n the integer to write.
|
||||||
|
*/
|
||||||
|
private void writeN(int n) {
|
||||||
|
asmWrite(String.format("%-6s","(" + n + ")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the d-field of an instruction.
|
||||||
|
* @param d the integer to write.
|
||||||
|
*/
|
||||||
|
private void writeD(int d) {
|
||||||
|
asmWrite(Integer.toString(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the name of primitive routine with relative address d.
|
||||||
|
* @param d the displacment of the primitive routine.
|
||||||
|
*/
|
||||||
|
private void writePrimitive(int d) {
|
||||||
|
Machine.Prim prim = Machine.intToPrim[d];
|
||||||
|
asmWrite(String.format("%-8s",prim.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the given instruction in assembly-code format.
|
||||||
|
* @param instr the instruction to display.
|
||||||
|
*/
|
||||||
|
private void writeInstruction(Instruction instr) {
|
||||||
|
|
||||||
|
String targetLabel = "***";
|
||||||
|
// get label of destination addr, if instr transfers control
|
||||||
|
if (instr.r == Machine.Reg.CB.ordinal())
|
||||||
|
targetLabel = addrToLabel.get(instr.d);
|
||||||
|
|
||||||
|
Machine.Op instruction = Machine.intToOp[instr.op];
|
||||||
|
asmWrite(String.format("%-7s",instruction.toString()));
|
||||||
|
switch (instruction) {
|
||||||
|
case LOAD:
|
||||||
|
blankN();
|
||||||
|
writeD(instr.d);
|
||||||
|
writeR('[', instr.r, ']');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOADA:
|
||||||
|
blankN();
|
||||||
|
writeD(instr.d);
|
||||||
|
writeR('[', instr.r, ']');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOADI:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOADL:
|
||||||
|
blankN();
|
||||||
|
writeD(instr.d);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STORE:
|
||||||
|
blankN();
|
||||||
|
writeD(instr.d);
|
||||||
|
writeR('[', instr.r, ']');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STOREI:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CALL:
|
||||||
|
if (instr.r == Machine.Reg.PB.ordinal()) {
|
||||||
|
blankN();
|
||||||
|
writePrimitive(instr.d);
|
||||||
|
} else {
|
||||||
|
blankN();
|
||||||
|
asmWrite(targetLabel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CALLI:
|
||||||
|
blankN();
|
||||||
|
asmWrite(targetLabel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RETURN:
|
||||||
|
writeN(instr.n);
|
||||||
|
writeD(instr.d);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CALLD:
|
||||||
|
blankN();
|
||||||
|
writeD(instr.d);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PUSH:
|
||||||
|
blankN();
|
||||||
|
writeD(instr.d);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case POP:
|
||||||
|
blankN();
|
||||||
|
writeD(instr.d);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JUMP:
|
||||||
|
blankN();
|
||||||
|
asmWrite(targetLabel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JUMPI:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JUMPIF:
|
||||||
|
writeN(instr.n);
|
||||||
|
asmWrite(targetLabel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HALT:
|
||||||
|
writeN(instr.n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
asmWrite("???? ");
|
||||||
|
writeN(instr.n);
|
||||||
|
writeD(instr.d);
|
||||||
|
writeR('[', instr.r, ']');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* disassembles program held in code store
|
||||||
|
*/
|
||||||
|
void disassembleProgram(String asmFileName) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
asmOut = new FileWriter(asmFileName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Disassembler: can not create asm output file "
|
||||||
|
+ asmName);
|
||||||
|
error = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect all addresses that may be the target of a jump instruction
|
||||||
|
SortedSet<Integer> targets = new TreeSet<Integer>();
|
||||||
|
for (int addr = Machine.CB; addr < Machine.CT; addr++) {
|
||||||
|
Instruction inst = Machine.code[addr];
|
||||||
|
Machine.Op op = Machine.intToOp[inst.op];
|
||||||
|
switch (op) {
|
||||||
|
case CALL:
|
||||||
|
case CALLI:
|
||||||
|
// only consider calls (branches) within code memory (i.e. not primitives)
|
||||||
|
if (inst.r == Machine.Reg.CB.ordinal())
|
||||||
|
targets.add(inst.d);
|
||||||
|
break;
|
||||||
|
case JUMP:
|
||||||
|
// address following an unconditional branch is an implicit target
|
||||||
|
targets.add(addr+1);
|
||||||
|
targets.add(inst.d);
|
||||||
|
break;
|
||||||
|
case JUMPIF:
|
||||||
|
// a jump of any sort creates a branch target
|
||||||
|
targets.add(inst.d);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// map branch target addresses to unique labels
|
||||||
|
addrToLabel = new HashMap<Integer, String>();
|
||||||
|
int labelCounter = 10;
|
||||||
|
for (Integer addr : targets) {
|
||||||
|
String label = "L" + labelCounter++ ;
|
||||||
|
addrToLabel.put(addr, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disassemble each instruction
|
||||||
|
for (int addr = Machine.CB; addr < Machine.CT; addr++) {
|
||||||
|
|
||||||
|
// generate instruction address
|
||||||
|
asmWrite(String.format("%3d ", addr));
|
||||||
|
|
||||||
|
// if this addr is a branch target, output label
|
||||||
|
if (addrToLabel.containsKey(addr))
|
||||||
|
asmWrite(String.format("%-7s", addrToLabel.get(addr) + ":"));
|
||||||
|
else
|
||||||
|
asmWrite(" ");
|
||||||
|
|
||||||
|
// instruction
|
||||||
|
writeInstruction(Machine.code[addr]);
|
||||||
|
|
||||||
|
// newline
|
||||||
|
asmWrite("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// close output file
|
||||||
|
try {
|
||||||
|
asmOut.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void asmWrite(String s) {
|
||||||
|
try {
|
||||||
|
asmOut.write(s);
|
||||||
|
} catch (IOException e) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("********** mJAM Disassembler (1.0) **********");
|
||||||
|
String objectFileName = "obj.mJAM";
|
||||||
|
if (args.length == 1)
|
||||||
|
objectFileName = args[0];
|
||||||
|
Disassembler d = new Disassembler(objectFileName);
|
||||||
|
d.disassemble();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disassemble object file
|
||||||
|
* @return true if error encountered else false
|
||||||
|
*/
|
||||||
|
public boolean disassemble() {
|
||||||
|
ObjectFile objectFile = new ObjectFile(objectFileName);
|
||||||
|
|
||||||
|
// read object file into code store
|
||||||
|
if (objectFile.read()) {
|
||||||
|
System.out.println("Disassembler: unable to read object file"
|
||||||
|
+ objectFileName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assembler-code output file name
|
||||||
|
if (objectFileName.endsWith(".mJAM"))
|
||||||
|
asmName = objectFileName.substring(0, objectFileName.length() - 5)
|
||||||
|
+ ".asm";
|
||||||
|
else
|
||||||
|
asmName = objectFileName + ".asm";
|
||||||
|
|
||||||
|
// disassemble to file
|
||||||
|
disassembleProgram(asmName);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
System.out.println("Disassembler: unable to write asm file"
|
||||||
|
+ asmName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* mJAM instruction format
|
||||||
|
* @author prins
|
||||||
|
* @version COMP 520 V2.2
|
||||||
|
*/
|
||||||
|
package mJAM;
|
||||||
|
|
||||||
|
public class Instruction {
|
||||||
|
|
||||||
|
public Instruction() {
|
||||||
|
op = 0;
|
||||||
|
r = 0;
|
||||||
|
n = 0;
|
||||||
|
d = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction(int op, int n, int r, int d) {
|
||||||
|
this.op = op;
|
||||||
|
this.n = n;
|
||||||
|
this.r = r;
|
||||||
|
this.d = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Java has no type synonyms, so the following representations are
|
||||||
|
// assumed:
|
||||||
|
//
|
||||||
|
// type
|
||||||
|
// OpCode = 0..15; {4 bits unsigned}
|
||||||
|
// Register = 0..15; (4 bits unsigned)
|
||||||
|
// Length = 0..255; {8 bits unsigned}
|
||||||
|
// Operand = -2147483648 .. +2147483647; (32 bits signed for use with LOADL)
|
||||||
|
public int op; // OpCode
|
||||||
|
public int r; // RegisterNumber
|
||||||
|
public int n; // Length
|
||||||
|
public int d; // Operand
|
||||||
|
}
|
|
@ -0,0 +1,888 @@
|
||||||
|
/**
|
||||||
|
* Interprets mJAM programs
|
||||||
|
* @author prins
|
||||||
|
* @version COMP 520 V2.2
|
||||||
|
*/
|
||||||
|
package mJAM;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
// import mJAM.Machine.Reg;
|
||||||
|
|
||||||
|
public class Interpreter {
|
||||||
|
|
||||||
|
// DATA STORE
|
||||||
|
static int[] data = new int[1024];
|
||||||
|
|
||||||
|
// DATA STORE REGISTERS AND OTHER REGISTERS
|
||||||
|
final static int CB = 0, SB = 0, HB = 1024; // = upper bound of data array + 1
|
||||||
|
|
||||||
|
|
||||||
|
static int CT, CP, ST, HT, LB, OB, status, temp;
|
||||||
|
|
||||||
|
// machine status values
|
||||||
|
final static int running = 0, halted = 1, failedDataStoreFull = 2,
|
||||||
|
failedInvalidCodeAddress = 3, failedInvalidInstruction = 4,
|
||||||
|
failedOverflow = 5, failedZeroDivide = 6, failedIOError = 7,
|
||||||
|
failedArrayIndex = 8, failedNullRef = 9, failedHeapRef =10,
|
||||||
|
failedMethodIndex = 11;
|
||||||
|
|
||||||
|
static long accumulator;
|
||||||
|
|
||||||
|
// Debugger state
|
||||||
|
enum DebuggerStatus {
|
||||||
|
PAUSED, RUNNING
|
||||||
|
}
|
||||||
|
|
||||||
|
static DebuggerStatus debuggerStatus = DebuggerStatus.PAUSED;
|
||||||
|
static ArrayList<Integer> breakpoints = new ArrayList<Integer>();
|
||||||
|
static ArrayList<String> sourceLines;
|
||||||
|
|
||||||
|
static int content(int r) {
|
||||||
|
// Returns the current content of register r,
|
||||||
|
Machine.Reg reg = Machine.intToReg[r];
|
||||||
|
switch (reg) {
|
||||||
|
case CB:
|
||||||
|
return CB;
|
||||||
|
case CT:
|
||||||
|
return CT;
|
||||||
|
case PB:
|
||||||
|
return Machine.PB;
|
||||||
|
case PT:
|
||||||
|
return Machine.PT;
|
||||||
|
case SB:
|
||||||
|
return SB;
|
||||||
|
case ST:
|
||||||
|
return ST;
|
||||||
|
case HB:
|
||||||
|
return HB;
|
||||||
|
case HT:
|
||||||
|
return HT;
|
||||||
|
case LB:
|
||||||
|
return LB;
|
||||||
|
case OB:
|
||||||
|
return OB;
|
||||||
|
case CP:
|
||||||
|
return CP;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROGRAM STATUS
|
||||||
|
|
||||||
|
static void dump() {
|
||||||
|
// Writes a summary of the machine state.
|
||||||
|
int addr, dynamicLink;
|
||||||
|
System.out.println("");
|
||||||
|
System.out.println("At instruction " + CP
|
||||||
|
+ ", state of mJAM data store and registers is:");
|
||||||
|
System.out.println("");
|
||||||
|
if (HT == HB)
|
||||||
|
System.out.println(" |--------| (heap is empty)");
|
||||||
|
else {
|
||||||
|
System.out.println(" HB--> ");
|
||||||
|
System.out.println(" |--------|");
|
||||||
|
for (addr = HB - 1; addr >= HT; addr--) {
|
||||||
|
System.out.print(rightPad(6, addr + ":"));
|
||||||
|
if (addr == OB)
|
||||||
|
System.out.print("OB--> ");
|
||||||
|
else if (addr == HT)
|
||||||
|
System.out.print("HT--> ");
|
||||||
|
else
|
||||||
|
System.out.print(" ");
|
||||||
|
System.out.println("|" + leftPad(8, String.valueOf(data[addr]))
|
||||||
|
+ "|");
|
||||||
|
}
|
||||||
|
System.out.println(" |--------|");
|
||||||
|
}
|
||||||
|
System.out.println(" |////////|");
|
||||||
|
System.out.println(" |////////|");
|
||||||
|
if (ST == SB)
|
||||||
|
System.out.println(" |--------| (stack is empty)");
|
||||||
|
else {
|
||||||
|
dynamicLink = LB;
|
||||||
|
System.out.println(" ST--> |////////|");
|
||||||
|
System.out.println(" |--------|");
|
||||||
|
for (addr = ST - 1; addr >= SB; addr--) {
|
||||||
|
System.out.print(rightPad(6, addr + ": "));
|
||||||
|
if (addr == SB)
|
||||||
|
System.out.print("SB--> ");
|
||||||
|
else if (addr == LB)
|
||||||
|
System.out.print("LB--> ");
|
||||||
|
else
|
||||||
|
System.out.print(" ");
|
||||||
|
if ((addr == dynamicLink) && (dynamicLink != SB))
|
||||||
|
System.out.print("|OB="
|
||||||
|
+ leftPad(5, String.valueOf(data[addr])) + "|");
|
||||||
|
else if ((addr == dynamicLink + 1) && (dynamicLink != SB))
|
||||||
|
System.out.print("|DL="
|
||||||
|
+ leftPad(5, String.valueOf(data[addr])) + "|");
|
||||||
|
else if ((addr == dynamicLink + 2) && (dynamicLink != SB))
|
||||||
|
System.out.print("|RA="
|
||||||
|
+ leftPad(5, String.valueOf(data[addr])) + "|");
|
||||||
|
else
|
||||||
|
System.out.print("|"
|
||||||
|
+ leftPad(8, String.valueOf(data[addr])) + "|");
|
||||||
|
System.out.println("");
|
||||||
|
if (addr == dynamicLink) {
|
||||||
|
System.out.println(" |--------|");
|
||||||
|
dynamicLink = data[addr + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String leftPad(int len, String s) {
|
||||||
|
int aLen = Math.max(len, s.length());
|
||||||
|
StringBuffer buf = new StringBuffer(s);
|
||||||
|
String r = buf.insert(0, " ").toString();
|
||||||
|
return r.substring(r.length() - aLen, r.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String rightPad(int len, String s) {
|
||||||
|
int aLen = Math.max(len, s.length());
|
||||||
|
String r = s + " ";
|
||||||
|
return r.substring(0, aLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showStatus() {
|
||||||
|
// Writes an indication of whether and why the program has terminated.
|
||||||
|
System.out.println("");
|
||||||
|
System.out.print("*** ");
|
||||||
|
switch (status) {
|
||||||
|
case running:
|
||||||
|
System.out.println("Program is running.");
|
||||||
|
break;
|
||||||
|
case halted:
|
||||||
|
System.out.println("Program has halted normally.");
|
||||||
|
break;
|
||||||
|
case failedDataStoreFull:
|
||||||
|
System.out.println("Program has failed due to exhaustion of Data Store.");
|
||||||
|
break;
|
||||||
|
case failedInvalidCodeAddress:
|
||||||
|
System.out.println("Program has failed due to an invalid code address.");
|
||||||
|
break;
|
||||||
|
case failedInvalidInstruction:
|
||||||
|
System.out.println("Program has failed due to an invalid instruction.");
|
||||||
|
break;
|
||||||
|
case failedOverflow:
|
||||||
|
System.out.println("Program has failed due to overflow.");
|
||||||
|
break;
|
||||||
|
case failedZeroDivide:
|
||||||
|
System.out.println("Program has failed due to division by zero.");
|
||||||
|
break;
|
||||||
|
case failedIOError:
|
||||||
|
System.out.println("Program has failed due to an IO error.");
|
||||||
|
break;
|
||||||
|
case failedArrayIndex:
|
||||||
|
System.out.println("Program has failed due to an array index error.");
|
||||||
|
break;
|
||||||
|
case failedNullRef:
|
||||||
|
System.out.println("Program has failed due to a null pointer reference.");
|
||||||
|
break;
|
||||||
|
case failedHeapRef:
|
||||||
|
System.out.println("Program has failed due to an invalid Heap reference.");
|
||||||
|
break;
|
||||||
|
case failedMethodIndex:
|
||||||
|
System.out.println("Program has failed due to an improper method index in CALLD.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("Machine is in an unknown state.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (status != halted)
|
||||||
|
dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
// INTERPRETATION
|
||||||
|
|
||||||
|
static void checkSpace(int spaceNeeded) {
|
||||||
|
// Signals failure if there is not enough space to expand the stack or
|
||||||
|
// heap by spaceNeeded.
|
||||||
|
if (HT - ST < spaceNeeded)
|
||||||
|
status = failedDataStoreFull;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean invalidHeapRef(int addr) {
|
||||||
|
// if addr is null ptr or outside of heap bounds, sets status to failure
|
||||||
|
if (addr == Machine.nullRep)
|
||||||
|
status = failedNullRef;
|
||||||
|
else if (addr < HT + 2 || addr >= HB)
|
||||||
|
status = failedHeapRef;
|
||||||
|
return (status != running);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isTrue(int datum) {
|
||||||
|
// Tests whether the given datum represents true.
|
||||||
|
return (datum == Machine.trueRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int overflowChecked(long datum) {
|
||||||
|
// Signals failure if the datum is too large to fit into a single word,
|
||||||
|
// otherwise returns the datum as a single word.
|
||||||
|
if ((Machine.minintRep <= datum) && (datum <= Machine.maxintRep))
|
||||||
|
return (int) datum;
|
||||||
|
else {
|
||||||
|
status = failedOverflow;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int toInt(boolean b) {
|
||||||
|
return b ? Machine.trueRep : Machine.falseRep;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int currentChar;
|
||||||
|
|
||||||
|
static int readInt() throws java.io.IOException {
|
||||||
|
int temp = 0;
|
||||||
|
int sign = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
currentChar = System.in.read();
|
||||||
|
} while (Character.isWhitespace((char) currentChar));
|
||||||
|
|
||||||
|
if ((currentChar == '-') || (currentChar == '+'))
|
||||||
|
do {
|
||||||
|
sign = (currentChar == '-') ? -1 : 1;
|
||||||
|
currentChar = System.in.read();
|
||||||
|
} while ((currentChar == '-') || currentChar == '+');
|
||||||
|
|
||||||
|
if (Character.isDigit((char) currentChar))
|
||||||
|
do {
|
||||||
|
temp = temp * 10 + (currentChar - '0');
|
||||||
|
currentChar = System.in.read();
|
||||||
|
} while (Character.isDigit((char) currentChar));
|
||||||
|
|
||||||
|
return sign * temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke primitive operation with argument(s) on the stack
|
||||||
|
// primitives are static and are not supplied an instance on the stack.
|
||||||
|
static void callPrimitive(int id) {
|
||||||
|
|
||||||
|
int addr, size, index;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
Machine.Prim prim = Machine.intToPrim[id];
|
||||||
|
switch (prim) {
|
||||||
|
case id:
|
||||||
|
break; // nothing to be done
|
||||||
|
case not:
|
||||||
|
data[ST - 1] = toInt(!isTrue(data[ST - 1]));
|
||||||
|
break;
|
||||||
|
case and:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(isTrue(data[ST - 1]) & isTrue(data[ST]));
|
||||||
|
break;
|
||||||
|
case or:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(isTrue(data[ST - 1]) | isTrue(data[ST]));
|
||||||
|
break;
|
||||||
|
case succ:
|
||||||
|
data[ST - 1] = overflowChecked(data[ST - 1] + 1);
|
||||||
|
break;
|
||||||
|
case pred:
|
||||||
|
data[ST - 1] = overflowChecked(data[ST - 1] - 1);
|
||||||
|
break;
|
||||||
|
case neg:
|
||||||
|
data[ST - 1] = overflowChecked(-data[ST - 1]);
|
||||||
|
break;
|
||||||
|
case add:
|
||||||
|
ST = ST - 1;
|
||||||
|
accumulator = data[ST - 1];
|
||||||
|
data[ST - 1] = overflowChecked(accumulator + data[ST]);
|
||||||
|
break;
|
||||||
|
case sub:
|
||||||
|
ST = ST - 1;
|
||||||
|
accumulator = data[ST - 1];
|
||||||
|
data[ST - 1] = overflowChecked(accumulator - data[ST]);
|
||||||
|
break;
|
||||||
|
case mult:
|
||||||
|
ST = ST - 1;
|
||||||
|
accumulator = data[ST - 1];
|
||||||
|
data[ST - 1] = overflowChecked(accumulator * data[ST]);
|
||||||
|
break;
|
||||||
|
case div:
|
||||||
|
ST = ST - 1;
|
||||||
|
accumulator = data[ST - 1];
|
||||||
|
if (data[ST] != 0)
|
||||||
|
data[ST - 1] = (int) (accumulator / data[ST]);
|
||||||
|
else
|
||||||
|
status = failedZeroDivide;
|
||||||
|
break;
|
||||||
|
case mod:
|
||||||
|
ST = ST - 1;
|
||||||
|
accumulator = data[ST - 1];
|
||||||
|
if (data[ST] != 0)
|
||||||
|
data[ST - 1] = (int) (accumulator % data[ST]);
|
||||||
|
else
|
||||||
|
status = failedZeroDivide;
|
||||||
|
break;
|
||||||
|
case lt:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(data[ST - 1] < data[ST]);
|
||||||
|
break;
|
||||||
|
case le:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(data[ST - 1] <= data[ST]);
|
||||||
|
break;
|
||||||
|
case ge:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(data[ST - 1] >= data[ST]);
|
||||||
|
break;
|
||||||
|
case gt:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(data[ST - 1] > data[ST]);
|
||||||
|
break;
|
||||||
|
case eq:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(data[ST - 1] == data[ST]);
|
||||||
|
break;
|
||||||
|
case ne:
|
||||||
|
ST = ST - 1;
|
||||||
|
data[ST - 1] = toInt(data[ST - 1] != data[ST]);
|
||||||
|
break;
|
||||||
|
case eol:
|
||||||
|
data[ST] = toInt(currentChar == '\n');
|
||||||
|
ST = ST + 1;
|
||||||
|
break;
|
||||||
|
case eof:
|
||||||
|
data[ST] = toInt(currentChar == -1);
|
||||||
|
ST = ST + 1;
|
||||||
|
break;
|
||||||
|
case get:
|
||||||
|
ST = ST - 1;
|
||||||
|
addr = data[ST];
|
||||||
|
try {
|
||||||
|
currentChar = System.in.read();
|
||||||
|
} catch (java.io.IOException s) {
|
||||||
|
status = failedIOError;
|
||||||
|
}
|
||||||
|
data[addr] = (int) currentChar;
|
||||||
|
break;
|
||||||
|
case put:
|
||||||
|
ST = ST - 1;
|
||||||
|
ch = (char) data[ST];
|
||||||
|
System.out.print(ch);
|
||||||
|
break;
|
||||||
|
case geteol:
|
||||||
|
try {
|
||||||
|
while ((currentChar = System.in.read()) != '\n')
|
||||||
|
;
|
||||||
|
} catch (java.io.IOException s) {
|
||||||
|
status = failedIOError;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case puteol:
|
||||||
|
System.out.println("");
|
||||||
|
break;
|
||||||
|
case getint:
|
||||||
|
ST = ST - 1;
|
||||||
|
addr = data[ST];
|
||||||
|
try {
|
||||||
|
accumulator = readInt();
|
||||||
|
} catch (java.io.IOException s) {
|
||||||
|
status = failedIOError;
|
||||||
|
}
|
||||||
|
data[addr] = (int) accumulator;
|
||||||
|
break;
|
||||||
|
case putint:
|
||||||
|
ST = ST - 1;
|
||||||
|
accumulator = data[ST];
|
||||||
|
System.out.print(accumulator);
|
||||||
|
break;
|
||||||
|
// output with prefix for tester
|
||||||
|
case putintnl:
|
||||||
|
ST = ST - 1;
|
||||||
|
accumulator = data[ST];
|
||||||
|
System.out.print(">>> " + accumulator + "\n");
|
||||||
|
break;
|
||||||
|
case alloc:
|
||||||
|
size = data[ST - 1];
|
||||||
|
checkSpace(size);
|
||||||
|
HT = HT - size;
|
||||||
|
data[ST - 1] = HT;
|
||||||
|
break;
|
||||||
|
case dispose:
|
||||||
|
ST = ST - 1; // no action taken at present
|
||||||
|
break;
|
||||||
|
case newobj:
|
||||||
|
// ..., class obj addr, number of fields ==> ..., new obj addr
|
||||||
|
size = data[ST - 1] + 2; // number of fields + 2 word descriptor
|
||||||
|
checkSpace(size);
|
||||||
|
HT = HT - size; // reserve space
|
||||||
|
data[HT] = data[ST - 2]; // set class object addr
|
||||||
|
data[HT + 1] = size - 2; // set size of object
|
||||||
|
data[ST - 2] = HT + 2; // addr of new object instance, returned on stack
|
||||||
|
ST = ST - 1; // net effect of pop 2 args, push 1 result
|
||||||
|
for (int i = 2; i < size; i++) {
|
||||||
|
data[HT + i] = 0; // zero all fields of new object
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case newarr:
|
||||||
|
// ..., number of elements ==> ..., new int[] addr
|
||||||
|
size = data[ST - 1] + 2; // array + 2 word descriptor
|
||||||
|
checkSpace(size);
|
||||||
|
HT = HT - size;
|
||||||
|
data[HT] = -2; // tag for array
|
||||||
|
data[HT + 1] = size - 2; // size of array
|
||||||
|
data[ST - 1] = HT + 2; // addr of array instance, returned on stack
|
||||||
|
for (int i = 2; i < size; i++) {
|
||||||
|
data[HT + i] = 0; // zero all elements of new array
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case arrayref:
|
||||||
|
// ..., array addr a, element index i ==> ..., a[i]
|
||||||
|
addr = data[ST - 2];
|
||||||
|
if (invalidHeapRef(addr))
|
||||||
|
break;
|
||||||
|
index = data[ST - 1];
|
||||||
|
if (data[addr - 2] != -2 || index < 0 || index >= data[addr - 1]) {
|
||||||
|
status = failedArrayIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data[ST - 2] = data[addr + index]; // result element, returned on stack
|
||||||
|
ST = ST - 1; // pop two args, return one result
|
||||||
|
break;
|
||||||
|
case arrayupd:
|
||||||
|
// ..., array addr a, element index i, new value v ==> ...
|
||||||
|
// and a[i] := v
|
||||||
|
addr = data[ST - 3];
|
||||||
|
if (invalidHeapRef(addr))
|
||||||
|
break;
|
||||||
|
index = data[ST - 2];
|
||||||
|
if (data[addr - 2] != -2 || index < 0 || index >= data[addr - 1]) {
|
||||||
|
status = failedArrayIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data[addr + index] = data[ST - 1]; // update array element
|
||||||
|
ST = ST - 3; // pop 3 args, return no result
|
||||||
|
break;
|
||||||
|
case fieldref:
|
||||||
|
// ..., obj addr a, field index i ==> ..., value of ith field of a
|
||||||
|
addr = data[ST - 2];
|
||||||
|
if (invalidHeapRef(addr))
|
||||||
|
break;
|
||||||
|
index = data[ST - 1];
|
||||||
|
if (index < 0 || index >= data[addr - 1]) {
|
||||||
|
status = failedArrayIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data[ST - 2] = data[addr + index]; // field to stack top
|
||||||
|
ST = ST - 1; // pop two args, return one result
|
||||||
|
break;
|
||||||
|
case fieldupd:
|
||||||
|
// ..., obj addr a, field index i, new value v ==> ...
|
||||||
|
// and a.i := v
|
||||||
|
addr = data[ST - 3];
|
||||||
|
if (invalidHeapRef(addr))
|
||||||
|
break;
|
||||||
|
index = data[ST - 2];
|
||||||
|
if (index < 0 || index >= data[addr - 1]) {
|
||||||
|
status = failedArrayIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data[addr + index] = data[ST - 1]; // update field to new value
|
||||||
|
ST = ST - 3; // pop 3 args, return no result
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interpretOneOperation() {
|
||||||
|
// Fetch instruction ...
|
||||||
|
Instruction currentInstr = Machine.code[CP];
|
||||||
|
// Decode instruction ...
|
||||||
|
int op = currentInstr.op;
|
||||||
|
int r = currentInstr.r;
|
||||||
|
int n = currentInstr.n;
|
||||||
|
int d = currentInstr.d;
|
||||||
|
int addr;
|
||||||
|
// Execute instruction ...
|
||||||
|
|
||||||
|
Machine.Op operation = Machine.intToOp[op];
|
||||||
|
|
||||||
|
switch (operation) {
|
||||||
|
case LOAD:
|
||||||
|
addr = d + content(r);
|
||||||
|
checkSpace(1);
|
||||||
|
data[ST] = data[addr];
|
||||||
|
ST = ST + 1;
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case LOADA:
|
||||||
|
addr = d + content(r);
|
||||||
|
checkSpace(1);
|
||||||
|
data[ST] = addr;
|
||||||
|
ST = ST + 1;
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case LOADI:
|
||||||
|
ST = ST - 1;
|
||||||
|
addr = data[ST];
|
||||||
|
checkSpace(1);
|
||||||
|
data[ST] = data[addr];
|
||||||
|
ST = ST + 1;
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case LOADL:
|
||||||
|
checkSpace(1);
|
||||||
|
data[ST] = d;
|
||||||
|
ST = ST + 1;
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case STORE:
|
||||||
|
addr = d + content(r);
|
||||||
|
ST = ST - 1;
|
||||||
|
data[addr] = data[ST];
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case STOREI:
|
||||||
|
ST = ST - 1;
|
||||||
|
addr = data[ST];
|
||||||
|
ST = ST - 1;
|
||||||
|
data[addr] = data[ST];
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CALL:
|
||||||
|
// call static method, including primitives
|
||||||
|
// arguments are on stack
|
||||||
|
addr = d + content(r); // effective address
|
||||||
|
if (addr >= Machine.PB) {
|
||||||
|
callPrimitive(addr - Machine.PB);
|
||||||
|
CP = CP + 1;
|
||||||
|
} else {
|
||||||
|
// static method in code segment, no instance addr on stack
|
||||||
|
checkSpace(3);
|
||||||
|
data[ST] = OB; // save caller OB in callee frame
|
||||||
|
data[ST + 1] = LB; // save caller LB in callee frame (dynamic link)
|
||||||
|
data[ST + 2] = CP + 1; // save caller return address in callee frame
|
||||||
|
OB = Machine.nullRep; // set callee OB (null since no instance)
|
||||||
|
LB = ST; // set LB = start of callee frame
|
||||||
|
ST = ST + 3; // set ST = end of callee frame
|
||||||
|
CP = addr; // execution resumes at addr specified in CALL inst
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CALLI:
|
||||||
|
// call instance method
|
||||||
|
// arguments on stack, followed by instance address
|
||||||
|
addr = d + content(r); // effective address
|
||||||
|
if (addr >= Machine.CT) {
|
||||||
|
// no instance methods outside of code segment
|
||||||
|
status = failedInvalidInstruction;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// instance address is last arg on stack and is overwritten by frame
|
||||||
|
checkSpace(2);
|
||||||
|
temp = data[ST - 1]; // save instance address temporarily
|
||||||
|
data[ST - 1] = OB; // save caller OB in callee frame
|
||||||
|
data[ST] = LB; // save caller LB in callee frame (dynamic link)
|
||||||
|
data[ST + 1] = CP + 1; // save caller return address in callee frame
|
||||||
|
OB = temp; // set OB for callee
|
||||||
|
LB = ST - 1; // set LB = start of callee frame
|
||||||
|
ST = ST + 2; // set ST = end of callee frame
|
||||||
|
CP = addr; // execution resumes at addr specified in CALL inst
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RETURN:
|
||||||
|
// d = number of method args (does not include instance addr for CALLI)
|
||||||
|
// n = size of result (0 or 1)
|
||||||
|
if (n < 0 || n > 1) {
|
||||||
|
status = failedInvalidInstruction;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
addr = LB - d; // addr of caller args
|
||||||
|
OB = data[LB]; // restore caller OB, LB, CP
|
||||||
|
CP = data[LB + 2];
|
||||||
|
LB = data[LB + 1];
|
||||||
|
if (n == 1)
|
||||||
|
data[addr] = data[ST - 1]; // return value if any
|
||||||
|
ST = addr + n; // caller stack top
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CALLD:
|
||||||
|
// dynamic method dispatch of method with index d (origin 0)
|
||||||
|
// arguments on stack, followed by instance addr
|
||||||
|
{
|
||||||
|
addr = data[ST - 1]; // instance addr
|
||||||
|
if (invalidHeapRef(addr))
|
||||||
|
break;
|
||||||
|
int classDescAddr = data[addr - 2];
|
||||||
|
if (classDescAddr >= ST || classDescAddr <= SB || d >= data[classDescAddr + 1] || d < 0) {
|
||||||
|
status = failedMethodIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ST = ST - 1;
|
||||||
|
checkSpace(3);
|
||||||
|
data[ST] = OB;
|
||||||
|
data[ST + 1] = LB;
|
||||||
|
data[ST + 2] = CP + 1;
|
||||||
|
OB = addr;
|
||||||
|
LB = ST;
|
||||||
|
ST = ST + 3;
|
||||||
|
CP = data[classDescAddr + 2 + n];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PUSH: // push d elements on stack
|
||||||
|
checkSpace(d);
|
||||||
|
ST = ST + d;
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case POP: // pop d elements off stack
|
||||||
|
ST = ST - d;
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case JUMP:
|
||||||
|
CP = d + content(r);
|
||||||
|
break;
|
||||||
|
case JUMPI:
|
||||||
|
ST = ST - 1;
|
||||||
|
CP = data[ST];
|
||||||
|
break;
|
||||||
|
case JUMPIF:
|
||||||
|
ST = ST - 1;
|
||||||
|
if (data[ST] == n)
|
||||||
|
CP = d + content(r);
|
||||||
|
else
|
||||||
|
CP = CP + 1;
|
||||||
|
break;
|
||||||
|
case HALT:
|
||||||
|
if (n > 0) {
|
||||||
|
// halt n > 0 --> snapshot machine state and continue execution
|
||||||
|
dump();
|
||||||
|
CP = CP + 1;
|
||||||
|
} else
|
||||||
|
status = halted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((CP < CB) || (CP >= CT))
|
||||||
|
status = failedInvalidCodeAddress;
|
||||||
|
|
||||||
|
if (breakpoints.indexOf(CP) != -1) {
|
||||||
|
debuggerStatus = DebuggerStatus.PAUSED;
|
||||||
|
System.out.println("Breakpoint hit: " + sourceLines.get(CP));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initMachine() {
|
||||||
|
// Initialize registers ...
|
||||||
|
ST = SB;
|
||||||
|
HT = HB;
|
||||||
|
LB = SB;
|
||||||
|
CP = CB;
|
||||||
|
OB = -1; // invalid instance addr
|
||||||
|
CT = Machine.CT;
|
||||||
|
status = running;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interpretProgram() {
|
||||||
|
// Runs the program in code store.
|
||||||
|
initMachine();
|
||||||
|
do {
|
||||||
|
interpretOneOperation();
|
||||||
|
} while (status == running);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void runProgramFromStart() {
|
||||||
|
initMachine();
|
||||||
|
continueProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void continueProgram() {
|
||||||
|
debuggerStatus = DebuggerStatus.RUNNING;
|
||||||
|
do {
|
||||||
|
interpretOneOperation();
|
||||||
|
} while (status == running && debuggerStatus == DebuggerStatus.RUNNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printHelp() {
|
||||||
|
String[] help = {
|
||||||
|
"p or print:",
|
||||||
|
" print entire machine state",
|
||||||
|
"l or list [offset] [size]:",
|
||||||
|
" print the instructions around CP + offset, with size lines on either side",
|
||||||
|
" offset = 0 and size = 2 by default",
|
||||||
|
"b or break [address]:",
|
||||||
|
" set a breakpoint at address",
|
||||||
|
" address = CP by default",
|
||||||
|
"del:",
|
||||||
|
" delete one or more breakpoints",
|
||||||
|
"n or next:",
|
||||||
|
" execute one instruction",
|
||||||
|
"c or continue:",
|
||||||
|
" continue running the program from current position, until next breakpoint or completion",
|
||||||
|
"r or run:",
|
||||||
|
" run the program from start, until next breakpoint or completion",
|
||||||
|
"i or info:", " list the current breakpoints",
|
||||||
|
"q, quit or <EOF>:", " quit the debugger",
|
||||||
|
"Simply press enter to repeat the last command", "? or help:",
|
||||||
|
" print this help" };
|
||||||
|
|
||||||
|
for (String line : help) {
|
||||||
|
System.out.println(" " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debugProgram() {
|
||||||
|
initMachine();
|
||||||
|
|
||||||
|
BufferedReader inputReader = new BufferedReader(new InputStreamReader(
|
||||||
|
System.in));
|
||||||
|
|
||||||
|
String lastCommand = "";
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
System.out.print("\n: ");
|
||||||
|
String inputLine;
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputLine = inputReader.readLine();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputLine == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Scanner scanner = new Scanner(inputLine);
|
||||||
|
String command = (scanner.hasNext() ? scanner.next() : lastCommand);
|
||||||
|
lastCommand = command;
|
||||||
|
|
||||||
|
if (command.equals("?") || command.equals("help")) {
|
||||||
|
printHelp();
|
||||||
|
} else if (command.equalsIgnoreCase("p")
|
||||||
|
|| command.equalsIgnoreCase("print")) {
|
||||||
|
dump();
|
||||||
|
} else if (command.equalsIgnoreCase("l")
|
||||||
|
|| command.equalsIgnoreCase("list")) {
|
||||||
|
int offset = 0, size = 2;
|
||||||
|
if (scanner.hasNextInt())
|
||||||
|
offset = scanner.nextInt();
|
||||||
|
if (scanner.hasNextInt())
|
||||||
|
size = scanner.nextInt();
|
||||||
|
|
||||||
|
for (int i = CP + offset - size; i <= CP + offset + size; ++i) {
|
||||||
|
if (i >= 0 && i < sourceLines.size())
|
||||||
|
System.out.println((i == CP ? " >" : " ")
|
||||||
|
+ sourceLines.get(i));
|
||||||
|
}
|
||||||
|
} else if (command.equalsIgnoreCase("b")
|
||||||
|
|| command.equalsIgnoreCase("break")) {
|
||||||
|
int addr = scanner.hasNextInt() ? scanner.nextInt() : CP;
|
||||||
|
if (!breakpoints.contains(addr))
|
||||||
|
breakpoints.add(addr);
|
||||||
|
System.out.println("Added breakpoint at "
|
||||||
|
+ sourceLines.get(addr));
|
||||||
|
} else if (command.equalsIgnoreCase("del")) {
|
||||||
|
while (scanner.hasNextInt()) {
|
||||||
|
int addr = scanner.nextInt(), idx = breakpoints
|
||||||
|
.indexOf(addr);
|
||||||
|
if (idx != -1) {
|
||||||
|
breakpoints.remove(idx);
|
||||||
|
} else {
|
||||||
|
System.out.println("No breakpoint at " + addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (command.equalsIgnoreCase("n")
|
||||||
|
|| command.equalsIgnoreCase("next")) {
|
||||||
|
if (status == running) {
|
||||||
|
interpretOneOperation();
|
||||||
|
} else {
|
||||||
|
System.out.println("Program is not running");
|
||||||
|
}
|
||||||
|
} else if (command.equalsIgnoreCase("c")
|
||||||
|
|| command.equalsIgnoreCase("continue")) {
|
||||||
|
continueProgram();
|
||||||
|
} else if (command.equalsIgnoreCase("r")
|
||||||
|
|| command.equalsIgnoreCase("run")) {
|
||||||
|
runProgramFromStart();
|
||||||
|
} else if (command.equalsIgnoreCase("i")
|
||||||
|
|| command.equalsIgnoreCase("info")) {
|
||||||
|
System.out.println("Breakpoints:");
|
||||||
|
for (int b : breakpoints) {
|
||||||
|
System.out.println("\t" + sourceLines.get(b));
|
||||||
|
}
|
||||||
|
} else if (command.equalsIgnoreCase("q")
|
||||||
|
|| command.equalsIgnoreCase("quit")) {
|
||||||
|
scanner.close();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
System.out.println("Unknown command '" + command
|
||||||
|
+ "'. Type 'help' for a list of commands");
|
||||||
|
}
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RUNNING
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out
|
||||||
|
.println("********** mJAM Interpreter (Version 1.2) **********");
|
||||||
|
|
||||||
|
String objectFileName;
|
||||||
|
if (args.length >= 1)
|
||||||
|
objectFileName = args[0];
|
||||||
|
else
|
||||||
|
objectFileName = "obj.mJAM";
|
||||||
|
|
||||||
|
String sourceFileName;
|
||||||
|
if (args.length >= 2) {
|
||||||
|
sourceFileName = args[1];
|
||||||
|
debug(objectFileName, sourceFileName);
|
||||||
|
} else {
|
||||||
|
interpret(objectFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void interpret(String objectFileName) {
|
||||||
|
|
||||||
|
ObjectFile objectFile = new ObjectFile(objectFileName);
|
||||||
|
if (objectFile.read()) {
|
||||||
|
System.out.println("Unable to load object file " + objectFileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
interpretProgram();
|
||||||
|
showStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void debug(String objectFileName, String sourceFileName) {
|
||||||
|
ObjectFile objectFile = new ObjectFile(objectFileName);
|
||||||
|
if (objectFile.read()) {
|
||||||
|
System.out.println("Unable to load object file " + objectFileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceLines = new ArrayList<String>();
|
||||||
|
try {
|
||||||
|
BufferedReader reader = new BufferedReader(new FileReader(new File(
|
||||||
|
sourceFileName)));
|
||||||
|
String line = reader.readLine();
|
||||||
|
while (line != null) {
|
||||||
|
sourceLines.add(line);
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.out.println("Unable to load source file " + sourceFileName);
|
||||||
|
return;
|
||||||
|
} catch (IOException ie) {
|
||||||
|
System.out.println("Unable to load source file " + sourceFileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugProgram();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
package mJAM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines names and sizes of mJAM instructions and primitives
|
||||||
|
* @author prins
|
||||||
|
* @version COMP 520 V2.2
|
||||||
|
*/
|
||||||
|
public final class Machine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mJAM instructions
|
||||||
|
*/
|
||||||
|
public enum Op {
|
||||||
|
LOAD,
|
||||||
|
LOADA,
|
||||||
|
LOADI,
|
||||||
|
LOADL,
|
||||||
|
STORE,
|
||||||
|
STOREI,
|
||||||
|
CALL, // direct call of instance method
|
||||||
|
CALLI, // indirect call of instance method
|
||||||
|
CALLD, // dynamic call of instance method
|
||||||
|
RETURN,
|
||||||
|
PUSH,
|
||||||
|
POP,
|
||||||
|
JUMP,
|
||||||
|
JUMPI,
|
||||||
|
JUMPIF,
|
||||||
|
HALT;
|
||||||
|
}
|
||||||
|
public static Op [] intToOp = Op.values();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mJAM registers
|
||||||
|
*/
|
||||||
|
public enum Reg {
|
||||||
|
ZR, // zero, not used
|
||||||
|
CB, // code base
|
||||||
|
CT, // code top
|
||||||
|
CP, // code pointer
|
||||||
|
PB, // primitives base
|
||||||
|
PT, // primitives top
|
||||||
|
SB, // execution stack base
|
||||||
|
ST, // execution stack top
|
||||||
|
LB, // locals base
|
||||||
|
HB, // heap base
|
||||||
|
HT, // heap top
|
||||||
|
OB; // object base
|
||||||
|
}
|
||||||
|
public static Reg [] intToReg = Reg.values();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mJAM primitives
|
||||||
|
*/
|
||||||
|
public enum Prim {
|
||||||
|
id,
|
||||||
|
not,
|
||||||
|
and,
|
||||||
|
or,
|
||||||
|
succ,
|
||||||
|
pred,
|
||||||
|
neg,
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
mult,
|
||||||
|
div,
|
||||||
|
mod,
|
||||||
|
lt,
|
||||||
|
le,
|
||||||
|
ge,
|
||||||
|
gt,
|
||||||
|
eq,
|
||||||
|
ne,
|
||||||
|
eol,
|
||||||
|
eof,
|
||||||
|
get,
|
||||||
|
put,
|
||||||
|
geteol,
|
||||||
|
puteol,
|
||||||
|
getint,
|
||||||
|
putint,
|
||||||
|
putintnl,
|
||||||
|
alloc,
|
||||||
|
dispose,
|
||||||
|
newobj,
|
||||||
|
newarr,
|
||||||
|
arrayref,
|
||||||
|
arrayupd,
|
||||||
|
fieldref,
|
||||||
|
fieldupd;
|
||||||
|
}
|
||||||
|
public static Prim [] intToPrim = Prim.values();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// range for int constants
|
||||||
|
public final static long
|
||||||
|
minintRep = -2147483648,
|
||||||
|
maxintRep = 2147483647;
|
||||||
|
|
||||||
|
|
||||||
|
// CODE STORE REGISTERS
|
||||||
|
public final static int CB = 0; // start of code space
|
||||||
|
public final static int PB = 1024; // size of code space reserved for instructions
|
||||||
|
public final static int PT = PB + Prim.values().length; // code space reserved for primitives
|
||||||
|
|
||||||
|
// CODE STORE
|
||||||
|
public static Instruction[] code = new Instruction[PB];
|
||||||
|
public static int CT = CB;
|
||||||
|
|
||||||
|
public static void initCodeGen() {
|
||||||
|
CT = CB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places an instruction, with the given fields, into the next position in the code store
|
||||||
|
* @param op - operation
|
||||||
|
* @param n - length
|
||||||
|
* @param r - register
|
||||||
|
* @param d - displacement
|
||||||
|
*/
|
||||||
|
public static void emit(Op op, int n, Reg r, Prim d) {
|
||||||
|
emit(op.ordinal(), n, r.ordinal(), d.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* emit operation with single literal argument d (n,r not used). These are
|
||||||
|
* operations like LOADL 44, PUSH 3, and CALLD 1
|
||||||
|
*/
|
||||||
|
public static void emit(Op op, int d) {
|
||||||
|
emit(op.ordinal(), 0, 0, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* emit "call primitive operation" (operation built-in to mJAM). This
|
||||||
|
* generates CALL primitiveop[PB]
|
||||||
|
*/
|
||||||
|
public static void emit(Prim d) {
|
||||||
|
emit(Op.CALL.ordinal(), 0, Machine.Reg.PB.ordinal(), d.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* emit operations without arguments. These are operations like
|
||||||
|
* LOADI and STOREI
|
||||||
|
*/
|
||||||
|
public static void emit(Op op) {
|
||||||
|
emit(op, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* emit operation with register r and integer displacement. These are
|
||||||
|
* operations like JUMP 25[CB] and LOAD 6[LB]
|
||||||
|
*/
|
||||||
|
public static void emit(Op op, Reg r, int d) {
|
||||||
|
emit(op.ordinal(), 0, r.ordinal(), d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* emit operation with n field, and register r and integer displacement. These are
|
||||||
|
* operations like JUMPIF (1) 25[CB]. In the assembly code the value of n is shown
|
||||||
|
* in parens.
|
||||||
|
*/
|
||||||
|
public static void emit(Op op, int n, Reg r, int d) {
|
||||||
|
emit(op.ordinal(), n, r.ordinal(), d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* emit operation with integer n, r, d. These are operations
|
||||||
|
* like RETURN (1) 3 and HALT (4) 0. For RETURN the value
|
||||||
|
* of d is the number of caller args to pop off the callers
|
||||||
|
* stack and n is the number of values to return at caller stack
|
||||||
|
* top. n must be 0 or 1.
|
||||||
|
*/
|
||||||
|
public static void emit(Op op, int n, int r, int d) {
|
||||||
|
emit(op.ordinal(), n, r, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper operation for emit using integer values
|
||||||
|
*/
|
||||||
|
private static void emit (int op, int n, int r, int d) {
|
||||||
|
if (n > 255) {
|
||||||
|
System.out.println("length of operand can't exceed 255 words");
|
||||||
|
n = 255; // to allow code generation to continue
|
||||||
|
}
|
||||||
|
if (CT >= Machine.PB)
|
||||||
|
System.out.println("mJAM: code segment capacity exceeded");
|
||||||
|
|
||||||
|
Instruction nextInstr = new Instruction(op, n, r, d);
|
||||||
|
Machine.code[CT] = nextInstr;
|
||||||
|
CT = CT + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return address (relative to CB) of next instruction to be generated
|
||||||
|
*/
|
||||||
|
public static int nextInstrAddr() {
|
||||||
|
return CT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the displacement component of the (JUMP or CALL) instruction at addr
|
||||||
|
* @param addr
|
||||||
|
* @param displacement
|
||||||
|
*/
|
||||||
|
public static void patch(int addr, int displacement) {
|
||||||
|
if (addr < 0 || addr >= CT) {
|
||||||
|
System.out.println("patch: address of instruction to be patched is out of range");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (displacement < 0 || displacement > CT) {
|
||||||
|
System.out.println("patch: target address of patch is out of range");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Machine.code[addr].d = displacement;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DATA REPRESENTATION
|
||||||
|
|
||||||
|
public final static int
|
||||||
|
booleanSize = 1,
|
||||||
|
characterSize = 1,
|
||||||
|
integerSize = 1,
|
||||||
|
addressSize = 1,
|
||||||
|
linkDataSize = 3 * addressSize, // caller's OB, LB, CP
|
||||||
|
falseRep = 0,
|
||||||
|
trueRep = 1,
|
||||||
|
nullRep = 0;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* Reads and writes mJAM object files
|
||||||
|
* @author prins
|
||||||
|
* @version COMP 520 V2.2
|
||||||
|
*/
|
||||||
|
package mJAM;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
public class ObjectFile {
|
||||||
|
|
||||||
|
String objectFileName;
|
||||||
|
|
||||||
|
public ObjectFile(String objectFileName) {
|
||||||
|
super();
|
||||||
|
this.objectFileName = objectFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write code store as object file
|
||||||
|
* @param output object file
|
||||||
|
* @return true if write fails
|
||||||
|
*/
|
||||||
|
public boolean write(){
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
FileOutputStream objectFile = new FileOutputStream(objectFileName);
|
||||||
|
DataOutputStream is = new DataOutputStream(objectFile);
|
||||||
|
for (int i = Machine.CB; i < Machine.CT; i++ ){
|
||||||
|
Instruction inst = Machine.code[i];
|
||||||
|
is.writeInt(inst.op);
|
||||||
|
is.writeInt(inst.n);
|
||||||
|
is.writeInt(inst.r);
|
||||||
|
is.writeInt(inst.d);
|
||||||
|
}
|
||||||
|
objectFile.close();
|
||||||
|
}
|
||||||
|
catch (Exception e) {failed = true;}
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read object file into code store, setting CT
|
||||||
|
* @return true if object code read fails
|
||||||
|
*/
|
||||||
|
public boolean read() {
|
||||||
|
boolean failed = false;
|
||||||
|
try {
|
||||||
|
FileInputStream objectFile = new FileInputStream(objectFileName);
|
||||||
|
DataInputStream is = new DataInputStream(objectFile);
|
||||||
|
|
||||||
|
Machine.CT = Machine.CB;
|
||||||
|
while (is.available() > 0 && Machine.CT < Machine.PB){
|
||||||
|
Instruction inst = new Instruction();
|
||||||
|
inst.op = is.readInt();
|
||||||
|
inst.n = is.readInt();
|
||||||
|
inst.r = is.readInt();
|
||||||
|
inst.d = is.readInt();
|
||||||
|
Machine.code[Machine.CT++] = inst;
|
||||||
|
}
|
||||||
|
objectFile.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
* Example illustrating components of mJAM package
|
||||||
|
* @author prins
|
||||||
|
* @version COMP 520 V2.2
|
||||||
|
*/
|
||||||
|
package mJAM;
|
||||||
|
import mJAM.Machine.Op;
|
||||||
|
import mJAM.Machine.Reg;
|
||||||
|
import mJAM.Machine.Prim;
|
||||||
|
|
||||||
|
// test class to construct and run an mJAM program
|
||||||
|
public class Test
|
||||||
|
{
|
||||||
|
public static void main(String[] args){
|
||||||
|
|
||||||
|
Machine.initCodeGen();
|
||||||
|
System.out.println("Generating test program object code");
|
||||||
|
|
||||||
|
/* class A {
|
||||||
|
* int x;
|
||||||
|
* int p(){return x;}
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
Machine.emit(Op.LOADL,11); // hello
|
||||||
|
Machine.emit(Prim.putintnl);
|
||||||
|
int patchme_coA = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP,Reg.CB,0); // jump around methods of class A (branch to /*coA*/)
|
||||||
|
|
||||||
|
// code for p() in A
|
||||||
|
int label_pA = Machine.nextInstrAddr();
|
||||||
|
/*pA*/ Machine.emit(Op.LOAD,Reg.OB,0); // x at offset 0 in current instance of A
|
||||||
|
Machine.emit(Op.HALT,4,0,0);
|
||||||
|
Machine.emit(Op.RETURN,1,0,0); // return one value, pop zero args
|
||||||
|
|
||||||
|
// build class object for A at 0[SB]
|
||||||
|
int label_coA = Machine.nextInstrAddr();
|
||||||
|
Machine.patch(patchme_coA, label_coA);
|
||||||
|
/*coA*/ Machine.emit(Op.LOADL,-1); // no superclass object
|
||||||
|
Machine.emit(Op.LOADL,1); // number of methods
|
||||||
|
Machine.emit(Op.LOADA,Reg.CB,label_pA); // code addr of p_A
|
||||||
|
|
||||||
|
|
||||||
|
/* class B extends A {
|
||||||
|
* int y;
|
||||||
|
* int p(){return x + 22;}
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
int patchme_coB = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP,Reg.CB,0); // branch around methods in class B
|
||||||
|
|
||||||
|
// code for p() in B
|
||||||
|
int label_pB = Machine.nextInstrAddr();
|
||||||
|
/*pB*/ Machine.emit(Op.LOAD,Reg.OB,0); // x at offset 0 in current instance
|
||||||
|
Machine.emit(Op.LOADL,22);
|
||||||
|
Machine.emit(Op.HALT,4,0,0);
|
||||||
|
Machine.emit(Prim.add);
|
||||||
|
Machine.emit(Op.RETURN,1,0,0); // return one value, pop zero args
|
||||||
|
|
||||||
|
// build class object for B at 3[SB]
|
||||||
|
int label_coB = Machine.nextInstrAddr();
|
||||||
|
Machine.patch(patchme_coB, label_coB);
|
||||||
|
/*coB*/ Machine.emit(Op.LOADA,Reg.SB,0); // addr of superclass object
|
||||||
|
Machine.emit(Op.LOADL,1); // number of methods
|
||||||
|
Machine.emit(Op.LOADA,Reg.CB,label_pB); // code addr of p_B
|
||||||
|
|
||||||
|
|
||||||
|
/* class C {
|
||||||
|
* public static void main(String [] args) {
|
||||||
|
* A a = new A();
|
||||||
|
* a.x = 33;
|
||||||
|
* System.out.println(a.p());
|
||||||
|
* ...
|
||||||
|
*/
|
||||||
|
int patchme_coC = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP,Reg.CB,0); // branch around methods of class C
|
||||||
|
|
||||||
|
// code for main() in C
|
||||||
|
int label_mainC = Machine.nextInstrAddr();
|
||||||
|
/*mainC*/ Machine.emit(Op.HALT,4,0,0);
|
||||||
|
// local var "a" will be at 3[LB] after init
|
||||||
|
Machine.emit(Op.LOADA,Reg.SB,0); // class descriptor for A
|
||||||
|
Machine.emit(Op.LOADL,1); // size of A
|
||||||
|
Machine.emit(Prim.newobj); // result addr becomes value of "a"
|
||||||
|
Machine.emit(Op.LOAD,Reg.LB,3); // value of "a" (heap addr)
|
||||||
|
Machine.emit(Op.LOADL,0); // "x" is field 0 in A
|
||||||
|
Machine.emit(Op.LOADL,33); // new value 33
|
||||||
|
Machine.emit(Op.HALT,4,0,0);
|
||||||
|
Machine.emit(Prim.fieldupd); // a.x = 33
|
||||||
|
Machine.emit(Op.LOAD,Reg.LB,3); // addr of instance "a" on heap
|
||||||
|
Machine.emit(Op.CALLI,Reg.CB,label_pA); // call to known instance method p_A
|
||||||
|
Machine.emit(Prim.putintnl); // print result
|
||||||
|
|
||||||
|
/* ...
|
||||||
|
* A b = new B();
|
||||||
|
* b.x = 44;
|
||||||
|
* System.out.println(b.p());
|
||||||
|
* } // end main
|
||||||
|
* } // end class C
|
||||||
|
*/
|
||||||
|
// local var "b" will be at 4[LB] after init
|
||||||
|
Machine.emit(Op.LOADA,Reg.SB,3); // class descriptor for B
|
||||||
|
Machine.emit(Op.LOADL,2); // size of B
|
||||||
|
Machine.emit(Prim.newobj); // result addr becomes value of "b"
|
||||||
|
Machine.emit(Op.LOAD,Reg.LB,4); // fetch b
|
||||||
|
Machine.emit(Op.LOADL,0); // field 0
|
||||||
|
Machine.emit(Op.LOADL,44); // b.x = 44
|
||||||
|
Machine.emit(Prim.fieldupd);
|
||||||
|
Machine.emit(Op.HALT,4,0,0);
|
||||||
|
Machine.emit(Op.LOAD,Reg.LB,4); // addr of instance "b"
|
||||||
|
Machine.emit(Op.CALLD,0); // dynamic call, method index 0 (= method p)
|
||||||
|
Machine.emit(Prim.putintnl); // print result
|
||||||
|
Machine.emit(Op.RETURN,0,0,1); // return no value (void), pop 1 arg (= String [] args)
|
||||||
|
|
||||||
|
// build class descriptor for C at 6[SB]
|
||||||
|
int label_coC = Machine.nextInstrAddr();
|
||||||
|
Machine.patch(patchme_coC, label_coC);
|
||||||
|
/*coC*/ Machine.emit(Op.LOADL,-1); // no superclass object
|
||||||
|
Machine.emit(Op.LOADL,0); // number of methods = 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* End of class declarations - call main
|
||||||
|
*/
|
||||||
|
Machine.emit(Op.LOADL,Machine.nullRep); // put null on stack as value of main's arg
|
||||||
|
Machine.emit(Op.CALL,Reg.CB,label_mainC); // call known static main()
|
||||||
|
Machine.emit(Op.LOADL,88); // goodbye
|
||||||
|
Machine.emit(Prim.putintnl);
|
||||||
|
Machine.emit(Machine.Op.HALT,0,0,0); // halt
|
||||||
|
|
||||||
|
/* write code as an object file */
|
||||||
|
String objectCodeFileName = "test.mJAM";
|
||||||
|
ObjectFile objF = new ObjectFile(objectCodeFileName);
|
||||||
|
System.out.print("Writing object code file " + objectCodeFileName + " ... ");
|
||||||
|
if (objF.write()) {
|
||||||
|
System.out.println("FAILED!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
System.out.println("SUCCEEDED");
|
||||||
|
|
||||||
|
/* create asm file using disassembler */
|
||||||
|
String asmCodeFileName = "test.asm";
|
||||||
|
System.out.print("Writing assembly file ... ");
|
||||||
|
Disassembler d = new Disassembler(objectCodeFileName);
|
||||||
|
if (d.disassemble()) {
|
||||||
|
System.out.println("FAILED!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
System.out.println("SUCCEEDED");
|
||||||
|
|
||||||
|
/* run code */
|
||||||
|
System.out.println("Running code ... ");
|
||||||
|
Interpreter.debug(objectCodeFileName, asmCodeFileName);
|
||||||
|
// Interpreter.interpret(objectCodeFileName);
|
||||||
|
|
||||||
|
System.out.println("*** mJAM execution completed");
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package miniJava.AbstractSyntaxTrees;
|
package miniJava.AbstractSyntaxTrees;
|
||||||
|
|
||||||
|
import miniJava.CodeGenerator.RuntimeEntity;
|
||||||
import miniJava.SyntacticAnalyzer.SourcePosition;
|
import miniJava.SyntacticAnalyzer.SourcePosition;
|
||||||
|
|
||||||
public abstract class Declaration extends AST {
|
public abstract class Declaration extends AST {
|
||||||
|
@ -25,6 +26,7 @@ public abstract class Declaration extends AST {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RuntimeEntity entity;
|
||||||
public String name;
|
public String name;
|
||||||
public Type type;
|
public Type type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package miniJava.AbstractSyntaxTrees;
|
package miniJava.AbstractSyntaxTrees;
|
||||||
|
|
||||||
|
import miniJava.CodeGenerator.RuntimeEntity;
|
||||||
import miniJava.SyntacticAnalyzer.SourcePosition;
|
import miniJava.SyntacticAnalyzer.SourcePosition;
|
||||||
|
|
||||||
public abstract class Reference extends AST {
|
public abstract class Reference extends AST {
|
||||||
|
@ -13,4 +14,5 @@ public abstract class Reference extends AST {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Declaration decl;
|
public Declaration decl;
|
||||||
|
public RuntimeEntity entity;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,698 @@
|
||||||
|
package miniJava.CodeGenerator;
|
||||||
|
|
||||||
|
import mJAM.Machine;
|
||||||
|
import mJAM.Machine.*;
|
||||||
|
import miniJava.AbstractSyntaxTrees.*;
|
||||||
|
import miniJava.AbstractSyntaxTrees.Package;
|
||||||
|
import miniJava.ContextualAnalyzer.Analyzer;
|
||||||
|
|
||||||
|
public class Encoder implements Visitor<Integer, Integer> {
|
||||||
|
|
||||||
|
private int mdLBOffset = 0;
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Convenience Functions
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get size of type for declaration purposes.
|
||||||
|
* @param t
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private int getSize(Type t) {
|
||||||
|
switch(t.typeKind) {
|
||||||
|
case ARRAY:
|
||||||
|
case CLASS:
|
||||||
|
return Machine.addressSize;
|
||||||
|
case INT:
|
||||||
|
return Machine.integerSize;
|
||||||
|
case BOOLEAN:
|
||||||
|
return Machine.booleanSize;
|
||||||
|
case VOID:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// PACKAGE
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitPackage(Package prog, Integer arg) {
|
||||||
|
|
||||||
|
// Initialize static fields
|
||||||
|
for(ClassDecl cd : prog.classDeclList) {
|
||||||
|
for(int i = 0; i < cd.fieldDeclList.size(); i++) {
|
||||||
|
if(cd.fieldDeclList.get(i).isStatic) {
|
||||||
|
cd.fieldDeclList.get(i).visit(this, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int patch = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||||
|
|
||||||
|
// Initialize all classes and methods
|
||||||
|
for(ClassDecl cd : prog.classDeclList) {
|
||||||
|
cd.visit(this, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link classes & build stack
|
||||||
|
Machine.patch(patch, Machine.nextInstrAddr());
|
||||||
|
for(ClassDecl cd : prog.classDeclList) {
|
||||||
|
int cdPatch = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||||
|
Machine.patch(cdPatch, cd.visit(this, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build main function
|
||||||
|
mdLBOffset = Machine.linkDataSize;
|
||||||
|
int mainPatch = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||||
|
Analyzer.mainMethod.visit(this, 0);
|
||||||
|
|
||||||
|
|
||||||
|
// Run main function
|
||||||
|
int mainLabel = Machine.nextInstrAddr();
|
||||||
|
RuntimeEntity main = Analyzer.mainMethod.entity;
|
||||||
|
|
||||||
|
Machine.emit(Op.LOADL, Machine.nullRep);
|
||||||
|
Machine.emit(Op.CALL, Reg.CB, main.addr);
|
||||||
|
Machine.emit(Machine.Op.HALT, 0 ,0, 0);
|
||||||
|
Machine.patch(mainPatch, mainLabel);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// DECLARATIONS
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitClassDecl(ClassDecl cd, Integer init) {
|
||||||
|
|
||||||
|
/* Give an address to each method and class, so it can be referred
|
||||||
|
* to directly by jumping.
|
||||||
|
*/
|
||||||
|
if(init != 0) {
|
||||||
|
|
||||||
|
// Get size of class
|
||||||
|
int size = 0;
|
||||||
|
for(FieldDecl fd : cd.fieldDeclList) {
|
||||||
|
if(!fd.isStatic) {
|
||||||
|
fd.visit(this, size);
|
||||||
|
size += getSize(fd.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Methods
|
||||||
|
for(MethodDecl md : cd.methodDeclList) {
|
||||||
|
md.visit(this, init);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runtime Entity
|
||||||
|
int addr = Machine.nextInstrAddr();
|
||||||
|
cd.entity = new RuntimeEntity(size, addr, Reg.CB, addr);
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Instance Methods
|
||||||
|
for(MethodDecl md : cd.methodDeclList) {
|
||||||
|
if(!md.isStatic) {
|
||||||
|
md.visit(this, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build class descriptor
|
||||||
|
int label = Machine.nextInstrAddr();
|
||||||
|
Machine.patch(cd.entity.instr, label);
|
||||||
|
|
||||||
|
Machine.emit(Op.LOADL, -1); // No superclasses allowed
|
||||||
|
Machine.emit(Op.LOADL, cd.entity.size); // Size of class
|
||||||
|
Machine.emit(Op.LOADA, Reg.CB, cd.entity.addr); // Address of class
|
||||||
|
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitFieldDecl(FieldDecl fd, Integer addr) {
|
||||||
|
|
||||||
|
int size = getSize(fd.type);
|
||||||
|
|
||||||
|
// Static fields are placed onto the stack
|
||||||
|
if(fd.isStatic) {
|
||||||
|
fd.entity = new RuntimeEntity(size, addr, Reg.SB);
|
||||||
|
Machine.emit(Op.PUSH, size);
|
||||||
|
} else {
|
||||||
|
fd.entity = new RuntimeEntity(size, addr, Reg.OB);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitMethodDecl(MethodDecl md, Integer init) {
|
||||||
|
|
||||||
|
mdLBOffset = Machine.linkDataSize;
|
||||||
|
|
||||||
|
if(init != 0) {
|
||||||
|
int size = getSize(md.type);
|
||||||
|
int addr = Machine.nextInstrAddr();
|
||||||
|
md.entity = new RuntimeEntity(size, addr, Reg.CB, addr);
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
// Setup parameters
|
||||||
|
int paramS = 0;
|
||||||
|
for(ParameterDecl pd : md.parameterDeclList) {
|
||||||
|
paramS += getSize(md.type);
|
||||||
|
pd.visit(this, -paramS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup body
|
||||||
|
int addr = Machine.nextInstrAddr();
|
||||||
|
Machine.patch(md.entity.instr, addr);
|
||||||
|
for(Statement s : md.statementList) {
|
||||||
|
s.visit(this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup return statement
|
||||||
|
if(md.returnExp != null) {
|
||||||
|
md.returnExp.visit(this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return from function
|
||||||
|
// Machine.emit(Op.HALT, 4, 0, 0); (See snapshot)
|
||||||
|
Machine.emit(Op.RETURN, md.entity.size, 0, paramS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note parameter addresses are negative to LB since they are
|
||||||
|
* placed onto the stack before emitting a CALL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer visitParameterDecl(ParameterDecl pd, Integer addr) {
|
||||||
|
|
||||||
|
// Builds runtime entity, offset at LB
|
||||||
|
int size = getSize(pd.type);
|
||||||
|
pd.entity = new RuntimeEntity(size, addr, Reg.LB);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable declarations are never disposed (even in static closures).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer visitVarDecl(VarDecl decl, Integer addr) {
|
||||||
|
|
||||||
|
// Builds runtime entity, offset at LB
|
||||||
|
int size = getSize(decl.type);
|
||||||
|
decl.entity = new RuntimeEntity(size, addr, Reg.LB);
|
||||||
|
|
||||||
|
// Allocates space on stack
|
||||||
|
Machine.emit(Op.PUSH, size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TYPES
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitBaseType(BaseType type, Integer arg) {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitClassType(ClassType type, Integer arg) {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitArrayType(ArrayType type, Integer arg) {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// STATEMENTS
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We note a block statement may contain a variable declaration,
|
||||||
|
* that should go out of scope at the end of the statement.
|
||||||
|
* If the passed argument is 1, we indicate we want this to
|
||||||
|
* happen.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer visitBlockStmt(BlockStmt stmt, Integer arg) {
|
||||||
|
|
||||||
|
// Execute statements
|
||||||
|
int size = 0;
|
||||||
|
for(Statement s : stmt.sl) {
|
||||||
|
s.visit(this, 0);
|
||||||
|
|
||||||
|
// Push variable declarations if necessary
|
||||||
|
if(arg == 1 && s instanceof VarDeclStmt) {
|
||||||
|
VarDeclStmt decl = (VarDeclStmt) s;
|
||||||
|
size += getSize(decl.varDecl.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop off variable declarations
|
||||||
|
Machine.emit(Op.POP, size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We declare the variable, place the RHS onto the stack, and
|
||||||
|
* replace the value of the variable with the top of the stack.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer visitVardeclStmt(VarDeclStmt stmt, Integer arg) {
|
||||||
|
stmt.varDecl.visit(this, mdLBOffset);
|
||||||
|
stmt.initExp.visit(this, 0);
|
||||||
|
|
||||||
|
// Assign value
|
||||||
|
RuntimeEntity e = stmt.varDecl.entity;
|
||||||
|
Machine.emit(Op.STORE, e.size, e.register, e.addr);
|
||||||
|
|
||||||
|
// Update position
|
||||||
|
mdLBOffset += getSize(stmt.varDecl.type);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitAssignStmt(AssignStmt stmt, Integer arg) {
|
||||||
|
|
||||||
|
stmt.ref.visit(this, 0);
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
RuntimeEntity e = stmt.ref.entity;
|
||||||
|
|
||||||
|
// Build code accordingly
|
||||||
|
if(stmt.ref instanceof QualifiedRef) {
|
||||||
|
e.parent.load();
|
||||||
|
Machine.emit(Op.LOADL, e.addr);
|
||||||
|
stmt.val.visit(this, 0);
|
||||||
|
Machine.emit(Prim.fieldupd);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(stmt.ref instanceof IndexedRef) {
|
||||||
|
IndexedRef ref = (IndexedRef) stmt.ref;
|
||||||
|
e.load();
|
||||||
|
ref.indexExpr.visit(this, 0);
|
||||||
|
stmt.val.visit(this, 0);
|
||||||
|
Machine.emit(Prim.arrayupd);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(stmt.ref instanceof IdRef) {
|
||||||
|
stmt.val.visit(this, 0);
|
||||||
|
Machine.emit(Op.STORE, e.size, e.register, e.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method in question can either be an instance or static
|
||||||
|
* function (it must be a MethodDecl).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer visitCallStmt(CallStmt stmt, Integer arg) {
|
||||||
|
|
||||||
|
MethodDecl md = (MethodDecl) stmt.methodRef.decl;
|
||||||
|
|
||||||
|
// Request to print out
|
||||||
|
if(md == Analyzer.println) {
|
||||||
|
stmt.argList.get(0).visit(this, 0);
|
||||||
|
Machine.emit(Prim.putintnl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push parameters on (must iterate in reverse order)
|
||||||
|
for(int i = stmt.argList.size() - 1; i >= 0; i--) {
|
||||||
|
stmt.argList.get(i).visit(this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call Method
|
||||||
|
stmt.methodRef.visit(this, 0);
|
||||||
|
if(md.isStatic) {
|
||||||
|
Machine.emit(Op.CALL, Reg.CB, md.entity.addr);
|
||||||
|
} else {
|
||||||
|
stmt.methodRef.entity.call();
|
||||||
|
Machine.emit(Op.CALLI, Reg.CB, md.entity.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitIfStmt(IfStmt stmt, Integer arg) {
|
||||||
|
|
||||||
|
stmt.cond.visit(this, 0);
|
||||||
|
|
||||||
|
// Build Then Statement
|
||||||
|
int ifPatch = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMPIF, Machine.trueRep, Reg.CB, 0);
|
||||||
|
|
||||||
|
int elsePatch = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||||
|
|
||||||
|
int thenLabel = Machine.nextInstrAddr();
|
||||||
|
stmt.thenStmt.visit(this, 0);
|
||||||
|
|
||||||
|
int thenPatch = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||||
|
|
||||||
|
// Connect labels/patches
|
||||||
|
int endLabel = Machine.nextInstrAddr();
|
||||||
|
Machine.patch(elsePatch, endLabel);
|
||||||
|
|
||||||
|
if(stmt.elseStmt != null) {
|
||||||
|
stmt.elseStmt.visit(this, 0);
|
||||||
|
endLabel = Machine.nextInstrAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine.patch(ifPatch, thenLabel);
|
||||||
|
Machine.patch(thenPatch, endLabel);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We note since the same declaration can be reached multiple times, we
|
||||||
|
* are forced to pop each declaration initialized.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer visitWhileStmt(WhileStmt stmt, Integer arg) {
|
||||||
|
|
||||||
|
// Must check the condition each loop
|
||||||
|
int whileLabel = Machine.nextInstrAddr();
|
||||||
|
stmt.cond.visit(this, 0);
|
||||||
|
|
||||||
|
// Jump out once condition fails
|
||||||
|
int whileEndPatch = Machine.nextInstrAddr();
|
||||||
|
Machine.emit(Op.JUMPIF, Machine.falseRep, Reg.CB, 0);
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
stmt.body.visit(this, 1);
|
||||||
|
Machine.emit(Op.JUMP, Reg.CB, whileLabel);
|
||||||
|
Machine.patch(whileEndPatch, Machine.nextInstrAddr());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// EXPRESSIONS
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitUnaryExpr(UnaryExpr expr, Integer arg) {
|
||||||
|
|
||||||
|
expr.expr.visit(this, 0);
|
||||||
|
switch(expr.operator.spelling) {
|
||||||
|
case "!":
|
||||||
|
Machine.emit(Prim.not);
|
||||||
|
break;
|
||||||
|
case "-":
|
||||||
|
Machine.emit(Prim.neg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitBinaryExpr(BinaryExpr expr, Integer arg) {
|
||||||
|
expr.left.visit(this, 0);
|
||||||
|
expr.right.visit(this, 0);
|
||||||
|
expr.operator.visit(this, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitRefExpr(RefExpr expr, Integer arg) {
|
||||||
|
|
||||||
|
expr.ref.visit(this, 0);
|
||||||
|
|
||||||
|
// Build code accordingly
|
||||||
|
if(expr.ref instanceof QualifiedRef) {
|
||||||
|
|
||||||
|
// Must be requesting length of array
|
||||||
|
if(expr.ref.decl.type.typeKind == TypeKind.ARRAY) {
|
||||||
|
int size = Machine.integerSize;
|
||||||
|
int addr = expr.ref.entity.addr + size;
|
||||||
|
Machine.emit(Op.LOAD, size, Reg.LB, addr);
|
||||||
|
} else {
|
||||||
|
expr.ref.entity.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(expr.ref instanceof IndexedRef) {
|
||||||
|
IndexedRef ref = (IndexedRef) expr.ref;
|
||||||
|
ref.entity.load();
|
||||||
|
ref.indexExpr.visit(this, 0);
|
||||||
|
Machine.emit(Prim.arrayref);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(expr.ref instanceof ThisRef) {
|
||||||
|
RuntimeEntity e = expr.ref.entity;
|
||||||
|
Machine.emit(Op.LOADA, e.size, e.register, e.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
expr.ref.entity.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitCallExpr(CallExpr expr, Integer arg) {
|
||||||
|
|
||||||
|
// Push parameters on (must iterate in reverse order)
|
||||||
|
for(int i = expr.argList.size() - 1; i >= 0; i--) {
|
||||||
|
expr.argList.get(i).visit(this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call method
|
||||||
|
MethodDecl md = (MethodDecl) expr.functionRef.decl;
|
||||||
|
expr.functionRef.visit(this, 0);
|
||||||
|
if(md.isStatic) {
|
||||||
|
Machine.emit(Op.CALL, Reg.CB, md.entity.addr);
|
||||||
|
} else {
|
||||||
|
expr.functionRef.entity.call();
|
||||||
|
Machine.emit(Op.CALLI, Reg.CB, md.entity.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitLiteralExpr(LiteralExpr expr, Integer arg) {
|
||||||
|
|
||||||
|
expr.literal.visit(this, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitNewObjectExpr(NewObjectExpr expr, Integer arg) {
|
||||||
|
|
||||||
|
RuntimeEntity e = expr.classtype.className.decl.entity;
|
||||||
|
Machine.emit(Op.LOADA, e.register, e.addr);
|
||||||
|
Machine.emit(Op.LOADL, e.size);
|
||||||
|
Machine.emit(Prim.newobj);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the address where the new array's size is being
|
||||||
|
* stored (as it cannot be easily accessed otherwise).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer visitNewArrayExpr(NewArrayExpr expr, Integer arg) {
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
int addr = mdLBOffset;
|
||||||
|
int size = Machine.integerSize;
|
||||||
|
mdLBOffset += Machine.integerSize;
|
||||||
|
|
||||||
|
// Add to stack
|
||||||
|
expr.sizeExpr.visit(this, 0);
|
||||||
|
|
||||||
|
// Create new array
|
||||||
|
Machine.emit(Op.LOAD, size, Reg.LB, addr);
|
||||||
|
Machine.emit(Prim.newarr);
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// REFERENCES
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitQualifiedRef(QualifiedRef ref, Integer arg) {
|
||||||
|
|
||||||
|
ref.ref.visit(this, 0);
|
||||||
|
|
||||||
|
// Must be accessing length of an array
|
||||||
|
if(ref.ref.decl.type.typeKind == TypeKind.ARRAY) {
|
||||||
|
ref.decl = ref.ref.decl;
|
||||||
|
ref.entity = ref.ref.entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access class member
|
||||||
|
else {
|
||||||
|
ref.entity = ref.id.decl.entity;
|
||||||
|
ref.entity.parent = ref.ref.entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitIndexedRef(IndexedRef ref, Integer arg) {
|
||||||
|
|
||||||
|
ref.entity = ref.decl.entity;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitIdRef(IdRef ref, Integer arg) {
|
||||||
|
|
||||||
|
ref.entity = ref.decl.entity;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitThisRef(ThisRef ref, Integer arg) {
|
||||||
|
|
||||||
|
RuntimeEntity e = ref.decl.entity;
|
||||||
|
ref.entity = new RuntimeEntity(e.size, 0, Reg.OB);
|
||||||
|
ref.entity.indirect = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TERMINALS
|
||||||
|
//
|
||||||
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitIdentifier(Identifier id, Integer arg) {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitOperator(Operator op, Integer arg) {
|
||||||
|
|
||||||
|
switch(op.token.spelling) {
|
||||||
|
case "+":
|
||||||
|
Machine.emit(Prim.add);
|
||||||
|
break;
|
||||||
|
case "-":
|
||||||
|
Machine.emit(Prim.sub);
|
||||||
|
break;
|
||||||
|
case "*":
|
||||||
|
Machine.emit(Prim.mult);
|
||||||
|
break;
|
||||||
|
case "/":
|
||||||
|
Machine.emit(Prim.div);
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
Machine.emit(Prim.lt);
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
Machine.emit(Prim.gt);
|
||||||
|
break;
|
||||||
|
case "<=":
|
||||||
|
Machine.emit(Prim.le);
|
||||||
|
break;
|
||||||
|
case ">=":
|
||||||
|
Machine.emit(Prim.ge);
|
||||||
|
break;
|
||||||
|
case "==":
|
||||||
|
Machine.emit(Prim.eq);
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
Machine.emit(Prim.ne);
|
||||||
|
break;
|
||||||
|
case "&&":
|
||||||
|
Machine.emit(Prim.and);
|
||||||
|
break;
|
||||||
|
case "||":
|
||||||
|
Machine.emit(Prim.or);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitIntLiteral(IntLiteral num, Integer arg) {
|
||||||
|
|
||||||
|
Integer lit = Integer.parseInt(num.spelling);
|
||||||
|
Machine.emit(Op.LOADL, lit.intValue());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitBooleanLiteral(BooleanLiteral bool, Integer arg) {
|
||||||
|
|
||||||
|
if(bool.spelling.equals("true")) {
|
||||||
|
Machine.emit(Op.LOADL, Machine.trueRep);
|
||||||
|
} else {
|
||||||
|
Machine.emit(Op.LOADL, Machine.falseRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package miniJava.CodeGenerator;
|
||||||
|
|
||||||
|
import mJAM.Machine;
|
||||||
|
import mJAM.Machine.Op;
|
||||||
|
import mJAM.Machine.Prim;
|
||||||
|
import mJAM.Machine.Reg;
|
||||||
|
|
||||||
|
public class RuntimeEntity {
|
||||||
|
|
||||||
|
public int size = -1; // Size of type
|
||||||
|
public int addr = -1; // Position relative to register
|
||||||
|
public int instr = -1; // Instruction relative to CB
|
||||||
|
public Reg register = Reg.ZR; // Register to offset by
|
||||||
|
|
||||||
|
// For use with nested elements
|
||||||
|
public boolean indirect = false;
|
||||||
|
public RuntimeEntity parent = null;
|
||||||
|
|
||||||
|
public RuntimeEntity(int size, int addr, Reg register) {
|
||||||
|
this(size, addr, register, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RuntimeEntity(int size, int addr, Reg register, int instr) {
|
||||||
|
this.size = size;
|
||||||
|
this.addr = addr;
|
||||||
|
this.register = register;
|
||||||
|
this.instr = instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load entity into memory (if this, should call LOADA)
|
||||||
|
public void load() {
|
||||||
|
if(parent != null) {
|
||||||
|
parent.load();
|
||||||
|
Machine.emit(Op.LOADL, addr);
|
||||||
|
Machine.emit(Prim.fieldref);
|
||||||
|
} else {
|
||||||
|
if(indirect) {
|
||||||
|
Machine.emit(Op.LOADA, size, register, addr);
|
||||||
|
} else {
|
||||||
|
Machine.emit(Op.LOAD, size, register, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void call() {
|
||||||
|
if(parent != null) {
|
||||||
|
parent.load();
|
||||||
|
} else {
|
||||||
|
Machine.emit(Op.LOADA, size, Reg.OB, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,11 @@ package miniJava;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
import mJAM.*;
|
||||||
import miniJava.SyntacticAnalyzer.*;
|
import miniJava.SyntacticAnalyzer.*;
|
||||||
// import miniJava.AbstractSyntaxTrees.ASTDisplay;
|
|
||||||
import miniJava.AbstractSyntaxTrees.Package;
|
import miniJava.AbstractSyntaxTrees.Package;
|
||||||
import miniJava.ContextualAnalyzer.Analyzer;
|
import miniJava.ContextualAnalyzer.Analyzer;
|
||||||
|
import miniJava.CodeGenerator.Encoder;
|
||||||
import miniJava.Exceptions.*;
|
import miniJava.Exceptions.*;
|
||||||
|
|
||||||
public class Compiler {
|
public class Compiler {
|
||||||
|
@ -26,14 +27,27 @@ public class Compiler {
|
||||||
Parser parser = new Parser(scanner);
|
Parser parser = new Parser(scanner);
|
||||||
Package p = parser.parse();
|
Package p = parser.parse();
|
||||||
|
|
||||||
// Display
|
|
||||||
// ASTDisplay display = new ASTDisplay();
|
|
||||||
// display.showTree(p);
|
|
||||||
|
|
||||||
// Identification/Type Checking
|
// Identification/Type Checking
|
||||||
Analyzer analyzer = new Analyzer();
|
Analyzer analyzer = new Analyzer();
|
||||||
analyzer.visitPackage(p, null);
|
analyzer.visitPackage(p, null);
|
||||||
System.exit(analyzer.validate());
|
int analyzed = analyzer.validate();
|
||||||
|
|
||||||
|
// Begin Compilation to mJAM
|
||||||
|
if(analyzed == 0) {
|
||||||
|
|
||||||
|
Encoder encoder = new Encoder();
|
||||||
|
encoder.visitPackage(p, null);
|
||||||
|
|
||||||
|
// Create object file
|
||||||
|
int pos = args[0].lastIndexOf(".java");
|
||||||
|
String objectFileName = args[0].substring(0, pos) + ".mJAM";
|
||||||
|
ObjectFile objF = new ObjectFile(objectFileName);
|
||||||
|
if(objF.write()) {
|
||||||
|
System.out.println("***Object File Failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.exit(analyzed);
|
||||||
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
System.out.println("***" + e.getMessage());
|
System.out.println("***" + e.getMessage());
|
||||||
|
|
|
@ -11,10 +11,18 @@ import miniJava.AbstractSyntaxTrees.Package;
|
||||||
|
|
||||||
public class Analyzer implements Visitor<IdentificationTable, Type> {
|
public class Analyzer implements Visitor<IdentificationTable, Type> {
|
||||||
|
|
||||||
private MethodDecl mainMethod = null;
|
public static MethodDecl mainMethod = null;
|
||||||
|
public static MethodDecl println = null;
|
||||||
|
|
||||||
|
// Notifies if handling predefined as opposed to user defined code
|
||||||
|
private boolean predefinedGen = true;
|
||||||
|
|
||||||
|
// Keeps track of declarations
|
||||||
|
private VarDecl currentVarDecl = null;
|
||||||
private ClassDecl currentClassDecl = null;
|
private ClassDecl currentClassDecl = null;
|
||||||
private MethodDecl currentMethodDecl = null;
|
private MethodDecl currentMethodDecl = null;
|
||||||
private VarDecl currentVarDecl = null;
|
|
||||||
|
// Keeps track of identifcatoin
|
||||||
private IdentificationTable table = new IdentificationTable();
|
private IdentificationTable table = new IdentificationTable();
|
||||||
|
|
||||||
// Keep track of all predefined names to handle
|
// Keep track of all predefined names to handle
|
||||||
|
@ -38,6 +46,7 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
||||||
Parser parser = new Parser(scanner);
|
Parser parser = new Parser(scanner);
|
||||||
parser.parse().visit(this, table);
|
parser.parse().visit(this, table);
|
||||||
}
|
}
|
||||||
|
predefinedGen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,6 +144,10 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
||||||
|
|
||||||
public Type visitMethodDecl(MethodDecl md, IdentificationTable arg) {
|
public Type visitMethodDecl(MethodDecl md, IdentificationTable arg) {
|
||||||
|
|
||||||
|
if(predefinedGen && md.name.equals("println")) {
|
||||||
|
println = md;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if a valid entry point to program
|
// Check if a valid entry point to program
|
||||||
if (IdentificationTable.isMainMethod(md)) {
|
if (IdentificationTable.isMainMethod(md)) {
|
||||||
if (mainMethod != null)
|
if (mainMethod != null)
|
||||||
|
@ -420,8 +433,18 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
||||||
|
|
||||||
Type refType = ref.ref.visit(this, arg);
|
Type refType = ref.ref.visit(this, arg);
|
||||||
|
|
||||||
|
// Array types only have the single 'length' field
|
||||||
|
if(refType.typeKind == TypeKind.ARRAY) {
|
||||||
|
if(!ref.id.spelling.equals("length")) {
|
||||||
|
Reporter.report(ErrorType.LENGTH, ref.id, null);
|
||||||
|
return refType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BaseType(TypeKind.INT, ref.id.posn);
|
||||||
|
}
|
||||||
|
|
||||||
// Note qualified ref's only make sense in the context of classes
|
// Note qualified ref's only make sense in the context of classes
|
||||||
if(refType.typeKind != TypeKind.CLASS) {
|
else if(refType.typeKind != TypeKind.CLASS) {
|
||||||
if(refType.typeKind != TypeKind.ERROR) // Don't need to report multiple times
|
if(refType.typeKind != TypeKind.ERROR) // Don't need to report multiple times
|
||||||
Reporter.report(ErrorType.TYPE_MISMATCH, new BaseType(TypeKind.CLASS, null), refType);
|
Reporter.report(ErrorType.TYPE_MISMATCH, new BaseType(TypeKind.CLASS, null), refType);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import miniJava.AbstractSyntaxTrees.*;
|
||||||
|
|
||||||
enum ErrorType {
|
enum ErrorType {
|
||||||
THIS,
|
THIS,
|
||||||
|
LENGTH,
|
||||||
VOID_TYPE,
|
VOID_TYPE,
|
||||||
CLASS_IDENTIFER,
|
CLASS_IDENTIFER,
|
||||||
VARDECL_USED,
|
VARDECL_USED,
|
||||||
|
@ -72,9 +73,15 @@ public class Reporter {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Array types have the single field 'length'
|
||||||
|
case LENGTH: {
|
||||||
|
emit("Array types have only a single field 'length' (at " + a1.posn + ").");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Can't use a class as an identifier solely
|
// Can't use a class as an identifier solely
|
||||||
case CLASS_IDENTIFER: {
|
case CLASS_IDENTIFER: {
|
||||||
emit("Cannot use class identifier by outside of a qualified reference at " + a1.posn);
|
emit("Cannot use class identifier outside of a qualified reference at " + a1.posn);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue