X-Git-Url: http://git.megacz.com/?p=org.ibex.js.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fjs%2FInterpreter.java;h=2b4e8887fb0f31ccef244f8cb3eaf549529ec4e9;hp=80de88d1ac23ef30ae2a51d5fbba48f8f235c839;hb=HEAD;hpb=7113d47d1ef227732b610026bee8c22b9ada3525 diff --git a/src/org/ibex/js/Interpreter.java b/src/org/ibex/js/Interpreter.java index 80de88d..2b4e888 100644 --- a/src/org/ibex/js/Interpreter.java +++ b/src/org/ibex/js/Interpreter.java @@ -29,7 +29,7 @@ class Interpreter implements ByteCodes, Tokens, Pausable { this.scope = f.parentScope; try { stack.push(new CallMarker(null)); // the "root function returned" marker -- f==null - stack.push(args); + stack.push(new JSArgs(args, f)); // FIXME: temprorary bug fix } catch(JSExn e) { throw new Error("should never happen"); } @@ -71,8 +71,8 @@ class Interpreter implements ByteCodes, Tokens, Pausable { 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"); + case ByteCodes.GET: case ByteCodes.GET_PRESERVE: case ByteCodes.CALLMETHOD: case ByteCodes.CALL: get = true; break; + default: throw new Error("paused on unexpected bytecode: " + f.op[pc]); } } @@ -107,36 +107,41 @@ class Interpreter implements ByteCodes, Tokens, Pausable { switch(op) { case LITERAL: stack.push((JS)arg); break; case OBJECT: stack.push(new JS.Obj()); break; - case ARRAY: stack.push(new JSArray(Script.toInt((JS)arg))); break; + case ARRAY: stack.push(new JSArray(JSU.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 (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 JT: if (JSU.toBoolean((JS)stack.pop())) pc += JSU.toInt((JS)arg) - 1; break; + case JF: if (!JSU.toBoolean((JS)stack.pop())) pc += JSU.toInt((JS)arg) - 1; break; + case JMP: pc += JSU.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 = Script.toInt((JS)arg); + int n = JSU.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, (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 SCOPEPUT: { + // FIXME: HACK: share this around more and find the callee. + Object val = stack.peek(); + if (val != null && val instanceof JS[]) val = new JSArgs((JS[])val, null); + scope.put((JS)arg, (JS)val); break; + } + case ASSERT: if (!JSU.toBoolean((JS)stack.pop())) throw je("ibex.assertion.failed"); break; + case BITNOT: stack.push(JSU.N(~JSU.toLong((JS)stack.pop()))); break; + case BANG: stack.push(JSU.B(!JSU.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(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")); + else if (o instanceof JSString) stack.push(JSU.S("string")); + else if (o instanceof JSNumber.B) stack.push(JSU.S("boolean")); + else if (o instanceof JSNumber) stack.push(JSU.S("number")); + else stack.push(JSU.S("object")); break; } @@ -148,13 +153,13 @@ class Interpreter implements ByteCodes, Tokens, Pausable { case LOOP: stack.push(new LoopMarker(pc, (String)(pc > 0 && f.op[pc - 1] == LABEL ? f.arg[pc - 1] : null), scope)); - stack.push(Script.T); + stack.push(JSU.T); break; case BREAK: case CONTINUE: while(!stack.empty()) { - JS o = (JS)stack.pop(); + Object o = 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 @@ -166,9 +171,9 @@ class Interpreter implements ByteCodes, Tokens, Pausable { if (o instanceof LoopMarker) { if (arg == null || arg.equals(((LoopMarker)o).label)) { int loopInstructionLocation = ((LoopMarker)o).location; - int endOfLoop = Script.toInt((JS)f.arg[loopInstructionLocation]) + loopInstructionLocation; + int endOfLoop = JSU.toInt((JS)f.arg[loopInstructionLocation]) + loopInstructionLocation; scope = ((LoopMarker)o).scope; - if (op == CONTINUE) { stack.push(o); stack.push(Script.F); } + if (op == CONTINUE) { stack.push(o); stack.push(JSU.F); } pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation; continue OUTER; } @@ -200,7 +205,7 @@ class Interpreter implements ByteCodes, Tokens, Pausable { 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 && !Script.toBoolean(retval); + boolean cascade = tm.t.isWriteTrap() && !tm.cascadeHappened && !JSU.toBoolean(retval); if(cascade) { JS.Trap t = tm.t.nextWrite(); if(t == null && tm.t.target() instanceof JS.Clone) { @@ -236,17 +241,17 @@ class Interpreter implements ByteCodes, Tokens, Pausable { } case CASCADE: { - boolean write = Script.toBoolean((JS)arg); + boolean write = JSU.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(); - if(tm.t.isWriteTrap() != write) throw new JSExn("tried to do a " + (write?"write":"read") + " cascade in a " + (write?"read":"write") + " trap"); + if(tm.t.isWriteTrap() != write) + throw new JSExn("tried to do a "+(write?"write":"read") + " cascade in a " + (write?"read":"write") + " trap"); 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) { + while (t == null && target instanceof JS.Clone) { target = ((JS.Clone)target).clonee; t = target.getTrap(key); if(t != null) t = write ? t.write() : t.read(); @@ -276,8 +281,8 @@ class Interpreter implements ByteCodes, Tokens, Pausable { 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"); + if (target == null) throw je("tried to put " + JSU.str(val) + " to the " + JSU.str(key) + " property on the null value"); + if (key == null) throw je("tried to assign \"" + JSU.str(val) + "\" to the null key"); JS.Trap t = target.getTrap(key); if(t != null) t = t.write(); @@ -312,10 +317,11 @@ class Interpreter implements ByteCodes, Tokens, Pausable { stack.push(key); } JS ret = null; - 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"); + if (key == null) throw je("tried to get the null key from " + JSU.str(target)); + if (target == null) throw je("tried to get property \"" + JSU.str(key) + "\" from the null object"); - JS.Trap t = target.getTrap(key); + JS.Trap t = null; + try { t = target.getTrap(key); } catch (JSExn e) {} if(t != null) t = t.read(); if(t == null && target instanceof JS.Clone) { @@ -338,7 +344,12 @@ class Interpreter implements ByteCodes, Tokens, Pausable { } case CALL: case CALLMETHOD: { - JS[] jsargs = (JS[])arg; + JS[] jsargs; + if (arg instanceof JSNumber.I) { + // FIXME: we should be able to recycle JS[]'s here + 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; @@ -348,7 +359,7 @@ class Interpreter implements ByteCodes, Tokens, Pausable { if (object == null) { method = (JS)stack.pop(); object = (JS)stack.pop(); - throw new JSExn("function '"+Script.str(method)+"' not found in " + object.getClass().getName()); + throw new JSExn("function '"+JSU.str(method)+"' not found in " + object.getClass().getName()); } else if (object instanceof JS.Method) { method = (JS)stack.pop(); object = (JS)stack.pop(); @@ -360,14 +371,14 @@ class Interpreter implements ByteCodes, Tokens, Pausable { if (object instanceof JSFunction) { stack.push(new CallMarker(this)); - stack.push(jsargs); f = (JSFunction)object; + stack.push(new JSArgs(jsargs, f)); scope = f.parentScope; pc = -1; break; } else { JS c = (JS)object; - ret = method == null ? c.call(jsargs) : c.call(method, jsargs); + ret = c.call(method, jsargs); } if (pausecount > initialPauseCount) { pc++; return null; } @@ -378,29 +389,6 @@ class Interpreter implements ByteCodes, Tokens, Pausable { case THROW: 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, Map v, JSScope scope) throws JSExn { - return r.match(s, start, v, final_scope); - } - 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 { - Map v = new Map(); - r.matchAndWrite((String)a0, 0, v, final_scope, "foo"); - return v.get("foo"); - } - }; - Object obj = stack.pop(); - if (obj != null && obj instanceof Grammar) r2 = new Grammar.Alternative((Grammar)obj, r2); - stack.push(r2); - break; - } - */ case ADD_TRAP: case DEL_TRAP: { JS val = (JS)stack.pop(); JS key = (JS)stack.pop(); @@ -421,13 +409,13 @@ class Interpreter implements ByteCodes, Tokens, Pausable { JS left = (JS)stack.pop(); JS ret; if(left instanceof JSString || right instanceof JSString) - ret = Script.S(Script.toString(left).concat(Script.toString(right))); + ret = JSU.S(JSU.toString(left).concat(JSU.toString(right))); else if(left instanceof JSNumber.D || right instanceof JSNumber.D) - ret = Script.N(Script.toDouble(left) + Script.toDouble(right)); + ret = JSU.N(JSU.toDouble(left) + JSU.toDouble(right)); else { - 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); + long l = JSU.toLong(left) + JSU.toLong(right); + if(l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) ret = JSU.N(l); + ret = JSU.N((int)l); } stack.push(ret); } else { @@ -435,27 +423,27 @@ class Interpreter implements ByteCodes, Tokens, Pausable { while(--count >= 0) args[count] = (JS)stack.pop(); if(args[0] instanceof JSString) { StringBuffer sb = new StringBuffer(64); - for(int i=0;i> Script.toLong(right))); break; - case URSH: stack.push(Script.N(Script.toLong(left) >>> Script.toLong(right))); break; + case LSH: stack.push(JSU.N(JSU.toLong(left) << JSU.toLong(right))); break; + case RSH: stack.push(JSU.N(JSU.toLong(left) >> JSU.toLong(right))); break; + case URSH: stack.push(JSU.N(JSU.toLong(left) >>> JSU.toLong(right))); break; - //#repeat />= LT/LE/GT/GE - case LT: { + case LT: case LE: case GT: case GE: { + int cmp = 0; if(left instanceof JSString && right instanceof JSString) - stack.push(Script.B(Script.toString(left).compareTo(Script.toString(right)) < 0)); + cmp = JSU.toString(left).compareTo(JSU.toString(right)); else - stack.push(Script.B(Script.toDouble(left) < Script.toDouble(right))); + cmp = (int)(100 * (JSU.toDouble(left) - JSU.toDouble(right))); + switch(op) { + case LE: stack.push(JSU.B(cmp <= 0)); break; + case LT: stack.push(JSU.B(cmp < 0)); break; + case GE: stack.push(JSU.B(cmp >= 0)); break; + case GT: stack.push(JSU.B(cmp > 0)); break; + default: throw new RuntimeException("impossible"); + } + break; } - //#end case EQ: case NE: { @@ -495,7 +490,7 @@ class Interpreter implements ByteCodes, Tokens, Pausable { if(left == null && right == null) ret = true; else if(left == null || right == null) ret = false; else ret = left.equals(right); - stack.push(Script.B(op == EQ ? ret : !ret)); break; + stack.push(JSU.B(op == EQ ? ret : !ret)); break; } default: throw new Error("unknown opcode " + op); @@ -515,7 +510,7 @@ class Interpreter implements ByteCodes, Tokens, Pausable { */ void catchException(JSExn e) throws JSExn { while(!stack.empty()) { - JS o = (JS)stack.pop(); + Object o = stack.pop(); if (o instanceof CatchMarker || o instanceof TryMarker) { boolean inCatch = o instanceof CatchMarker; if(inCatch) { @@ -546,7 +541,7 @@ class Interpreter implements ByteCodes, Tokens, Pausable { void setupTrap(JS.Trap t, JS val, CallMarker cm) throws JSExn { stack.push(cm); stack.push(new TrapArgs(t, val)); - f = (JSFunction)t.function(); // FIXME + f = (JSFunction)t.function(); scope = f.parentScope; pc = 0; } @@ -615,17 +610,36 @@ class Interpreter implements ByteCodes, Tokens, Pausable { public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg = null; } // Just throw this exn } + static class JSArgs extends JS.Immutable { + private final JS[] args; + private final JS callee; + + public JSArgs(JS[] args, JS callee) { this.args = args; this.callee = callee; } + + public JS get(JS key) throws JSExn { + if(JSU.isInt(key)) { + int i = JSU.toInt(key); + return i>=args.length ? null : args[i]; + } + //#switch(JSU.toString(key)) + case "callee": return callee; + case "length": return JSU.N(args.length); + //#end + return super.get(key); + } + } + 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(Script.isInt(key) && Script.toInt(key) == 0) return val; - //#switch(Script.str(key)) + if(JSU.isInt(key) && JSU.toInt(key) == 0) return val; + //#switch(JSU.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; + case "length": return t.isWriteTrap() ? JSU.ONE : JSU.ZERO; //#end return super.get(key); } @@ -635,7 +649,10 @@ class Interpreter implements ByteCodes, Tokens, Pausable { private JS method; JS obj; public Stub(JS obj, JS method) { this.obj = obj; this.method = method; } - public JS call(JS[] args) throws JSExn { return obj.call(method, args); } + public JS call(JS method, JS[] args) throws JSExn { + if (method==null) return obj.call(this.method, args); + return super.call(method, args); + } } static final class Stack { @@ -671,7 +688,7 @@ class Interpreter implements ByteCodes, Tokens, Pausable { if(cm.f == null) break; String s = cm.f.sourceName + ":" + cm.f.line[cm.pc-1]; if(cm instanceof Interpreter.TrapMarker) - s += " (trap on " + Script.str(((Interpreter.TrapMarker)cm).t.key()) + ")"; + s += " (trap on " + JSU.str(((Interpreter.TrapMarker)cm).t.key()) + ")"; e.addBacktrace(s); } }