1
Fork 0

Code generation / compilation complete.

master
Joshua Potter 2014-04-15 15:32:46 -04:00
parent 65e5c05c70
commit ab84203b61
15 changed files with 2508 additions and 83 deletions

View File

@ -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);
}
}
}
}

5
src/mJAM/Assembler.java Normal file
View File

@ -0,0 +1,5 @@
package mJAM;
public class Assembler {
// TBD
}

309
src/mJAM/Disassembler.java Normal file
View File

@ -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;
}
}

36
src/mJAM/Instruction.java Normal file
View File

@ -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
}

888
src/mJAM/Interpreter.java Normal file
View File

@ -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();
}
}

232
src/mJAM/Machine.java Normal file
View File

@ -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;
}

70
src/mJAM/ObjectFile.java Normal file
View File

@ -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;
}
}

158
src/mJAM/Test.java Normal file
View File

@ -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");
}
}

View File

@ -5,6 +5,7 @@
*/
package miniJava.AbstractSyntaxTrees;
import miniJava.CodeGenerator.RuntimeEntity;
import miniJava.SyntacticAnalyzer.SourcePosition;
public abstract class Declaration extends AST {
@ -25,6 +26,7 @@ public abstract class Declaration extends AST {
}
}
public RuntimeEntity entity;
public String name;
public Type type;
}

View File

@ -5,6 +5,7 @@
*/
package miniJava.AbstractSyntaxTrees;
import miniJava.CodeGenerator.RuntimeEntity;
import miniJava.SyntacticAnalyzer.SourcePosition;
public abstract class Reference extends AST {
@ -13,4 +14,5 @@ public abstract class Reference extends AST {
}
public Declaration decl;
public RuntimeEntity entity;
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -2,10 +2,11 @@ package miniJava;
import java.io.*;
import mJAM.*;
import miniJava.SyntacticAnalyzer.*;
// import miniJava.AbstractSyntaxTrees.ASTDisplay;
import miniJava.AbstractSyntaxTrees.Package;
import miniJava.ContextualAnalyzer.Analyzer;
import miniJava.CodeGenerator.Encoder;
import miniJava.Exceptions.*;
public class Compiler {
@ -26,14 +27,27 @@ public class Compiler {
Parser parser = new Parser(scanner);
Package p = parser.parse();
// Display
// ASTDisplay display = new ASTDisplay();
// display.showTree(p);
// Identification/Type Checking
Analyzer analyzer = new Analyzer();
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) {
System.out.println("***" + e.getMessage());

View File

@ -11,10 +11,18 @@ import miniJava.AbstractSyntaxTrees.Package;
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 MethodDecl currentMethodDecl = null;
private VarDecl currentVarDecl = null;
// Keeps track of identifcatoin
private IdentificationTable table = new IdentificationTable();
// 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.parse().visit(this, table);
}
predefinedGen = false;
}
/**
@ -135,6 +144,10 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
public Type visitMethodDecl(MethodDecl md, IdentificationTable arg) {
if(predefinedGen && md.name.equals("println")) {
println = md;
}
// Check if a valid entry point to program
if (IdentificationTable.isMainMethod(md)) {
if (mainMethod != null)
@ -420,8 +433,18 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
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
if(refType.typeKind != TypeKind.CLASS) {
else if(refType.typeKind != TypeKind.CLASS) {
if(refType.typeKind != TypeKind.ERROR) // Don't need to report multiple times
Reporter.report(ErrorType.TYPE_MISMATCH, new BaseType(TypeKind.CLASS, null), refType);

View File

@ -4,6 +4,7 @@ import miniJava.AbstractSyntaxTrees.*;
enum ErrorType {
THIS,
LENGTH,
VOID_TYPE,
CLASS_IDENTIFER,
VARDECL_USED,
@ -72,9 +73,15 @@ public class Reporter {
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
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;
}