Files
exercise03/MicroJava Compiler/src/ssw/mj/Interpreter.java

518 lines
14 KiB
Java

// MicroJava Virtual Machine
// -------------------------
// Syntax: java ssw.mj.Run fileName [-debug]
// ===========================================================================
// by Hanspeter Moessenboeck, 2002-10-28
// edited by Albrecht Woess, 2002-10-30
package ssw.mj;
import ssw.mj.impl.Code;
import ssw.mj.impl.Code.OpCode;
import java.io.IOException;
public class Interpreter {
private final boolean debug; // debug output on or off
private final byte[] code; // code array
private final int[] data; // global data
private final int[] heap; // dynamic heap
private final int[] stack; // expression stack
private final int[] local; // method stack
private final int startPC; // address of main() method
private int pc; // program counter
private int fp, sp; // frame pointer, stack pointer on method stack
private int esp; // expression stack pointer
private int free; // next free heap address
private static final int heapSize = 100000, // size of the heap in words
mStackSize = 4000, // size of the method stack in words
eStackSize = 30; // size of the expression stack in words
private void write(String s, int len) {
for (int i = 0; i < len; i++) {
io.write(' ');
}
for (int i = 0; i < s.length(); i++) {
io.write(s.charAt(i));
}
}
public static class BufferIO implements IO {
private final StringBuffer output;
private final String input;
private int inputPos;
public BufferIO(String input) {
output = new StringBuffer();
this.input = input;
}
@Override
public char read() {
if (inputPos >= input.length()) {
return 0;
}
return input.charAt(inputPos++);
}
@Override
public void write(char c) {
output.append(c);
}
public String getOutput() {
return output.toString();
}
}
public static final IO ConsoleIO = new IO() {
@Override
public char read() {
try {
int i = System.in.read();
if (i == -1) {
return 0;
}
return (char) i;
} catch (IOException ex) {
return 0;
}
}
@Override
public void write(char c) {
System.out.print(c);
}
};
public interface IO {
char read();
void write(char c);
}
private final IO io;
public Interpreter(byte[] code, int startPC, int dataSize, IO io, boolean debug) {
this.code = code;
this.startPC = startPC;
this.io = io;
this.debug = debug;
heap = new int[heapSize]; // fixed sized heap
data = new int[dataSize]; // global data as specified in
// classfile
stack = new int[eStackSize]; // expression stack
local = new int[mStackSize]; // method stack
fp = 0;
sp = 0;
esp = 0;
free = 1; // no block should start at address 0
}
// ----- expression stack
private void push(int val) throws IllegalStateException {
if (esp == eStackSize) {
throw new IllegalStateException("expression stack overflow");
}
stack[esp++] = val;
}
private int pop() throws IllegalStateException {
if (esp == 0) {
throw new IllegalStateException("expression stack underflow");
}
return stack[--esp];
}
// ----- method stack
private void PUSH(int val) throws IllegalStateException {
if (sp == mStackSize) {
throw new IllegalStateException("method stack overflow");
}
local[sp++] = val;
}
private int POP() throws IllegalStateException {
if (sp == 0) {
throw new IllegalStateException("method stack underflow");
}
return local[--sp];
}
// ----- instruction fetch
private byte next(boolean dbgPrint) {
byte b = code[pc++];
if (debug && dbgPrint) {
System.out.print(b + " ");
}
return b;
}
private short next2(boolean dbgPrint) {
short s = (short) (((next(false) << 8)
+ (next(false) & 0xff)) << 16 >> 16);
if (debug && dbgPrint) {
System.out.print(s + " ");
}
return s;
}
private int next4() {
return next4(true);
}
private int next4(boolean dbgPrint) {
int n = (next2(false) << 16) + (next2(false) & 0xffff);
if (debug && dbgPrint) {
System.out.print(n + " ");
}
return n;
}
/**
* Allocate heap block of size bytes
*/
private int alloc(int size) throws IllegalStateException {
int adr = free;
free += ((size + 3) >> 2); // skip to next free adr
// (>> 2 to convert byte to word)
if (free > heapSize) {
throw new IllegalStateException("heap overflow");
}
return adr;
}
/**
* Retrieve byte n from val. Byte 0 is MSB
*/
private static byte getByte(int val, int n) {
return (byte) (val << (8 * n) >>> 24);
}
/**
* Replace byte n in val by b
*/
private static int setByte(int val, int n, byte b) {
int delta = (3 - n) * 8;
int mask = ~(255 << delta); // mask all 1 except on chosen byte
int by = (b & 255) << delta;
return (val & mask) ^ by;
}
/**
* Read int from standard input stream
*/
private int readInt() {
int val = 0;
int prev = ' ';
int b = io.read();
while (b < '0' || b > '9') {
prev = b;
b = io.read();
}
while (b >= '0' && b <= '9') {
val = 10 * val + b - '0';
b = io.read();
}
if (prev == '-') {
val = -val;
}
return val;
}
private void printInstr() {
int op = code[pc - 1];
OpCode opCode = Code.OpCode.get(op);
String instr = (opCode != null) ? opCode.cleanName() : "???";
System.out.printf("%5d: %s ", pc - 1, instr);
}
private void printStack() {
for (int i = 0; i < esp; i++) {
System.out.print(stack[i] + " ");
}
System.out.println();
}
// ----- actual interpretation
public void run() throws IllegalStateException {
Code.OpCode op;
int adr, val, val2, off, idx, len, i;
pc = startPC;
if (debug) { // header for debug output
System.out.println();
System.out.println(" pos: instruction operands");
System.out.println(" | expressionstack");
System.out.println("-----------------------------");
}
for (; ; ) { // terminated by return instruction
op = Code.OpCode.get(next(false));
if (debug) {
printInstr();
}
switch (op) {
// load/store local variables
case load -> push(local[fp + next(true)]);
case load_0, load_1, load_2, load_3 -> push(local[fp + op.code() - OpCode.load_0.code()]); // mapping
// on
// range
// 0..3
case store -> local[fp + next(true)] = pop();
case store_0, store_1, store_2, store_3 -> local[fp + op.code() - OpCode.store_0.code()] = pop(); // mapping
// on
// range
// 0..3
// load/store global variables
case getstatic -> push(data[next2(true)]);
case putstatic -> data[next2(true)] = pop();
// load/store object fields
case getfield -> {
adr = pop();
if (adr == 0) {
throw new IllegalStateException("null reference used");
}
push(heap[adr + next2(true)]);
}
case putfield -> {
val = pop();
adr = pop();
if (adr == 0) {
throw new IllegalStateException("null reference used");
}
heap[adr + next2(true)] = val;
}
// load constants
case const_0, const_1, const_2, const_3, const_4, const_5 ->
push(op.code() - OpCode.const_0.code()); // map opcode to
// 0..5
case const_m1 -> push(-1);
case const_ -> push(next4());
// arithmetic operations
case add -> push(pop() + pop());
case sub -> push(-pop() + pop());
case mul -> push(pop() * pop());
case div -> {
val = pop();
if (val == 0) {
throw new IllegalStateException("division by zero");
}
push(pop() / val);
}
case rem -> {
val = pop();
if (val == 0) {
throw new IllegalStateException("division by zero");
}
push(pop() % val);
}
case neg -> push(-pop());
case shl -> {
val = pop();
push(pop() << val);
}
case shr -> {
val = pop();
push(pop() >> val);
}
case inc -> {
off = fp + next(true);
local[off] += next(true);
}
// object creation
case new_ -> push(alloc(next2(true) * 4));
case newarray -> {
val = next(true);
len = pop();
if (val == 0) {
adr = alloc(len + 4);
} else {
adr = alloc(len * 4 + 4);
}
heap[adr] = len;
push(adr + 1); // skip length field of array
}
// array access
case aload -> {
idx = pop();
adr = pop();
if (adr == 0) {
throw new IllegalStateException("null reference used");
}
len = heap[adr - 1];
if (idx < 0 || idx >= len) {
throw new IllegalStateException("index out of bounds");
}
push(heap[adr + idx]);
}
case astore -> {
val = pop();
idx = pop();
adr = pop();
if (adr == 0) {
throw new IllegalStateException("null reference used");
}
len = heap[adr - 1];
if (debug) {
System.out.println("\nArraylength = " + len);
System.out.println("Address = " + adr);
System.out.println("Index = " + idx);
System.out.println("Value = " + val);
}
if (idx < 0 || idx >= len) {
throw new IllegalStateException("index out of bounds");
}
heap[adr + idx] = val;
}
case baload -> {
idx = pop();
adr = pop();
if (adr == 0) {
throw new IllegalStateException("null reference used");
}
len = heap[adr - 1];
if (idx < 0 || idx >= len) {
throw new IllegalStateException("index out of bounds");
}
push(getByte(heap[adr + idx / 4], idx % 4));
}
case bastore -> {
val = pop();
idx = pop();
adr = pop();
if (adr == 0) {
throw new IllegalStateException("null reference used");
}
len = heap[adr - 1];
if (idx < 0 || idx >= len) {
throw new IllegalStateException("index out of bounds");
}
heap[adr + idx / 4] = setByte(heap[adr + idx / 4], idx % 4,
(byte) val);
}
case arraylength -> {
adr = pop();
if (adr == 0) {
throw new IllegalStateException("null reference used");
}
push(heap[adr - 1]);
}
// stack manipulation
case pop -> pop();
case dup -> {
val = pop();
push(val);
push(val);
}
case dup2 -> {
val = pop();
val2 = pop();
push(val2);
push(val);
push(val2);
push(val);
}
// jumps
case jmp -> {
off = next2(true);
pc += off - 3;
}
case jeq, jne, jlt, jle, jgt, jge -> {
off = next2(true);
val2 = pop();
val = pop();
boolean cond = false;
switch (op) {
case jeq -> cond = val == val2;
case jne -> cond = val != val2;
case jlt -> cond = val < val2;
case jle -> cond = val <= val2;
case jgt -> cond = val > val2;
case jge -> cond = val >= val2;
default -> {
assert false;
}
}
if (cond) {
pc += off - 3;
}
}
// method calls
case call -> {
off = next2(true);
PUSH(pc);
pc += off - 3;
}
case return_ -> {
if (sp == 0) {
return;
}
pc = POP();
}
case enter -> {
int psize = next(true);
int lsize = next(true);
PUSH(fp);
fp = sp;
for (i = 0; i < lsize; i++) {
PUSH(0);
}
assert sp == (fp + lsize);
for (i = psize - 1; i >= 0; i--) {
local[fp + i] = pop();
}
}
case exit -> {
sp = fp;
fp = POP();
}
// I/O
case read -> push(readInt());
case print -> {
len = pop();
val = pop();
String s = String.valueOf(val);
len = len - s.length();
write(s, len);
}
case bread -> push(io.read());
case bprint -> {
len = pop() - 1;
val = pop();
write(Character.toString((char) val), len);
}
case nop -> {
}
// nothing to do
case trap -> throw new IllegalStateException("trap(" + next(true) + ")");
default -> throw new IllegalStateException("wrong opcode " + op);
}
if (debug) {
System.out.println();
System.out.print(" | ");
printStack();
}
}
}
}