From 190e32962a4936670125a38c266f81adc098b539 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:40:41 +0000 Subject: [PATCH] 2003/10/31 09:50:09 darcs-hash:20040130074041-2ba56-630a1c9729be29db21665d9241fbc7f0676fba37.gz --- src/org/xwt/js/CompiledFunctionImpl.java | 368 +++++++++++++++--------------- src/org/xwt/js/JS.java | 47 ++-- src/org/xwt/js/Math.java | 4 +- src/org/xwt/plat/OpenGL.java | 4 +- 4 files changed, 216 insertions(+), 207 deletions(-) diff --git a/src/org/xwt/js/CompiledFunctionImpl.java b/src/org/xwt/js/CompiledFunctionImpl.java index 66151c7..df1b621 100644 --- a/src/org/xwt/js/CompiledFunctionImpl.java +++ b/src/org/xwt/js/CompiledFunctionImpl.java @@ -5,7 +5,7 @@ import org.xwt.util.*; import java.io.*; /** a JavaScript function, compiled into bytecode */ -class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { +class CompiledFunctionImpl extends JS.Obj implements ByteCodes, Tokens { // Fields and Accessors /////////////////////////////////////////////// @@ -13,27 +13,27 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { int numFormalArgs = 0; /** the source code file that this block was drawn from */ - private String sourceName; + String sourceName; public String getSourceName() throws JS.Exn { return sourceName; } /** the line numbers */ - private int[] line = new int[10]; + int[] line = new int[10]; /** the first line of this script */ private int firstLine = -1; /** the instructions */ - private int[] op = new int[10]; + int[] op = new int[10]; /** the arguments to the instructions */ - private Object[] arg = new Object[10]; + Object[] arg = new Object[10]; /** the number of instruction/argument pairs */ - private int size = 0; + int size = 0; int size() { return size; } /** the scope in which this function was declared; by default this function is called in a fresh subscope of the parentScope */ - private JS.Scope parentScope; + JS.Scope parentScope; // Constructors //////////////////////////////////////////////////////// @@ -63,30 +63,6 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { add(-1, RETURN); } - public Object call(JS.Array args) throws JS.Exn { return call(args, new FunctionScope(sourceName, parentScope)); } - public Object call(JS.Array args, JS.Scope scope) throws JS.Exn { - JS.Thread cx = JS.Thread.fromJavaThread(java.lang.Thread.currentThread()); - CompiledFunction saved = cx.currentCompiledFunction; - try { - cx.currentCompiledFunction = (CompiledFunction)this; - int size = cx.stack.size(); - cx.stack.push(callMarker); - cx.stack.push(args); - eval(scope); - Object ret = cx.stack.pop(); - if (cx.stack.size() > size) - // this should never happen - throw new Error("ERROR: stack grew by " + (cx.stack.size() - size) + - " elements during call at " + sourceName + ":" + firstLine); - return ret; - } catch(Error e) { - // Unwind the stack - while(cx.stack.size() > 0) if(cx.stack.pop() instanceof CallMarker) throw e; - throw new Error("CallMarker not found on the stack"); // should never happen - } finally { - cx.currentCompiledFunction = saved; - } - } // Adding and Altering Bytecodes /////////////////////////////////////////////////// @@ -111,63 +87,71 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { return this; } - public int getLine(int pc) { - if(pc < 0 || pc >= size) return -1; - return line[pc]; + public static int getLine(JS.Thread cx) { + if(cx.pc < 0 || cx.pc >= ((CompiledFunctionImpl)cx.currentCompiledFunction).size) return -1; + return ((CompiledFunctionImpl)cx.currentCompiledFunction).line[cx.pc]; } // Invoking the Bytecode /////////////////////////////////////////////////////// - - void eval(JS.Scope s) { - final JS.Thread cx = JS.Thread.fromJavaThread(java.lang.Thread.currentThread()); - final Vec t = cx.stack; - int pc; - int lastPC = -1; - OUTER: for(pc=0; pc 0 && op[pc - 1] == LABEL ? (String)arg[pc - 1] : (String)null,s)); - t.push(Boolean.TRUE); + cx.stack.push(new LoopMarker(cx.pc, cx.pc > 0 && cx.currentCompiledFunction.op[cx.pc - 1] == LABEL ? (String)cx.currentCompiledFunction.arg[cx.pc - 1] : (String)null, cx.scope)); + cx.stack.push(Boolean.TRUE); break; } case BREAK: case CONTINUE: - while(t.size() > 0) { - Object o = t.pop(); + 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 - t.push(new FinallyData(curOP, curArg)); - s = ((TryMarker)o).scope; - pc = ((TryMarker)o).finallyLoc - 1; + cx.stack.push(new FinallyData(curOP, curArg)); + cx.scope = ((TryMarker)o).scope; + cx.pc = ((TryMarker)o).finallyLoc - 1; continue OUTER; } if (o instanceof LoopMarker) { if (curArg == null || curArg.equals(((LoopMarker)o).label)) { int loopInstructionLocation = ((LoopMarker)o).location; - int endOfLoop = ((Integer)arg[loopInstructionLocation]).intValue() + loopInstructionLocation; - s = ((LoopMarker)o).scope; - if (curOP == CONTINUE) { t.push(o); t.push(Boolean.FALSE); } - pc = curOP == BREAK ? endOfLoop - 1 : loopInstructionLocation; + int endOfLoop = ((Integer)cx.currentCompiledFunction.arg[loopInstructionLocation]).intValue() + loopInstructionLocation; + cx.scope = ((LoopMarker)o).scope; + if (curOP == CONTINUE) { cx.stack.push(o); cx.stack.push(Boolean.FALSE); } + cx.pc = curOP == BREAK ? endOfLoop - 1 : loopInstructionLocation; continue OUTER; } } } - throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + sourceName + ":" + getLine(pc)); + throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + ((CompiledFunctionImpl)cx.currentCompiledFunction).sourceName + ":" + getLine(cx)); case TRY: { - int[] jmps = (int[]) arg[pc]; + int[] jmps = (int[]) curArg; // 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 - t.push(new TryMarker(jmps[0] < 0 ? -1 : pc + jmps[0], jmps[1] < 0 ? -1 : pc + jmps[1],s)); + 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 = t.pop(); - while(t.size() > 0) { - Object o = t.pop(); + 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; - t.push(retval); - t.push(new FinallyData(RETURN)); - s = ((TryMarker)o).scope; - pc = ((TryMarker)o).finallyLoc - 1; + cx.stack.push(retval); + cx.stack.push(new FinallyData(RETURN)); + cx.scope = ((TryMarker)o).scope; + cx.pc = ((TryMarker)o).finallyLoc - 1; continue OUTER; } if (o instanceof CallMarker) { - t.push(retval); - return; + cx.scope = ((CallMarker)o).scope; + cx.pc = ((CallMarker)o).pc; + cx.currentCompiledFunction = (CompiledFunction)((CallMarker)o).currentCompiledFunction; + cx.stack.push(retval); + continue OUTER; } } throw new Error("error: RETURN invoked but couldn't find a CallMarker!"); } case PUT: { - Object val = t.pop(); - Object key = t.pop(); - Object target = t.peek(); + 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)) @@ -255,31 +242,31 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { if (key == null) throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key"); ((JS)target).put(key, val); - t.push(val); + cx.stack.push(val); break; } case GET: case GET_PRESERVE: { Object o, v; - if (op[pc] == GET) { - v = arg[pc] == null ? t.pop() : arg[pc]; - o = t.pop(); + if (curOP == GET) { + v = curArg == null ? cx.stack.pop() : curArg; + o = cx.stack.pop(); } else { - v = t.pop(); - o = t.peek(); - t.push(v); + v = cx.stack.pop(); + o = cx.stack.peek(); + cx.stack.push(v); } Object ret = null; if (o == null) throw je("tried to get property \"" + v + "\" from the null value"); if (v == null) throw je("tried to get the null key from " + o); if (o instanceof String || o instanceof Number || o instanceof Boolean) ret = Internal.getFromPrimitive(o,v); - else if (o instanceof JS) + else if (o instanceof JS) { ret = ((JS)o).get(v); - else + } else throw je("tried to get property " + v + " from a " + o.getClass().getName()); - t.push(ret); + cx.stack.push(ret); break; } @@ -287,50 +274,69 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { case CALL: { JS.Array arguments = new JS.Array(); - int numArgs = JS.toNumber(arg[pc]).intValue(); + int numArgs = JS.toNumber(curArg).intValue(); arguments.setSize(numArgs); - for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j); - Object o = t.pop(); + for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j); + Object o = cx.stack.pop(); if(o == null) throw je("attempted to call null"); Object ret; - if(op[pc] == CALLMETHOD) { + if(curOP == CALLMETHOD) { Object method = o; - o = t.pop(); - if(o instanceof String || o instanceof Number || o instanceof Boolean) + o = cx.stack.pop(); + if(o instanceof String || o instanceof Number || o instanceof Boolean) { ret = Internal.callMethodOnPrimitive(o,method,arguments); - else if(o instanceof JS) - ret = ((JS)o).callMethod(method,arguments,false); - else - throw new JS.Exn("Tried to call a method on an object that isn't a JS object"); - } else { + cx.stack.push(ret); + break; + } else if (o instanceof JS) { + if (((JS)o).callMethod(method, arguments, true) == Boolean.TRUE) { + ret = ((JS)o).callMethod(method,arguments,false); + cx.stack.push(ret); + break; + } else { + o = ((JS)o).get(method); + } + } else { + throw new JS.Exn("Tried to call a method on an object that isn't a JS object: " + o); + } + } + + if (o instanceof CompiledFunctionImpl) { + cx.stack.push(new CallMarker(cx)); + cx.stack.push(arguments); + cx.currentCompiledFunction = (CompiledFunction)o; + cx.scope = new FunctionScope("unknown", cx.currentCompiledFunction.parentScope); + cx.pc = -1; + break; + + } else { ret = ((JS.Callable)o).call(arguments); } - t.push(ret); + cx.stack.push(ret); break; } // fall through if exception was thrown case THROW: { - Object o = t.pop(); + Object o = cx.stack.pop(); if(o instanceof JS.Exn) throw (JS.Exn)o; throw new JS.Exn(o); } case INC: case DEC: { - boolean isPrefix = JS.toBoolean(arg[pc]); - Object key = t.pop(); - JS obj = (JS)t.pop(); + boolean isPrefix = JS.toBoolean(curArg); + Object key = cx.stack.pop(); + JS obj = (JS)cx.stack.pop(); Number num = JS.toNumber(obj.get(key)); - Number val = new Double(op[pc] == INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0); + Number val = new Double(curOP == INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0); obj.put(key, val); - t.push(isPrefix ? val : num); + cx.stack.push(isPrefix ? val : num); break; } case ASSIGN_SUB: case ASSIGN_ADD: { - Object val = t.pop(); - Object old = t.pop(); - Object key = t.pop(); - Object obj = t.peek(); + Object val = cx.stack.pop(); + Object old = cx.stack.pop(); + Object key = cx.stack.pop(); + Object obj = cx.stack.peek(); if (val instanceof CompiledFunction) { if (obj instanceof JS.Scope) { JS.Scope parent = (JS.Scope)obj; @@ -342,41 +348,41 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { ((org.xwt.Box)parent).delTrap(key, val); } // skip over the "normal" implementation of +=/-= - pc += ((Integer)arg[pc]).intValue() - 1; + cx.pc += ((Integer)curArg).intValue() - 1; break; } } } // use the "normal" implementation - t.push(key); - t.push(old); - t.push(val); + cx.stack.push(key); + cx.stack.push(old); + cx.stack.push(val); break; } case ADD: { - int count = ((Number)arg[pc]).intValue(); + int count = ((Number)curArg).intValue(); if(count < 2) throw new Error("this should never happen"); if(count == 2) { // common case - Object right = t.pop(); - Object left = t.pop(); - if(left instanceof String || right instanceof String) t.push(JS.toString(left).concat(JS.toString(right))); - else t.push(new Double(JS.toDouble(left) + JS.toDouble(right))); + 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] = t.pop(); + 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: t.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break; + case LSH: cx.stack.push(new Long(JS.toLong(left) << JS.toLong(right))); break; + case RSH: cx.stack.push(new Long(JS.toLong(left) >> 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); @@ -422,8 +428,8 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { } else { result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right)); } - t.push(new Boolean((op[pc] == LT && result < 0) || (op[pc] == LE && result <= 0) || - (op[pc] == GT && result > 0) || (op[pc] == GE && result >= 0))); + cx.stack.push(new Boolean((curOP == LT && result < 0) || (curOP == LE && result <= 0) || + (curOP == GT && result > 0) || (curOP == GE && result >= 0))); break; } @@ -439,45 +445,45 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { 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); - t.push(new Boolean(op[pc] == EQ ? ret : !ret)); break; + cx.stack.push(new Boolean(curOP == EQ ? ret : !ret)); break; } - default: throw new Error("unknown opcode " + op[pc]); + default: throw new Error("unknown opcode " + curOP); } } } } catch(JS.Exn e) { - while(t.size() > 0) { - Object o = t.pop(); + 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 = t.pop(); + 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 - t.push(o); - t.push(catchMarker); - t.push(e.getObject()); - s = ((TryMarker)o).scope; - pc = ((TryMarker)o).catchLoc - 1; + 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 { - t.push(e); - t.push(new FinallyData(THROW)); - s = ((TryMarker)o).scope; - pc = ((TryMarker)o).finallyLoc - 1; + 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 new Error("couldn't find a Try or Call Marker!"); + throw e; } // end try/catch } // end for // this should never happen, we will ALWAYS have a RETURN at the end of the func - throw new Error("Just fell out of CompiledFunction::eval() loop. Last PC was " + lastPC); + throw new Error("Just fell out of CompiledFunction::eval() loop."); } // Debugging ////////////////////////////////////////////////////////////////////// @@ -509,13 +515,13 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { // Exception Stuff //////////////////////////////////////////////////////////////// static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } } - EvaluatorException ee(String s) { throw new EvaluatorException(sourceName + ":" + JS.Thread.currentJSThread().getLine() + " " + s); } - JS.Exn je(String s) { throw new JS.Exn(sourceName + ":" + JS.Thread.currentJSThread().getLine() + " " + s); } + static EvaluatorException ee(String s) { throw new EvaluatorException(((CompiledFunctionImpl)(JS.Thread.current()).currentCompiledFunction).sourceName + ":" + JS.Thread.current().getLine() + " " + s); } + static JS.Exn je(String s) { throw new JS.Exn(((CompiledFunctionImpl)(JS.Thread.current()).currentCompiledFunction).sourceName + ":" + JS.Thread.current().getLine() + " " + s); } // FunctionScope ///////////////////////////////////////////////////////////////// - private static class FunctionScope extends JS.Scope { + static class FunctionScope extends JS.Scope { String sourceName; public FunctionScope(String sourceName, Scope parentScope) { super(parentScope); this.sourceName = sourceName; } public String getSourceName() { return sourceName; } @@ -524,8 +530,12 @@ class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens { // Markers ////////////////////////////////////////////////////////////////////// - public static class CallMarker { public CallMarker() { } } - private static CallMarker callMarker = new CallMarker(); + public static class CallMarker { + int pc; + Scope scope; + CompiledFunctionImpl currentCompiledFunction; + public CallMarker(JS.Thread cx) { pc = cx.pc + 1; scope = cx.scope; currentCompiledFunction = cx.currentCompiledFunction; } + } public static class CatchMarker { public CatchMarker() { } } private static CatchMarker catchMarker = new CatchMarker(); diff --git a/src/org/xwt/js/JS.java b/src/org/xwt/js/JS.java index 885d840..256c33a 100644 --- a/src/org/xwt/js/JS.java +++ b/src/org/xwt/js/JS.java @@ -219,38 +219,37 @@ public abstract class JS { CompiledFunction currentCompiledFunction = null; Vec stack = new Vec(); - int pc; + public Scope scope = null; + int pc = 0; + boolean paused = false; - /** binds this thread to the current Java Thread */ - public void bindToCurrentJavaThread() { javaThreadToJSThread.put(java.lang.Thread.currentThread(), this); } + CompiledFunction tailCallFunction = null; + JS.Array tailCallArgs = null; - /** returns the line of code that is currently executing */ - public int getLine() { return currentCompiledFunction == null ? -1 : currentCompiledFunction.getLine(pc); } + public Thread(CompiledFunction function) { + this(function, new CompiledFunctionImpl.FunctionScope("unknown", function.parentScope)); + } + public Thread(CompiledFunction function, Scope scope) { + stack.push(new CompiledFunctionImpl.CallMarker(this)); + stack.push(new JS.Array()); + this.currentCompiledFunction = function; + this.scope = scope; + } + + public void setTailCall(JS.CompiledFunction f, JS.Array args) { tailCallFunction = f; tailCallArgs = args; } - /** returns the name of the source code file which declared the currently executing function */ + public static JS.Thread current() { return (JS.Thread)javaThreadToJSThread.get(java.lang.Thread.currentThread()); } + + public void resume() { bind(); paused = false; CompiledFunctionImpl.eval(this); } + public void pause() { paused = true; unbind(); } + public void bind() { javaThreadToJSThread.put(java.lang.Thread.currentThread(), this); } + public void unbind() { if (current() == this) javaThreadToJSThread.put(java.lang.Thread.currentThread(), this); } + public int getLine() { return currentCompiledFunction == null ? -1 : currentCompiledFunction.getLine(this); } public String getSourceName() { return currentCompiledFunction == null ? null : currentCompiledFunction.getSourceName(); } /** fetches the currently-executing javascript function */ public JS.CompiledFunction getCurrentCompiledFunction() { return currentCompiledFunction; } - - - // Statics /////////////////////////////////////////////////////////////////////// - private static Hashtable javaThreadToJSThread = new Hashtable(); - - /** returns the JS thread for a given Java thread, creating one if necessary */ - public static JS.Thread fromJavaThread(java.lang.Thread t) { - JS.Thread ret = (JS.Thread)javaThreadToJSThread.get(t); - if (ret == null) { - ret = new JS.Thread(); - ret.bindToCurrentJavaThread(); - } - return ret; - } - - public static JS.Thread currentJSThread() { - return fromJavaThread(java.lang.Thread.currentThread()); - } } } diff --git a/src/org/xwt/js/Math.java b/src/org/xwt/js/Math.java index 558d34a..2bb8c4d 100644 --- a/src/org/xwt/js/Math.java +++ b/src/org/xwt/js/Math.java @@ -6,7 +6,7 @@ import java.io.*; import java.util.*; /** The JavaScript Math object */ -class Math extends JS.Obj { +public class Math extends JS.Obj { public static Math singleton = new Math(); private static final JS.Callable ceil = new JS.Callable() { public Object call(JS.Array args) { @@ -76,7 +76,7 @@ class Math extends JS.Obj { private static final Double SQRT1_2 = new Double(1/java.lang.Math.sqrt(2)); private static final Double SQRT2 = new Double(java.lang.Math.sqrt(2)); - Math() { + protected Math() { put("abs", abs); put("acos", acos); put("asin", asin); diff --git a/src/org/xwt/plat/OpenGL.java b/src/org/xwt/plat/OpenGL.java index 359e17a..4cc0bd3 100644 --- a/src/org/xwt/plat/OpenGL.java +++ b/src/org/xwt/plat/OpenGL.java @@ -120,8 +120,8 @@ abstract class OpenGL { public void deleteTexture(final int tex) { // CHECKME: Is this safe to do from finalize()? // natDeleteTexture MUST be run from the message queue thread - Message.Q.add(new Message() { public void perform() { - natDeleteTexture(tex); + Scheduler.add(new Scheduler.Task() { public Object call(Object o) { + natDeleteTexture(tex); return null; }}); } -- 1.7.10.4