X-Git-Url: http://git.megacz.com/?p=org.ibex.js.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fjs%2FInterpreter.java;h=b0eb602e0eed47c8f5cb7b8429f4d3fc502e9a10;hp=efbe2a99f06e8efc94a48d798d75dadb9ab9f2e8;hb=b7d7d6f7dc1ddac7889d8334c194a96f344524e7;hpb=73131826a18c93af4fb04672bc3ec820e1197ad1 diff --git a/src/org/ibex/js/Interpreter.java b/src/org/ibex/js/Interpreter.java index efbe2a9..b0eb602 100644 --- a/src/org/ibex/js/Interpreter.java +++ b/src/org/ibex/js/Interpreter.java @@ -8,7 +8,7 @@ import org.ibex.util.*; import java.util.*; /** Encapsulates a single JS interpreter (ie call stack) */ -class Interpreter implements ByteCodes, Tokens { +class Interpreter implements ByteCodes, Tokens, Pausable { // Thread-Interpreter Mapping ///////////////////////////////////////////////////////////////////////// static Interpreter current() { return (Interpreter)threadToInterpreter.get(Thread.currentThread()); } @@ -23,7 +23,7 @@ class Interpreter implements ByteCodes, Tokens { final Stack stack = new Stack(); ///< the object stack int pc = 0; ///< the program counter - Interpreter(JSFunction f, boolean pauseable, JSArgs args) { + Interpreter(JSFunction f, boolean pauseable, JS[] args) { this.f = f; this.pausecount = pauseable ? 0 : -1; this.scope = f.parentScope; @@ -35,7 +35,7 @@ class Interpreter implements ByteCodes, Tokens { } } - Interpreter(Trap t, JS val, boolean pauseOnPut) { + Interpreter(JS.Trap t, JS val, boolean pauseOnPut) { this.pausecount = -1; try { setupTrap(t,val,new TrapMarker(null,t,val,pauseOnPut)); @@ -43,14 +43,21 @@ class Interpreter implements ByteCodes, Tokens { throw new Error("should never happen"); } } - + + private boolean get = false; + // FIXME: split this stuff out into a Script instance control object + // so it's possible to make JS either single or multi threaded. /** this is the only synchronization point we need in order to be threadsafe */ - synchronized JS resume() throws JSExn { - if(f == null) throw new RuntimeException("function already finished"); - if(scope == null) throw new RuntimeException("scope is null"); + public synchronized Object run(Object o) throws JSExn { + if (f == null) throw new AlreadyRunningException("function already finished"); + if (scope == null) throw new RuntimeException("scope is null"); + Thread t = Thread.currentThread(); Interpreter old = (Interpreter)threadToInterpreter.get(t); threadToInterpreter.put(t, this); + + if (get) stack.push(o); + try { return run(); } finally { @@ -58,6 +65,16 @@ class Interpreter implements ByteCodes, Tokens { else threadToInterpreter.put(t, old); } } + + public void pause() throws NotPausableException { + if (pausecount == -1 || f == null) throw new NotPausableException(); + pausecount++; + switch(f.op[pc]) { + case Tokens.RETURN: case ByteCodes.PUT: get = false; break; + case ByteCodes.GET: case ByteCodes.CALL: get = true; break; + default: throw new Error("should never happen"); + } + } static int getLine() { Interpreter c = Interpreter.current(); @@ -89,55 +106,55 @@ class Interpreter implements ByteCodes, Tokens { } switch(op) { case LITERAL: stack.push((JS)arg); break; - case OBJECT: stack.push(new JS.O()); break; - case ARRAY: stack.push(new JSArray(JS.toInt((JS)arg))); break; + case OBJECT: stack.push(new JS.Obj()); break; + case ARRAY: stack.push(new JSArray(Script.toInt((JS)arg))); break; //case DECLARE: scope.declare((JS)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push((JS)arg); break; - case JT: if (JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break; - case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break; - case JMP: pc += JS.toInt((JS)arg) - 1; break; + case JT: if (Script.toBoolean((JS)stack.pop())) pc += Script.toInt((JS)arg) - 1; break; + case JF: if (!Script.toBoolean((JS)stack.pop())) pc += Script.toInt((JS)arg) - 1; break; + case JMP: pc += Script.toInt((JS)arg) - 1; break; case POP: stack.pop(); break; case SWAP: stack.swap(); break; case DUP: stack.push(stack.peek()); break; case NEWSCOPE: { - int n = JS.toInt((JS)arg); + int n = Script.toInt((JS)arg); scope = new JSScope(scope,(n>>>16)&0xffff,(n>>>0)&0xffff); break; } case OLDSCOPE: scope = scope.parent; break; case GLOBALSCOPE: stack.push(scope.getGlobal()); break; case SCOPEGET: stack.push(scope.get((JS)arg)); break; - case SCOPEPUT: scope.put((JS)arg,stack.peek()); break; - case ASSERT: if (!JS.toBoolean(stack.pop())) throw je("ibex.assertion.failed"); break; - case BITNOT: stack.push(JS.N(~JS.toLong(stack.pop()))); break; - case BANG: stack.push(JS.B(!JS.toBoolean(stack.pop()))); break; + case SCOPEPUT: scope.put((JS)arg, (JS)stack.peek()); break; + case ASSERT: if (!Script.toBoolean((JS)stack.pop())) throw je("ibex.assertion.failed"); break; + case BITNOT: stack.push(Script.N(~Script.toLong((JS)stack.pop()))); break; + case BANG: stack.push(Script.B(!Script.toBoolean((JS)stack.pop()))); break; case NEWFUNCTION: stack.push(((JSFunction)arg)._cloneWithNewParentScope(scope)); break; case LABEL: break; case TYPEOF: { Object o = stack.pop(); if (o == null) stack.push(null); - else if (o instanceof JSString) stack.push(JS.S("string")); - else if (o instanceof JSNumber.B) stack.push(JS.S("boolean")); - else if (o instanceof JSNumber) stack.push(JS.S("number")); - else stack.push(JS.S("object")); + else if (o instanceof JSString) stack.push(Script.S("string")); + else if (o instanceof JSNumber.B) stack.push(Script.S("boolean")); + else if (o instanceof JSNumber) stack.push(Script.S("number")); + else stack.push(Script.S("object")); break; } case PUSHKEYS: { - JS o = stack.peek(); + JS o = (JS)stack.peek(); stack.push(o == null ? null : o.keys()); break; } case LOOP: - stack.push(new LoopMarker(pc, pc > 0 && f.op[pc - 1] == LABEL ? (String)f.arg[pc - 1] : (String)null, scope)); - stack.push(JS.T); + stack.push(new LoopMarker(pc, (String)(pc > 0 && f.op[pc - 1] == LABEL ? f.arg[pc - 1] : null), scope)); + stack.push(Script.T); break; case BREAK: case CONTINUE: while(!stack.empty()) { - JS o = stack.pop(); + JS o = (JS)stack.pop(); if (o instanceof CallMarker) je("break or continue not within a loop"); if (o instanceof TryMarker) { if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going @@ -149,9 +166,9 @@ class Interpreter implements ByteCodes, Tokens { if (o instanceof LoopMarker) { if (arg == null || arg.equals(((LoopMarker)o).label)) { int loopInstructionLocation = ((LoopMarker)o).location; - int endOfLoop = JS.toInt((JS)f.arg[loopInstructionLocation]) + loopInstructionLocation; + int endOfLoop = Script.toInt((JS)f.arg[loopInstructionLocation]) + loopInstructionLocation; scope = ((LoopMarker)o).scope; - if (op == CONTINUE) { stack.push(o); stack.push(JS.F); } + if (op == CONTINUE) { stack.push(o); stack.push(Script.F); } pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation; continue OUTER; } @@ -169,7 +186,7 @@ class Interpreter implements ByteCodes, Tokens { } case RETURN: { - JS retval = stack.pop(); + JS retval = (JS)stack.pop(); while(!stack.empty()) { Object o = stack.pop(); if (o instanceof TryMarker) { @@ -183,21 +200,21 @@ class Interpreter implements ByteCodes, Tokens { boolean didTrapPut = false; if (o instanceof TrapMarker) { // handles return component of a write trap TrapMarker tm = (TrapMarker) o; - boolean cascade = tm.t.isWriteTrap() && !tm.cascadeHappened && !JS.toBoolean(retval); + boolean cascade = tm.t.isWriteTrap() && !tm.cascadeHappened && !Script.toBoolean(retval); if(cascade) { - Trap t = tm.t.nextWriteTrap(); - if(t == null && tm.t.target instanceof JS.Clone) { - t = ((JS.Clone)tm.t.target).clonee.getTrap(tm.t.key); - if(t != null) t = t.writeTrap(); + JS.Trap t = tm.t.nextWrite(); + if(t == null && tm.t.target() instanceof JS.Clone) { + t = ((JS.Clone)tm.t.target()).clonee.getTrap(tm.t.key()); + if(t != null && !t.isWriteTrap()) t = t.nextWrite(); } if(t != null) { tm.t = t; // we reuse the old trap marker - setupTrap(t,tm.val,tm); + setupTrap(t, tm.val, tm); pc--; // we increment it on the next iter continue OUTER; } else { didTrapPut = true; - if(!tm.pauseOnPut) tm.t.target.put(tm.t.key,tm.val); + if(!tm.pauseOnPut) tm.t.target().put(tm.t.key(), tm.val); } } } @@ -219,20 +236,20 @@ class Interpreter implements ByteCodes, Tokens { } case CASCADE: { - boolean write = JS.toBoolean((JS)arg); - JS val = write ? stack.pop() : null; + boolean write = Script.toBoolean((JS)arg); + JS val = write ? (JS)stack.pop() : null; CallMarker o = stack.findCall(); if(!(o instanceof TrapMarker)) throw new JSExn("tried to CASCADE while not in a trap"); TrapMarker tm = (TrapMarker) o; - JS key = tm.t.key; - JS target = tm.t.target; + JS key = tm.t.key(); + JS target = tm.t.target(); if(tm.t.isWriteTrap() != write) throw new JSExn("tried to do a " + (write?"write":"read") + " cascade in a " + (write?"read":"write") + " trap"); - Trap t = write ? tm.t.nextWriteTrap() : tm.t.nextReadTrap(); + JS.Trap t = write ? tm.t.nextWrite() : tm.t.nextRead(); // FIXME: Doesn't handle multiple levels of clone's (probably can just make this a while loop) if(t == null && target instanceof JS.Clone) { target = ((JS.Clone)target).clonee; t = target.getTrap(key); - if(t != null) t = write ? t.writeTrap() : t.readTrap(); + if(t != null) t = write ? t.write() : t.read(); } if(write) { tm.cascadeHappened = true; @@ -247,8 +264,8 @@ class Interpreter implements ByteCodes, Tokens { target.put(key,val); } else { JS ret = target.get(key); - if (ret == JS.METHOD) ret = new Stub(target, key); - stack.push(ret); + if (ret != null && ret instanceof JS.Method) ret = new Stub(target, key); + stack.push(ret); } if (pausecount > initialPauseCount) { pc++; return null; } // we were paused } @@ -256,19 +273,19 @@ class Interpreter implements ByteCodes, Tokens { } case PUT: { - JS val = stack.pop(); - JS key = stack.pop(); - JS target = stack.peek(); - if (target == null) throw je("tried to put " + JS.debugToString(val) + " to the " + JS.debugToString(key) + " property on the null value"); - if (key == null) throw je("tried to assign \"" + JS.debugToString(val) + "\" to the null key"); + JS val = (JS)stack.pop(); + JS key = (JS)stack.pop(); + JS target = (JS)stack.peek(); + if (target == null) throw je("tried to put " + Script.str(val) + " to the " + Script.str(key) + " property on the null value"); + if (key == null) throw je("tried to assign \"" + Script.str(val) + "\" to the null key"); - Trap t = target.getTrap(key); - if(t != null) t = t.writeTrap(); + JS.Trap t = target.getTrap(key); + if(t != null) t = t.write(); if(t == null && target instanceof JS.Clone) { target = ((JS.Clone)target).clonee; t = target.getTrap(key); - if(t != null) t = t.writeTrap(); + if(t != null) t = t.nextWrite(); } stack.push(val); @@ -287,24 +304,24 @@ class Interpreter implements ByteCodes, Tokens { case GET_PRESERVE: { JS target, key; if (op == GET) { - key = arg == null ? stack.pop() : (JS)arg; - target = stack.pop(); + key = arg == null ? (JS)stack.pop() : (JS)arg; + target = (JS)stack.pop(); } else { - key = stack.pop(); - target = stack.peek(); + key = (JS)stack.pop(); + target = (JS)stack.peek(); stack.push(key); } JS ret = null; - if (key == null) throw je("tried to get the null key from " + JS.debugToString(target)); - if (target == null) throw je("tried to get property \"" + JS.debugToString(key) + "\" from the null object"); + if (key == null) throw je("tried to get the null key from " + Script.str(target)); + if (target == null) throw je("tried to get property \"" + Script.str(key) + "\" from the null object"); - Trap t = target.getTrap(key); - if(t != null) t = t.readTrap(); + JS.Trap t = target.getTrap(key); + if(t != null) t = t.read(); if(t == null && target instanceof JS.Clone) { target = ((JS.Clone)target).clonee; t = target.getTrap(key); - if(t != null) t = t.readTrap(); + if(t != null) t = t.nextRead(); } if(t != null) { @@ -313,33 +330,34 @@ class Interpreter implements ByteCodes, Tokens { } else { ret = target.get(key); if (pausecount > initialPauseCount) { pc++; return null; } // we were paused - if (ret == JS.METHOD) ret = new Stub(target, key); + + if (ret != null && ret instanceof JS.Method) ret = new Stub(target, key); stack.push(ret); } break; } case CALL: case CALLMETHOD: { - int numArgs = JS.toInt((JS)arg); - - JS[] rest = numArgs > 3 ? new JS[numArgs - 3] : null; - for(int i=numArgs - 1; i>2; i--) rest[i-3] = stack.pop(); - JS a2 = numArgs <= 2 ? null : stack.pop(); - JS a1 = numArgs <= 1 ? null : stack.pop(); - JS a0 = numArgs <= 0 ? null : stack.pop(); - + // FIXME: can a lot of simple cases in Parser be + // reduced so creating a new JS[] is not necessary? + JS[] jsargs; + if (arg instanceof JSNumber.I) { + jsargs = new JS[((JSNumber.I)arg).toInt()]; + for (int i=0; i < jsargs.length; i++) jsargs[i] = (JS)stack.pop(); + } else jsargs = (JS[])arg; + JS method = null; JS ret = null; - JS object = stack.pop(); + JS object = (JS)stack.pop(); if (op == CALLMETHOD) { - if (object == JS.METHOD) { - method = stack.pop(); - object = stack.pop(); - } else if (object == null) { - method = stack.pop(); - object = stack.pop(); - throw new JSExn("function '"+JS.debugToString(method)+"' not found in " + object.getClass().getName()); + if (object == null) { + method = (JS)stack.pop(); + object = (JS)stack.pop(); + throw new JSExn("function '"+Script.str(method)+"' not found in " + object.getClass().getName()); + } else if (object instanceof JS.Method) { + method = (JS)stack.pop(); + object = (JS)stack.pop(); } else { stack.pop(); stack.pop(); @@ -348,14 +366,14 @@ class Interpreter implements ByteCodes, Tokens { if (object instanceof JSFunction) { stack.push(new CallMarker(this)); - stack.push(new JSArgs(a0,a1,a2,rest,numArgs,object)); + stack.push(jsargs); f = (JSFunction)object; scope = f.parentScope; pc = -1; break; } else { JS c = (JS)object; - ret = method == null ? c.call(a0, a1, a2, rest, numArgs) : c.callMethod(method, a0, a1, a2, rest, numArgs); + ret = method == null ? c.call(jsargs) : c.call(method, jsargs); } if (pausecount > initialPauseCount) { pc++; return null; } @@ -364,21 +382,21 @@ class Interpreter implements ByteCodes, Tokens { } case THROW: - throw new JSExn(stack.pop(), this); + throw new JSExn((JS)stack.pop(), this); /* FIXME GRAMMAR case MAKE_GRAMMAR: { final Grammar r = (Grammar)arg; final JSScope final_scope = scope; Grammar r2 = new Grammar() { - public int match(String s, int start, Hash v, JSScope scope) throws JSExn { + public int match(String s, int start, Map v, JSScope scope) throws JSExn { return r.match(s, start, v, final_scope); } - public int matchAndWrite(String s, int start, Hash v, JSScope scope, String key) throws JSExn { + public int matchAndWrite(String s, int start, Map v, JSScope scope, String key) throws JSExn { return r.matchAndWrite(s, start, v, final_scope, key); } public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { - Hash v = new Hash(); + Map v = new Map(); r.matchAndWrite((String)a0, 0, v, final_scope, "foo"); return v.get("foo"); } @@ -390,13 +408,13 @@ class Interpreter implements ByteCodes, Tokens { } */ case ADD_TRAP: case DEL_TRAP: { - JS val = stack.pop(); - JS key = stack.pop(); - JS js = stack.peek(); + JS val = (JS)stack.pop(); + JS key = (JS)stack.pop(); + JS js = (JS)stack.peek(); // A trap addition/removal - if(!(val instanceof JSFunction)) throw new JSExn("tried to add/remove a non-function trap"); - if(op == ADD_TRAP) js.addTrap(key, (JSFunction)val); - else js.delTrap(key, (JSFunction)val); + if(!(val instanceof JSFunction)) throw new JSExn("tried to add/remove a non-function trap"); // FIXME + if(op == ADD_TRAP) js.addTrap(key, val); + else js.delTrap(key, val); break; } @@ -405,45 +423,45 @@ class Interpreter implements ByteCodes, Tokens { if(count < 2) throw new Error("this should never happen"); if(count == 2) { // common case - JS right = stack.pop(); - JS left = stack.pop(); + JS right = (JS)stack.pop(); + JS left = (JS)stack.pop(); JS ret; if(left instanceof JSString || right instanceof JSString) - ret = JS.S(JS.toString(left).concat(JS.toString(right))); + ret = Script.S(Script.toString(left).concat(Script.toString(right))); else if(left instanceof JSNumber.D || right instanceof JSNumber.D) - ret = JS.N(JS.toDouble(left) + JS.toDouble(right)); + ret = Script.N(Script.toDouble(left) + Script.toDouble(right)); else { - long l = JS.toLong(left) + JS.toLong(right); - if(l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) ret = JS.N(l); - ret = JS.N((int)l); + long l = Script.toLong(left) + Script.toLong(right); + if(l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) ret = Script.N(l); + ret = Script.N((int)l); } stack.push(ret); } else { JS[] args = new JS[count]; - while(--count >= 0) args[count] = stack.pop(); + while(--count >= 0) args[count] = (JS)stack.pop(); if(args[0] instanceof JSString) { StringBuffer sb = new StringBuffer(64); - for(int i=0;i> JS.toLong(right))); break; - case URSH: stack.push(JS.N(JS.toLong(left) >>> JS.toLong(right))); break; + case LSH: stack.push(Script.N(Script.toLong(left) << Script.toLong(right))); break; + case RSH: stack.push(Script.N(Script.toLong(left) >> Script.toLong(right))); break; + case URSH: stack.push(Script.N(Script.toLong(left) >>> Script.toLong(right))); break; //#repeat />= LT/LE/GT/GE case LT: { if(left instanceof JSString && right instanceof JSString) - stack.push(JS.B(JS.toString(left).compareTo(JS.toString(right)) < 0)); + stack.push(Script.B(Script.toString(left).compareTo(Script.toString(right)) < 0)); else - stack.push(JS.B(JS.toDouble(left) < JS.toDouble(right))); + stack.push(Script.B(Script.toDouble(left) < Script.toDouble(right))); } //#end @@ -482,8 +500,8 @@ class Interpreter implements ByteCodes, Tokens { boolean ret; if(left == null && right == null) ret = true; else if(left == null || right == null) ret = false; - else ret = left.jsequals(right); - stack.push(JS.B(op == EQ ? ret : !ret)); break; + else ret = left.equals(right); + stack.push(Script.B(op == EQ ? ret : !ret)); break; } default: throw new Error("unknown opcode " + op); @@ -503,11 +521,11 @@ class Interpreter implements ByteCodes, Tokens { */ void catchException(JSExn e) throws JSExn { while(!stack.empty()) { - JS o = stack.pop(); + Object o = stack.pop(); if (o instanceof CatchMarker || o instanceof TryMarker) { boolean inCatch = o instanceof CatchMarker; if(inCatch) { - o = stack.pop(); + o = (JS)stack.pop(); if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going } if(!inCatch && ((TryMarker)o).catchLoc >= 0) { @@ -531,10 +549,10 @@ class Interpreter implements ByteCodes, Tokens { throw e; } - void setupTrap(Trap t, JS val, CallMarker cm) throws JSExn { + void setupTrap(JS.Trap t, JS val, CallMarker cm) throws JSExn { stack.push(cm); - stack.push(new TrapArgs(t,val)); - f = t.f; + stack.push(new TrapArgs(t, val)); + f = (JSFunction)t.function(); // FIXME scope = f.parentScope; pc = 0; } @@ -542,12 +560,7 @@ class Interpreter implements ByteCodes, Tokens { // Markers ////////////////////////////////////////////////////////////////////// - static class Marker extends JS { - public JS get(JS key) throws JSExn { throw new Error("this should not be accessible from a script"); } - public void put(JS key, JS val) throws JSExn { throw new Error("this should not be accessible from a script"); } - public String coerceToString() { throw new Error("this should not be accessible from a script"); } - public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { throw new Error("this should not be accessible from a script"); } - } + static class Marker {} static class CallMarker extends Marker { final int pc; @@ -561,12 +574,12 @@ class Interpreter implements ByteCodes, Tokens { } static class TrapMarker extends CallMarker { - Trap t; + JS.Trap t; JS val; boolean cascadeHappened; final boolean pauseOnPut; - public TrapMarker(Interpreter cx, Trap t, JS val) { this(cx,t,val,false); } - public TrapMarker(Interpreter cx, Trap t, JS val, boolean pauseOnPut) { + public TrapMarker(Interpreter cx, JS.Trap t, JS val) { this(cx,t,val,false); } + public TrapMarker(Interpreter cx, JS.Trap t, JS val, boolean pauseOnPut) { super(cx); this.t = t; this.val = val; @@ -575,7 +588,7 @@ class Interpreter implements ByteCodes, Tokens { } static class CatchMarker extends Marker { } - private static CatchMarker catchMarker = new CatchMarker(); + private static final CatchMarker catchMarker = new CatchMarker(); static class LoopMarker extends Marker { final public int location; @@ -608,77 +621,41 @@ class Interpreter implements ByteCodes, Tokens { public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg = null; } // Just throw this exn } - static class TrapArgs extends JS { + static class TrapArgs extends JS.Immutable { private Trap t; private JS val; public TrapArgs(Trap t, JS val) { this.t = t; this.val = val; } public JS get(JS key) throws JSExn { - if(JS.isInt(key) && JS.toInt(key) == 0) return val; - //#switch(JS.toString(key)) - case "trapee": return t.target; - case "callee": return t.f; - case "trapname": return t.key; - case "length": return t.isWriteTrap() ? ONE : ZERO; + if(Script.isInt(key) && Script.toInt(key) == 0) return val; + //#switch(Script.str(key)) + case "trapee": return t.target(); + case "callee": return t.function(); + case "trapname": return t.key(); + case "length": return t.isWriteTrap() ? Script.ONE : Script.ZERO; //#end return super.get(key); } } - static class JSArgs extends JS { - private final JS a0; - private final JS a1; - private final JS a2; - private final JS[] rest; - private final int nargs; - private final JS callee; - - public JSArgs(JS callee) { this(null,null,null,null,0,callee); } - public JSArgs(JS a0, JS callee) { this(a0,null,null,null,1,callee); } - public JSArgs(JS a0, JS a1, JS a2, JS[] rest, int nargs, JS callee) { - this.a0 = a0; this.a1 = a1; this.a2 = a2; - this.rest = rest; this.nargs = nargs; - this.callee = callee; - } - - public JS get(JS key) throws JSExn { - if(JS.isInt(key)) { - int n = JS.toInt(key); - switch(n) { - case 0: return a0; - case 1: return a1; - case 2: return a2; - default: return n>= 0 && n < nargs ? rest[n-3] : null; - } - } - //#switch(JS.toString(key)) - case "callee": return callee; - case "length": return JS.N(nargs); - //#end - return super.get(key); - } - } - - static class Stub extends JS { + static class Stub extends JS.Immutable { private JS method; JS obj; public Stub(JS obj, JS method) { this.obj = obj; this.method = method; } - public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn { - return ((JS)obj).callMethod(method, a0, a1, a2, rest, nargs); - } + public JS call(JS[] args) throws JSExn { return obj.call(method, args); } } - static class Stack { + static final class Stack { private static final int MAX_STACK_SIZE = 512; - private JS[] stack = new JS[64]; + private Object[] stack = new Object[8]; private int sp = 0; boolean empty() { return sp == 0; } - void push(JS o) throws JSExn { if(sp == stack.length) grow(); stack[sp++] = o; } - JS peek() { if(sp == 0) throw new RuntimeException("Stack underflow"); return stack[sp-1]; } - final JS pop() { if(sp == 0) throw new RuntimeException("Stack underflow"); return stack[--sp]; } + void push(Object o) throws JSExn { if(sp == stack.length) grow(); stack[sp++] = o; } + Object peek() { if(sp == 0) throw new RuntimeException("stack underflow"); return stack[sp-1]; } + final Object pop() { if(sp == 0) throw new RuntimeException("stack underflow"); return stack[--sp]; } void swap() throws JSExn { if(sp < 2) throw new JSExn("stack overflow"); - JS tmp = stack[sp-2]; + Object tmp = stack[sp-2]; stack[sp-2] = stack[sp-1]; stack[sp-1] = tmp; } @@ -687,9 +664,10 @@ class Interpreter implements ByteCodes, Tokens { return null; } void grow() throws JSExn { - if(stack.length >= MAX_STACK_SIZE) throw new JSExn("Stack overflow"); - JS[] stack2 = new JS[stack.length * 2]; + if(stack.length >= MAX_STACK_SIZE) throw new JSExn("stack overflow"); + Object[] stack2 = new Object[stack.length * 2]; System.arraycopy(stack,0,stack2,0,stack.length); + stack = stack2; } void backtrace(JSExn e) { @@ -699,7 +677,7 @@ class Interpreter implements ByteCodes, Tokens { if(cm.f == null) break; String s = cm.f.sourceName + ":" + cm.f.line[cm.pc-1]; if(cm instanceof Interpreter.TrapMarker) - s += " (trap on " + JS.debugToString(((Interpreter.TrapMarker)cm).t.key) + ")"; + s += " (trap on " + Script.str(((Interpreter.TrapMarker)cm).t.key()) + ")"; e.addBacktrace(s); } }