X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fxwt%2Fjs%2FJSFunction.java;h=55741a5314648eb08087b8eba59a452d717c5840;hb=0b0673bbc7f06c5d5418d5ab7ad5961a464e2de0;hp=00244c6b9b04f13cda353146c4efb24415234ccd;hpb=7f5df8070a5551fe66abd11a589677e285ca62f8;p=org.ibex.core.git diff --git a/src/org/xwt/js/JSFunction.java b/src/org/xwt/js/JSFunction.java index 00244c6..55741a5 100644 --- a/src/org/xwt/js/JSFunction.java +++ b/src/org/xwt/js/JSFunction.java @@ -8,13 +8,6 @@ import java.io.*; /** A JavaScript function, compiled into bytecode */ public class JSFunction extends JSCallable implements ByteCodes, Tokens { - /** Note: code gets run in an unpauseable context. */ - public Object call(JSArray args) { - JSContext cx = new JSContext(this, false); - cx.invoke(args); - return cx.stack.pop(); - } - // Fields and Accessors /////////////////////////////////////////////// @@ -42,7 +35,7 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens { return ret; } - private JSFunction(String sourceName, int firstLine, JSScope parentJSScope) { + JSFunction(String sourceName, int firstLine, JSScope parentJSScope) { this.sourceName = sourceName; this.firstLine = firstLine; this.parentJSScope = parentJSScope; @@ -60,7 +53,13 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens { add(-1, LITERAL, null); add(-1, RETURN); } - + + /** Note: code gets run in an unpauseable context. */ + public Object call(JSArray args) { + JSContext cx = new JSContext(this, false); + cx.invoke(args); + return cx.stack.pop(); + } // Adding and Altering Bytecodes /////////////////////////////////////////////////// @@ -86,451 +85,6 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens { } - // Invoking the Bytecode /////////////////////////////////////////////////////// - - /** returns false if the thread has been paused */ - static Object eval(final JSContext cx) throws JS.Exn { - final int initialPauseCount = cx.pausecount; - OUTER: for(;; cx.pc++) { - try { - if (cx.f == null || cx.pc >= cx.f.size) return cx.stack.pop(); - int op = cx.f.op[cx.pc]; - Object arg = cx.f.arg[cx.pc]; - if(op == FINALLY_DONE) { - FinallyData fd = (FinallyData) cx.stack.pop(); - if(fd == null) continue OUTER; // NOP - op = fd.op; - arg = fd.arg; - } - switch(op) { - case LITERAL: cx.stack.push(arg); break; - case OBJECT: cx.stack.push(new JSObj()); break; - case ARRAY: cx.stack.push(new JSArray(JS.toNumber(arg).intValue())); break; - case DECLARE: cx.scope.declare((String)(arg==null ? cx.stack.peek() : arg)); if(arg != null) cx.stack.push(arg); break; - case TOPSCOPE: cx.stack.push(cx.scope); break; - case JT: if (JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break; - case JF: if (!JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break; - case JMP: cx.pc += JS.toNumber(arg).intValue() - 1; break; - case POP: cx.stack.pop(); break; - case SWAP: { - int depth = (arg == null ? 1 : toInt(arg)); - Object save = cx.stack.elementAt(cx.stack.size() - 1); - for(int i=cx.stack.size() - 1; i > cx.stack.size() - 1 - depth; i--) - cx.stack.setElementAt(cx.stack.elementAt(i-1), i); - cx.stack.setElementAt(save, cx.stack.size() - depth - 1); - break; } - case DUP: cx.stack.push(cx.stack.peek()); break; - case NEWSCOPE: cx.scope = new JSScope(cx.scope); break; - case OLDSCOPE: cx.scope = cx.scope.getParentJSScope(); break; - case ASSERT: if (!JS.toBoolean(cx.stack.pop())) throw je("assertion failed"); break; - case BITNOT: cx.stack.push(new Long(~JS.toLong(cx.stack.pop()))); break; - case BANG: cx.stack.push(new Boolean(!JS.toBoolean(cx.stack.pop()))); break; - case NEWFUNCTION: cx.stack.push(((JSFunction)arg).cloneWithNewParentJSScope(cx.scope)); break; - case LABEL: break; - - case TYPEOF: { - Object o = cx.stack.pop(); - if (o == null) cx.stack.push(null); - else if (o instanceof JS) cx.stack.push(((JS)o).typeName()); - else if (o instanceof String) cx.stack.push("string"); - else if (o instanceof Number) cx.stack.push("number"); - else if (o instanceof Boolean) cx.stack.push("boolean"); - else cx.stack.push("unknown"); - break; - } - - case PUSHKEYS: { - Object o = cx.stack.peek(); - Enumeration e = ((JS)o).keys(); - JSArray a = new JSArray(); - while(e.hasMoreElements()) a.addElement(e.nextElement()); - cx.stack.push(a); - break; - } - - case LOOP: - cx.stack.push(new LoopMarker(cx.pc, cx.pc > 0 && cx.f.op[cx.pc - 1] == LABEL ? - (String)cx.f.arg[cx.pc - 1] : (String)null, cx.scope)); - cx.stack.push(Boolean.TRUE); - break; - - case BREAK: - case CONTINUE: - while(cx.stack.size() > 0) { - Object o = cx.stack.pop(); - if (o instanceof CallMarker) ee("break or continue not within a loop"); - if (o instanceof TryMarker) { - if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going - cx.stack.push(new FinallyData(op, arg)); - cx.scope = ((TryMarker)o).scope; - cx.pc = ((TryMarker)o).finallyLoc - 1; - continue OUTER; - } - if (o instanceof LoopMarker) { - if (arg == null || arg.equals(((LoopMarker)o).label)) { - int loopInstructionLocation = ((LoopMarker)o).location; - int endOfLoop = ((Integer)cx.f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation; - cx.scope = ((LoopMarker)o).scope; - if (op == CONTINUE) { cx.stack.push(o); cx.stack.push(Boolean.FALSE); } - cx.pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation; - continue OUTER; - } - } - } - throw new Error("CONTINUE/BREAK invoked but couldn't find LoopMarker at " + - cx.getSourceName() + ":" + cx.getLine()); - - case TRY: { - int[] jmps = (int[]) arg; - // jmps[0] is how far away the catch block is, jmps[1] is how far away the finally block is - // each can be < 0 if the specified block does not exist - cx.stack.push(new TryMarker(jmps[0] < 0 ? -1 : cx.pc + jmps[0], jmps[1] < 0 ? -1 : cx.pc + jmps[1], cx.scope)); - break; - } - - case RETURN: { - Object retval = cx.stack.pop(); - while(cx.stack.size() > 0) { - Object o = cx.stack.pop(); - if (o instanceof TryMarker) { - if(((TryMarker)o).finallyLoc < 0) continue; - cx.stack.push(retval); - cx.stack.push(new FinallyData(RETURN)); - cx.scope = ((TryMarker)o).scope; - cx.pc = ((TryMarker)o).finallyLoc - 1; - continue OUTER; - } else if (o instanceof CallMarker) { - if (cx.scope instanceof JSTrap.JSTrapScope) { - JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)cx.scope; - if (!ts.cascadeHappened) { - ts.cascadeHappened = true; - JSTrap t = ts.t.next; - while (t != null && t.f.numFormalArgs == 0) t = t.next; - if (t == null) { - ((JS)ts.t.trapee).put(t.name, ts.val); - if (cx.pausecount > initialPauseCount) return null; // we were paused - } else { - cx.stack.push(o); - JSArray args = new JSArray(); - args.addElement(ts.val); - cx.stack.push(args); - cx.f = t.f; - cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, ts.val); - cx.pc = -1; - break; - } - } - } - cx.scope = ((CallMarker)o).scope; - cx.pc = ((CallMarker)o).pc; - cx.f = (JSFunction)((CallMarker)o).f; - cx.stack.push(retval); - continue OUTER; - } - } - throw new Error("error: RETURN invoked but couldn't find a CallMarker!"); - } - - case PUT: { - Object val = cx.stack.pop(); - Object key = cx.stack.pop(); - Object target = cx.stack.peek(); - if (target == null) - throw je("tried to put a value to the " + key + " property on the null value"); - if (!(target instanceof JS)) - throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName()); - if (key == null) - throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key"); - JSTrap t = null; - if (target instanceof JSTrap.JSTrappable) { - t = ((JSTrap.JSTrappable)target).getTrap(val); - while (t != null && t.f.numFormalArgs == 0) t = t.next; - } else if (target instanceof JSTrap.JSTrapScope && key.equals("cascade")) { - JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)target; - t = ts.t.next; - ts.cascadeHappened = true; - while (t != null && t.f.numFormalArgs == 0) t = t.next; - if (t == null) target = ts.t.trapee; - } - if (t != null) { - cx.stack.push(new CallMarker(cx)); - JSArray args = new JSArray(); - args.addElement(val); - cx.stack.push(args); - cx.f = t.f; - cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, val); - cx.pc = -1; - break; - } - ((JS)target).put(key, val); - if (cx.pausecount > initialPauseCount) return null; // we were paused - cx.stack.push(val); - break; - } - - case GET: - case GET_PRESERVE: { - Object o, v; - if (op == GET) { - v = arg == null ? cx.stack.pop() : arg; - o = cx.stack.pop(); - } else { - v = cx.stack.pop(); - o = cx.stack.peek(); - cx.stack.push(v); - } - Object ret = null; - if (v == null) throw je("tried to get the null key from " + o); - if (o == null) throw je("tried to get property \"" + v + "\" from the null value @" + cx.pc + "\n" + cx.f.dump()); - if (o instanceof String || o instanceof Number || o instanceof Boolean) { - ret = Internal.getFromPrimitive(o,v); - cx.stack.push(ret); - break; - } else if (o instanceof JS) { - JSTrap t = null; - if (o instanceof JSTrap.JSTrappable) { - t = ((JSTrap.JSTrappable)o).getTrap(v); - while (t != null && t.f.numFormalArgs != 0) t = t.next; - } else if (o instanceof JSTrap.JSTrapScope && v.equals("cascade")) { - t = ((JSTrap.JSTrapScope)o).t.next; - while (t != null && t.f.numFormalArgs != 0) t = t.next; - if (t == null) o = ((JSTrap.JSTrapScope)o).t.trapee; - } - if (t != null) { - cx.stack.push(new CallMarker(cx)); - JSArray args = new JSArray(); - cx.stack.push(args); - cx.f = t.f; - cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, null); - ((JSTrap.JSTrapScope)cx.scope).cascadeHappened = true; - cx.pc = -1; - break; - } - ret = ((JS)o).get(v); - if (cx.pausecount > initialPauseCount) return null; // we were paused - cx.stack.push(ret); - break; - } - throw je("tried to get property " + v + " from a " + o.getClass().getName()); - } - - case CALL: case CALLMETHOD: { - int numArgs = JS.toInt(arg); - Object method = null; - Object object = null; - Object ret = null; - JSArray arguments = null; - - if(op == CALLMETHOD) { - Object getResult = cx.stack.pop(); - method = cx.stack.pop(); - object = cx.stack.pop(); - if (getResult != null) { - method = null; - object = getResult; - } - } else { - method = null; - object = cx.stack.pop(); - } - - if (object instanceof String || object instanceof Number || object instanceof Boolean) { - arguments = new JSArray(); for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j); - ret = Internal.callMethodOnPrimitive(object, method, arguments); - - } else if (object instanceof JSFunction) { - // FEATURE: use something similar to call0/call1/call2 here - arguments = new JSArray(); for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j); - cx.stack.push(new CallMarker(cx)); - cx.stack.push(arguments); - cx.f = (JSFunction)object; - cx.scope = new JSScope(cx.f.parentJSScope); - cx.pc = -1; - break; - - } else if (object instanceof JSCallable) { - JSCallable c = (JSCallable)object; - switch(numArgs) { - case 0: ret = c.call0(method); break; - case 1: ret = c.call1(method, cx.stack.pop()); break; - case 2: { - Object first = cx.stack.pop(); - Object second = cx.stack.pop(); - ret = c.call2(method, second, first); - break; - } - default: { - arguments = new JSArray(); - for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j); - ret = c.call(method, arguments); - break; - } - } - } else { - throw new JS.Exn("can't call a " + object.getClass().getName() + " @" + cx.pc + "\n" + cx.f.dump()); - } - if (cx.pausecount > initialPauseCount) return null; - cx.stack.push(ret); - break; - } - - case THROW: { - Object o = cx.stack.pop(); - if(o instanceof JS.Exn) throw (JS.Exn)o; - throw new JS.Exn(o); - } - - case ASSIGN_SUB: case ASSIGN_ADD: { - Object val = cx.stack.pop(); - Object old = cx.stack.pop(); - Object key = cx.stack.pop(); - Object obj = cx.stack.peek(); - if (val instanceof JSFunction && obj instanceof JSScope) { - JSScope parent = (JSScope)obj; - while(parent.getParentJSScope() != null) parent = parent.getParentJSScope(); - if (parent instanceof JSTrap.JSTrappable) { - JSTrap.JSTrappable b = (JSTrap.JSTrappable)parent; - if (op == ASSIGN_ADD) JSTrap.addTrap(b, key, (JSFunction)val); - else JSTrap.delTrap(b, key, (JSFunction)val); - // skip over the "normal" implementation of +=/-= - cx.pc += ((Integer)arg).intValue() - 1; - break; - } - } - // use the "normal" implementation - cx.stack.push(key); - cx.stack.push(old); - cx.stack.push(val); - break; - } - - case ADD: { - int count = ((Number)arg).intValue(); - if(count < 2) throw new Error("this should never happen"); - if(count == 2) { - // common case - Object right = cx.stack.pop(); - Object left = cx.stack.pop(); - if(left instanceof String || right instanceof String) - cx.stack.push(JS.toString(left).concat(JS.toString(right))); - else cx.stack.push(new Double(JS.toDouble(left) + JS.toDouble(right))); - } else { - Object[] args = new Object[count]; - while(--count >= 0) args[count] = cx.stack.pop(); - if(args[0] instanceof String) { - StringBuffer sb = new StringBuffer(64); - for(int i=0;i> JS.toLong(right))); break; - case URSH: cx.stack.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break; - - case LT: case LE: case GT: case GE: { - if (left == null) left = new Integer(0); - if (right == null) right = new Integer(0); - int result = 0; - if (left instanceof String || right instanceof String) { - result = left.toString().compareTo(right.toString()); - } else { - result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right)); - } - cx.stack.push(new Boolean((op == LT && result < 0) || (op == LE && result <= 0) || - (op == GT && result > 0) || (op == GE && result >= 0))); - break; - } - - case EQ: - case NE: { - Object l = left; - Object r = right; - boolean ret; - if (l == null) { Object tmp = r; r = l; l = tmp; } - if (l == null && r == null) ret = true; - else if (r == null) ret = false; // l != null, so its false - else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l); - else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue(); - else if (l instanceof String) ret = r != null && l.equals(r.toString()); - else ret = l.equals(r); - cx.stack.push(new Boolean(op == EQ ? ret : !ret)); break; - } - - default: throw new Error("unknown opcode " + op); - } } - } - - } catch(JS.Exn e) { - while(cx.stack.size() > 0) { - Object o = cx.stack.pop(); - if (o instanceof CatchMarker || o instanceof TryMarker) { - boolean inCatch = o instanceof CatchMarker; - if(inCatch) { - o = cx.stack.pop(); - if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going - } - if(!inCatch && ((TryMarker)o).catchLoc >= 0) { - // run the catch block, this will implicitly run the finally block, if it exists - cx.stack.push(o); - cx.stack.push(catchMarker); - cx.stack.push(e.getObject()); - cx.scope = ((TryMarker)o).scope; - cx.pc = ((TryMarker)o).catchLoc - 1; - continue OUTER; - } else { - cx.stack.push(e); - cx.stack.push(new FinallyData(THROW)); - cx.scope = ((TryMarker)o).scope; - cx.pc = ((TryMarker)o).finallyLoc - 1; - continue OUTER; - } - } - // no handler found within this func - if(o instanceof CallMarker) throw e; - } - throw e; - } // end try/catch - } // end for - } - - // Debugging ////////////////////////////////////////////////////////////////////// public String toString() { return "JSFunction [" + sourceName + ":" + firstLine + "]"; } @@ -559,54 +113,5 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens { } - // Exception Stuff //////////////////////////////////////////////////////////////// - - static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } } - static EvaluatorException ee(String s) { - throw new EvaluatorException(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s); - } - static JS.Exn je(String s) { - throw new JS.Exn(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s); - } - - - // Markers ////////////////////////////////////////////////////////////////////// - - public static class CallMarker { - int pc; - JSScope scope; - JSFunction f; - public CallMarker(JSContext cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; } - } - - public static class CatchMarker { public CatchMarker() { } } - private static CatchMarker catchMarker = new CatchMarker(); - - public static class LoopMarker { - public int location; - public String label; - public JSScope scope; - public LoopMarker(int location, String label, JSScope scope) { - this.location = location; - this.label = label; - this.scope = scope; - } - } - public static class TryMarker { - public int catchLoc; - public int finallyLoc; - public JSScope scope; - public TryMarker(int catchLoc, int finallyLoc, JSScope scope) { - this.catchLoc = catchLoc; - this.finallyLoc = finallyLoc; - this.scope = scope; - } - } - public static class FinallyData { - public int op; - public Object arg; - public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; } - public FinallyData(int op) { this(op,null); } - } }