initial commit, template added
This commit is contained in:
517
MicroJava Compiler/src/ssw/mj/Interpreter.java
Normal file
517
MicroJava Compiler/src/ssw/mj/Interpreter.java
Normal file
@@ -0,0 +1,517 @@
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user