return this;
}
- 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];
+ public static int getLine(Context cx) {
+ if(cx.pc < 0 || cx.pc >= ((CompiledFunctionImpl)cx.f).size) return -1;
+ return ((CompiledFunctionImpl)cx.f).line[cx.pc];
}
// Invoking the Bytecode ///////////////////////////////////////////////////////
/** returns false if the thread has been paused */
- static Object eval(final JS.Thread cx) throws JS.Exn {
+ static Object eval(final Context cx, Object pushFirst) throws JS.Exn {
+ cx.stack.push(pushFirst);
OUTER: for(;; cx.pc++) {
try {
- if (cx.paused) return null;
- cx.bind();
- if (cx.currentCompiledFunction == null) return cx.stack.pop();
- if (cx.pc >= ((CompiledFunctionImpl)cx.currentCompiledFunction).size) return cx.stack.pop();
+ if (cx.f == null) return cx.stack.pop();
+ if (cx.pc >= ((CompiledFunctionImpl)cx.f).size) return cx.stack.pop();
String label = null;
- int curOP = cx.currentCompiledFunction.op[cx.pc];
- Object curArg = cx.currentCompiledFunction.arg[cx.pc];
+ int curOP = cx.f.op[cx.pc];
+ Object curArg = cx.f.arg[cx.pc];
+ Object returnedFromJava = null;
+ boolean checkReturnedFromJava = false;
if(curOP == FINALLY_DONE) {
FinallyData fd = (FinallyData) cx.stack.pop();
if(fd == null) continue OUTER; // NOP
case LABEL: break;
case LOOP: {
- 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(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;
}
if (o instanceof LoopMarker) {
if (curArg == null || curArg.equals(((LoopMarker)o).label)) {
int loopInstructionLocation = ((LoopMarker)o).location;
- int endOfLoop = ((Integer)cx.currentCompiledFunction.arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
+ int endOfLoop = ((Integer)cx.f.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;
}
}
}
- throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + ((CompiledFunctionImpl)cx.currentCompiledFunction).sourceName + ":" + getLine(cx));
+ throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + ((CompiledFunctionImpl)cx.f).sourceName + ":" + getLine(cx));
case TRY: {
int[] jmps = (int[]) curArg;
if (o instanceof CallMarker) {
cx.scope = ((CallMarker)o).scope;
cx.pc = ((CallMarker)o).pc;
- cx.currentCompiledFunction = (CompiledFunction)((CallMarker)o).currentCompiledFunction;
+ cx.f = (CompiledFunction)((CallMarker)o).f;
cx.stack.push(retval);
continue OUTER;
}
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");
- // FIXME too many allocations here
- TailCall tail = new TailCall();
- ((JS)target).put(key, val, tail);
- cx.stack.push(val);
- if (tail.func != null) {
- cx.stack.push(new CallMarker(cx));
- cx.stack.push(tail.args);
- cx.currentCompiledFunction = tail.func;
- cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", tail.func.parentScope);
- cx.pc = -1;
- break;
+ returnedFromJava = ((JS)target).put(key, val);
+ if (returnedFromJava != null) {
+ checkReturnedFromJava = true;
+ } else {
+ cx.stack.push(val);
}
break;
}
if (o instanceof String || o instanceof Number || o instanceof Boolean)
ret = Internal.getFromPrimitive(o,v);
else if (o instanceof JS) {
- ret = ((JS)o).get(v);
- if (ret instanceof JS.TailCall) {
- cx.stack.push(new CallMarker(cx));
- cx.stack.push(((JS.TailCall)ret).args);
- cx.currentCompiledFunction = ((JS.TailCall)ret).func;
- cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)ret).func.parentScope);
- cx.pc = -1;
- break;
- }
+ returnedFromJava = ((JS)o).get(v);
+ checkReturnedFromJava = true;
+ break;
} else
throw je("tried to get property " + v + " from a " + o.getClass().getName());
cx.stack.push(ret);
case CALLMETHOD:
case CALL:
+ case CALL_REVERSED:
{
JS.Array arguments = new JS.Array();
int numArgs = JS.toNumber(curArg).intValue();
arguments.setSize(numArgs);
+ Object o = null;
+ if (curOP == CALL_REVERSED) o = cx.stack.pop();
for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
- Object o = cx.stack.pop();
+ if (curOP != CALL_REVERSED) o = cx.stack.pop();
if(o == null) throw je("attempted to call null");
Object ret;
+
if(curOP == CALLMETHOD) {
Object method = o;
o = cx.stack.pop();
if(o instanceof String || o instanceof Number || o instanceof Boolean) {
ret = Internal.callMethodOnPrimitive(o,method,arguments);
cx.stack.push(ret);
+ cx.pc += 2; // skip the GET and CALL
+ break;
+ } else if (o instanceof JS && ((JS)o).callMethod(method, arguments, true) == Boolean.TRUE) {
+ returnedFromJava = ((JS)o).callMethod(method, arguments, false);
+ checkReturnedFromJava = true;
+ cx.pc += 2; // skip the GET and CALL
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);
- if (o instanceof JS.TailCall) {
- cx.stack.push(new CallMarker(cx));
- cx.stack.push(((JS.TailCall)o).args);
- cx.currentCompiledFunction = ((JS.TailCall)o).func;
- cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)o).func.parentScope);
- cx.pc = -1;
- break;
- }
- }
} else {
- throw new JS.Exn("Tried to call a method on an object that isn't a JS object: " + o);
+ // put the args back on the stack and let the GET followed by CALL happen
+ for(int j=0; j<numArgs; j++) cx.stack.push(arguments.elementAt(j));
+ cx.stack.push(o);
+ cx.stack.push(method);
+ break;
}
}
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.f = (CompiledFunction)o;
+ cx.scope = new FunctionScope("unknown", cx.f.parentScope);
cx.pc = -1;
break;
} else {
- ret = ((JS.Callable)o).call(arguments);
- if (ret instanceof JS.TailCall) {
- cx.stack.push(new CallMarker(cx));
- cx.stack.push(((JS.TailCall)ret).args);
- cx.currentCompiledFunction = ((JS.TailCall)ret).func;
- cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)ret).func.parentScope);
- cx.pc = -1;
- break;
- }
+ returnedFromJava = ((JS.Callable)o).call(arguments);
+ checkReturnedFromJava = true;
+ break;
}
- cx.stack.push(ret);
- break;
}
- // fall through if exception was thrown
+
case THROW: {
Object o = cx.stack.pop();
if(o instanceof JS.Exn) throw (JS.Exn)o;
default: throw new Error("unknown opcode " + curOP);
} }
}
+
+ // handle special directions returned from Java callouts
+ // ideally we would do this with exceptions, but they're *very* slow in gcj
+ if (checkReturnedFromJava) {
+ checkReturnedFromJava = false;
+ if (returnedFromJava == Context.pause) {
+ cx.pc++;
+ return Context.pause;
+ } else if (returnedFromJava instanceof TailCall) {
+ cx.stack.push(new CallMarker(cx));
+ cx.stack.push(((JS.TailCall)returnedFromJava).args);
+ cx.f = ((JS.TailCall)returnedFromJava).func;
+ cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", cx.f.parentScope);
+ cx.pc = -1;
+ } else {
+ cx.stack.push(returnedFromJava);
+ }
+ continue OUTER;
+ }
+
+
} catch(JS.Exn e) {
while(cx.stack.size() > 0) {
Object o = cx.stack.pop();
// Exception Stuff ////////////////////////////////////////////////////////////////
static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(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); }
-
+ static EvaluatorException ee(String s) {
+ throw new EvaluatorException(JS.Context.getSourceName() + ":" + JS.Context.getLine() + " " + s);
+ }
+ static JS.Exn je(String s) {
+ throw new JS.Exn(JS.Context.getSourceName() + ":" + JS.Context.getLine() + " " + s);
+ }
// FunctionScope /////////////////////////////////////////////////////////////////
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; }
+ CompiledFunctionImpl f;
+ public CallMarker(Context cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
}
public static class CatchMarker { public CatchMarker() { } }
package org.xwt.js;
import org.xwt.util.*;
+import org.xwt.*;
import java.io.*;
import java.util.*;
// Instance Methods ////////////////////////////////////////////////////////////////////
public abstract Object get(Object key) throws JS.Exn;
- public abstract void put(Object key, Object val) throws JS.Exn;
- public void put(Object key, Object val, TailCall tail) throws JS.Exn { put(key, val); }
+ public abstract Object put(Object key, Object val) throws JS.Exn;
public abstract Object[] keys();
public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn {
- if(checkOnly) return Boolean.FALSE;
+ if (checkOnly) return Boolean.FALSE;
Object o = get(method);
if(o instanceof JS.Callable) {
return ((JS.Callable)o).call(args);
public Obj() { this(false); }
public Obj(boolean sealed) { this.sealed = sealed; }
public void setSeal(boolean sealed) { this.sealed = sealed; } ///< a sealed object cannot have its properties modified
- public void put(Object key, Object val) { put2(key, null, val); }
+ public Object put(Object key, Object val) { put2(key, null, val); return null; }
protected void put2(Object key, Object key2, Object val) {
if (sealed) return;
if (entries == null) entries = new Hash();
public void addElement(Object o) { super.addElement(o); }
public void setElementAt(Object o, int i) { super.setElementAt(o, i); }
public Object get(Object key) { return super._get(key); }
- public void put(Object key, Object val) { super._put(key, val); }
+ public Object put(Object key, Object val) { super._put(key, val); return null; }
}
/** Any object which becomes part of the scope chain must support this interface */
public boolean isTransparent() { return super.isTransparent(); }
public boolean has(Object key) { return super.has(key); }
public Object get(Object key) { return super._get(key); }
- public void put(Object key, Object val) { super._put(key, val); }
+ public Object put(Object key, Object val) { super._put(key, val); return null; }
public void declare(String s) { super.declare(s); }
}
public boolean equals(Object o) { return (this == o || graftee.equals(o)); }
public int hashCode() { return graftee.hashCode(); }
public Object get(Object key) { return replaced_key.equals(key) ? replaced_val : graftee.get(key); }
- public void put(Object key, Object val) { graftee.put(key, val); }
+ public Object put(Object key, Object val) { graftee.put(key, val); return null; }
public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn {
if (!replaced_key.equals(method)) return graftee.callMethod(method, args, checkOnly);
if (replaced_val instanceof Callable) return checkOnly ? Boolean.TRUE : ((Callable)replaced_val).call(args);
public TailCall set(CompiledFunction func, JS.Array args) { this.func = func; this.args = args; return this; }
}
- /** encapsulates a single JavaScript thread; the JS.Thread->java.lang.Thread mapping is 1:1 */
- public static class Thread {
+ /** encapsulates a single JavaScript thread; the JS.Context->java.lang.Thread mapping is 1:1 */
+ public static class Context {
- CompiledFunction currentCompiledFunction = null;
+ CompiledFunction f = null;
Vec stack = new Vec();
public Scope scope = null;
int pc = 0;
- boolean paused = false;
- public Thread(CompiledFunction function) { this(function, null); }
- public Thread(CompiledFunction function, Scope scope) { this(function, scope, new JS.Array()); }
- public Thread(CompiledFunction function, Scope scope, JS.Array args) {
+ /** return this to signal a pause */
+ public static Object pause = new Object();
+
+ public static int getLine() { return current().f == null ? -1 : current().f.getLine(current()); }
+ public static String getSourceName() { return current().f == null ? null : current().f.getSourceName(); }
+
+ public Context(CompiledFunction function, Scope scope, JS.Array args) {
if (scope == null) scope = new CompiledFunctionImpl.FunctionScope("unknown", function.parentScope);
+ if (args == null) args = new JS.Array();
stack.push(new CompiledFunctionImpl.CallMarker(this));
stack.push(args);
- this.currentCompiledFunction = function;
+ this.f = function;
this.scope = scope;
}
- public static JS.Thread current() { return (JS.Thread)javaThreadToJSThread.get(java.lang.Thread.currentThread()); }
- public Object resume() { bind(); paused = false; Object ret = CompiledFunctionImpl.eval(this); unbind(); return ret; }
- public void pause() { paused = true; unbind(); }
- public void bind() { javaThreadToJSThread.put(java.lang.Thread.currentThread(), this); }
- public void unbind() { if (current() == this) javaThreadToJSThread.remove(java.lang.Thread.currentThread()); }
- public int getLine() { return currentCompiledFunction == null ? -1 : currentCompiledFunction.getLine(this); }
- public String getSourceName() { return currentCompiledFunction == null ? null : currentCompiledFunction.getSourceName(); }
+ public Object resume(Object o) {
+ try {
+ javaContextToJSContext.put(Thread.currentThread(), this);
+ return CompiledFunctionImpl.eval(this, o);
+ } finally {
+ javaContextToJSContext.remove(Thread.currentThread());
+ }
+ }
/** fetches the currently-executing javascript function */
- public JS.CompiledFunction getCurrentCompiledFunction() { return currentCompiledFunction; }
- private static Hashtable javaThreadToJSThread = new Hashtable();
+ public static JS.Context current() { return (JS.Context)javaContextToJSContext.get(Thread.currentThread()); }
+ private static Hashtable javaContextToJSContext = new Hashtable();
}
}