import org.xwt.util.*;
import java.io.*;
-/** a JavaScript function, compiled into bytecode */
-public class Function extends JS.Obj implements ByteCodes, Tokens {
-
- public int getNumFormalArgs() { return numFormalArgs; }
+/** A JavaScript function, compiled into bytecode */
+public class JSFunction extends JSCallable implements ByteCodes, Tokens {
+
+ /** Note: code gets run in an <i>unpauseable</i> context. */
+ public Object call(JSArray args) {
+ Context cx = new JSContext(this, false);
+ cx.invoke(args);
+ return cx.stack.pop();
+ }
// Fields and Accessors ///////////////////////////////////////////////
int[] op = new int[10]; ///< the instructions
Object[] arg = new Object[10]; ///< the arguments to the instructions
int size = 0; ///< the number of instruction/argument pairs
- JS.Scope parentScope; ///< the default scope to use as a parent scope when executing this
+ JSScope parentJSScope; ///< the default scope to use as a parent scope when executing this
// Constructors ////////////////////////////////////////////////////////
- private Function cloneWithNewParentScope(JS.Scope s) {
- Function ret = new Function(sourceName, firstLine, s);
+ public JSFunction cloneWithNewParentJSScope(JSScope s) {
+ JSFunction ret = new JSFunction(sourceName, firstLine, s);
// Reuse the same op, arg, line, and size variables for the new "instance" of the function
// NOTE: Neither *this* function nor the new function should be modified after this call
ret.op = this.op;
return ret;
}
- private Function(String sourceName, int firstLine, JS.Scope parentScope) {
+ private JSFunction(String sourceName, int firstLine, JSScope parentJSScope) {
this.sourceName = sourceName;
this.firstLine = firstLine;
- this.parentScope = parentScope;
+ this.parentJSScope = parentJSScope;
}
- protected Function(String sourceName, int firstLine, Reader sourceCode, JS.Scope parentScope) throws IOException {
- this(sourceName, firstLine, parentScope);
+ protected JSFunction(String sourceName, int firstLine, Reader sourceCode, JSScope parentJSScope) throws IOException {
+ this(sourceName, firstLine, parentJSScope);
if (sourceCode == null) return;
Parser p = new Parser(sourceCode, sourceName, firstLine);
while(true) {
void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
void set(int pos, Object arg_) { arg[pos] = arg_; }
int pop() { size--; arg[size] = null; return op[size]; }
- void paste(Function other) { for(int i=0; i<other.size; i++) add(other.line[i], other.op[i], other.arg[i]); }
- Function add(int line, int op_) { return add(line, op_, null); }
- Function add(int line, int op_, Object arg_) {
+ void paste(JSFunction other) { for(int i=0; i<other.size; i++) add(other.line[i], other.op[i], other.arg[i]); }
+ JSFunction add(int line, int op_) { return add(line, op_, null); }
+ JSFunction add(int line, int op_, Object arg_) {
if (size == op.length - 1) {
int[] line2 = new int[op.length * 2]; System.arraycopy(this.line, 0, line2, 0, op.length); this.line = line2;
Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
// Invoking the Bytecode ///////////////////////////////////////////////////////
/** returns false if the thread has been paused */
- static Object eval(final Context cx) throws JS.Exn {
+ static Object eval(final JSContext cx) throws JS.Exn {
+ final 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];
- Object returnedFromJava = null;
- boolean checkReturnedFromJava = false;
if(op == FINALLY_DONE) {
FinallyData fd = (FinallyData) cx.stack.pop();
if(fd == null) continue OUTER; // NOP
}
switch(op) {
case LITERAL: cx.stack.push(arg); break;
- case OBJECT: cx.stack.push(new JS.Obj()); break;
- case ARRAY: cx.stack.push(new JS.Array(JS.toNumber(arg).intValue())); 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 POP: cx.stack.pop(); break;
case SWAP: { Object o1 = cx.stack.pop(); Object o2 = cx.stack.pop(); cx.stack.push(o1); cx.stack.push(o2); break; }
case DUP: cx.stack.push(cx.stack.peek()); break;
- case NEWSCOPE: cx.scope = new JS.Scope(cx.scope); break;
- case OLDSCOPE: cx.scope = cx.scope.getParentScope(); 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(((Function)arg).cloneWithNewParentScope(cx.scope)); break;
+ case NEWFUNCTION: cx.stack.push(((JSFunction)arg).cloneWithNewParentJSScope(cx.scope)); break;
case LABEL: break;
case TYPEOF: {
case PUSHKEYS: {
Object o = cx.stack.peek();
- Object[] keys = ((JS)o).keys();
- JS.Array a = new JS.Array();
- a.setSize(keys.length);
- for(int j=0; j<keys.length; j++) a.setElementAt(keys[j], j);
+ Enumeration e = ((JS)o).keys();
+ JSArray a = new JSArray();
+ while(e.hasMoreElements()) a.addElement(e.nextElement());
cx.stack.push(a);
break;
}
cx.pc = ((TryMarker)o).finallyLoc - 1;
continue OUTER;
} else if (o instanceof CallMarker) {
+ if (cx.scope instanceof JSTrapScope) {
+ JSTrapScope ts = (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) {
+ ts.trappee.put(key, val);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ } else {
+ cx.stack.push(o);
+ JSArray args = new JSArray();
+ args.addElement(ts.val);
+ cx.stack.push(ta);
+ cx.f = t.f;
+ cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, ts.val);
+ cx.pc = -1;
+ break;
+ }
+ }
+ }
cx.scope = ((CallMarker)o).scope;
cx.pc = ((CallMarker)o).pc;
- cx.f = (Function)((CallMarker)o).f;
+ cx.f = (JSFunction)((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");
- returnedFromJava = ((JS)target).put(key, val);
- if (returnedFromJava != null) checkReturnedFromJava = true;
- else cx.stack.push(val);
+ 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 && key.equals("cascade")) {
+ JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)o;
+ t = ts.t.next;
+ ts.cascadeHappened = true;
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ if (t == null) o = ts.t.trappee;
+ }
+ if (t != null) {
+ cx.stack.push(new CallMarker(cx));
+ JSArray args = new JSArray();
+ args.addElement(val);
+ cx.stack.push(ta);
+ cx.f = t.f;
+ cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, val);
+ cx.pc = -1;
+ break;
+ }
+ ((JS)target).put(key, val);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ cx.stack.push(val);
break;
}
cx.stack.push(ret);
break;
} else if (o instanceof JS) {
- returnedFromJava = ((JS)o).get(v);
- checkReturnedFromJava = true;
+ 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 && key.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.trappee;
+ }
+ if (t != null) {
+ cx.stack.push(new CallMarker(cx));
+ JSArray args = new JSArray();
+ cx.stack.push(ta);
+ cx.f = t.f;
+ cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, null);
+ ((JSTrap.JSTrapScope)cx.scope).cascadeHappened = true;
+ cx.pc = -1;
+ break;
+ }
+ ret = ((JS)o).get(v);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ cx.stack.push(ret);
break;
}
throw je("tried to get property " + v + " from a " + o.getClass().getName());
}
- case CALL: case CALLMETHOD: case CALL_REVERSED: {
- JS.Array arguments = new JS.Array();
+ case CALL: case CALLMETHOD: {
int numArgs = JS.toNumber(arg).intValue();
- arguments.setSize(numArgs);
- Object o = null;
- if (op == CALL_REVERSED) o = cx.stack.pop();
- for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
- if (op != CALL_REVERSED) o = cx.stack.pop();
+ Object o = cx.stack.pop();
if(o == null) throw je("attempted to call null");
Object ret;
-
+ Object method = null;
if(op == CALLMETHOD) {
- Object method = o;
+ method = o;
+ if (method == null) throw new JS.Exn("cannot call the null method");
o = cx.stack.pop();
if(o instanceof String || o instanceof Number || o instanceof Boolean) {
+ JSArray arguments = new JSArray();
+ for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
ret = Internal.callMethodOnPrimitive(o,method,arguments);
cx.stack.push(ret);
cx.pc += 2; // skip the GET and CALL
- } 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 JSCallable) {
+ // fall through
} else {
// 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;
}
- } else if (o instanceof Function) {
+ } else if (o instanceof JSFunction) {
+ // FEATURE: use something similar to call0/call1/call2 here
+ JSArray 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 = (Function)o;
- cx.scope = new Scope(cx.f.parentScope);
+ cx.f = (JSFunction)o;
+ cx.scope = new JSScope(cx.f.parentJSScope);
cx.pc = -1;
-
- } else {
- returnedFromJava = ((JS.Callable)o).call(arguments);
- checkReturnedFromJava = true;
+ break;
+ }
+
+ JSCallable c = ((JSCallable)o);
+ switch(numArgs) {
+ case 0: ret = c.call0(method); break;
+ case 1: ret = c.call1(method, cx.stack.pop()); break;
+ case 2: ret = c.call2(method, cx.stack.pop(), cx.stack.pop()); break;
+ default: {
+ JSArray arguments = new JSArray();
+ for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
+ ret = c.call(method, arguments);
+ if (cx.pausecount > initialPauseCount) return; // we were paused
+ break;
+ }
}
+ cx.stack.push(ret);
+ if (method != null) cx.pc += 2; // skip the GET and CALL if this was a GETCALL
+
break;
}
throw new JS.Exn(o);
}
- case INC: case DEC: {
- boolean isPrefix = JS.toBoolean(arg);
- Object key = cx.stack.pop();
- JS obj = (JS)cx.stack.pop();
- Number num = JS.toNumber(obj.get(key));
- Number val = new Double(op == INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
- obj.put(key, val);
- cx.stack.push(isPrefix ? val : num);
- break;
- }
-
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 Function && obj instanceof JS.Scope) {
- JS.Scope parent = (JS.Scope)obj;
- while(parent.getParentScope() != null) parent = parent.getParentScope();
- if (parent instanceof org.xwt.Box) {
- org.xwt.Box b = (org.xwt.Box)parent;
- if (op == ASSIGN_ADD) b.addTrap(key, val); else b.delTrap(key, val);
+ 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;
} }
}
- // 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 Scope(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();
// Debugging //////////////////////////////////////////////////////////////////////
- public String toString() { return "Function [" + sourceName + ":" + firstLine + "]"; }
+ public String toString() { return "JSFunction [" + sourceName + ":" + firstLine + "]"; }
public String dump() {
StringBuffer sb = new StringBuffer(1024);
sb.append("\n" + sourceName + ": " + firstLine + "\n");
static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } }
static EvaluatorException ee(String s) {
- throw new EvaluatorException(Context.getSourceName() + ":" + Context.getLine() + " " + s);
+ throw new EvaluatorException(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
}
static JS.Exn je(String s) {
- throw new JS.Exn(Context.getSourceName() + ":" + Context.getLine() + " " + s);
+ throw new JS.Exn(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
}
public static class CallMarker {
int pc;
- Scope scope;
- Function f;
- public CallMarker(Context cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
+ JSScope scope;
+ JSFunction f;
+ public CallMarker(JSContext cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
}
public static class CatchMarker { public CatchMarker() { } }
public static class LoopMarker {
public int location;
public String label;
- public JS.Scope scope;
- public LoopMarker(int location, String label, JS.Scope scope) {
+ 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 JS.Scope scope;
- public TryMarker(int catchLoc, int finallyLoc, JS.Scope scope) {
+ public JSScope scope;
+ public TryMarker(int catchLoc, int finallyLoc, JSScope scope) {
this.catchLoc = catchLoc;
this.finallyLoc = finallyLoc;
this.scope = scope;