import org.xwt.util.*;
import java.io.*;
-// FIXME: could use some cleaning up
/** a JavaScript function, compiled into bytecode */
-class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
+class CompiledFunctionImpl extends JS.Callable implements ByteCodes, Tokens {
// Fields and Accessors ///////////////////////////////////////////////
+ /** the number of formal arguments */
+ int numFormalArgs = 0;
+
/** the source code file that this block was drawn from */
private String sourceName;
public String getSourceName() throws JS.Exn { return sourceName; }
private CompiledFunctionImpl cloneWithNewParentScope(JS.Scope s) throws IOException {
CompiledFunctionImpl ret = new JS.CompiledFunction(sourceName, firstLine, null, s);
- ret.paste(this);
+ // 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;
+ ret.arg = this.arg;
+ ret.line = this.line;
+ ret.size = this.size;
return ret;
}
try {
cx.currentCompiledFunction = (CompiledFunction)this;
int size = cx.stack.size();
- cx.stack.push(new CallMarker());
+ cx.stack.push(callMarker);
cx.stack.push(args);
eval(scope);
Object ret = cx.stack.pop();
- // FIXME: if we catch an exception in Java, JS won't notice and the stack will be messed up
if (cx.stack.size() > size)
// this should never happen
- throw new Error("ERROR: stack grew by " + (cx.stack.size() - size) + " elements during call");
+ 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;
}
int pc;
int lastPC = -1;
OUTER: for(pc=0; pc<size; pc++) {
+ try {
String label = null;
cx.pc = lastPC = pc;
int curOP = op[pc];
for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
Object o = t.pop();
if(o == null) throw je("attempted to call null");
- try {
- Object ret;
- if(op[pc] == CALLMETHOD) {
- Object method = o;
- o = t.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 {
- ret = ((JS.Callable)o).call(arguments);
- }
- t.push(ret);
- break;
- } catch (JS.Exn e) {
- t.push(e);
+ Object ret;
+ if(op[pc] == CALLMETHOD) {
+ Object method = o;
+ o = t.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 {
+ ret = ((JS.Callable)o).call(arguments);
}
+ t.push(ret);
+ break;
}
// fall through if exception was thrown
case THROW: {
- Object exn = t.pop();
- while(t.size() > 0) {
- Object o = t.pop();
- if (o instanceof CatchMarker || o instanceof TryMarker) {
- boolean inCatch = o instanceof CatchMarker;
- if(inCatch) {
- o = t.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((exn instanceof JS.Exn) ? ((JS.Exn)exn).getObject() : exn);
- s = ((TryMarker)o).scope;
- pc = ((TryMarker)o).catchLoc - 1;
- continue OUTER;
- } else {
- t.push(exn);
- t.push(new FinallyData(THROW));
- s = ((TryMarker)o).scope;
- pc = ((TryMarker)o).finallyLoc - 1;
- continue OUTER;
- }
- }
- // no handler found within this func
- if(o instanceof CallMarker) {
- if(exn instanceof JS.Exn)
- throw (JS.Exn)exn;
- else
- throw new JS.Exn(exn);
- }
- }
- throw new Error("error: THROW invoked but couldn't find a Try or Call Marker!");
+ Object o = t.pop();
+ if(o instanceof JS.Exn) throw (JS.Exn)o;
+ throw new JS.Exn(o);
}
case INC: case DEC: {
t.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();
+ if (val instanceof CompiledFunction) {
+ if (obj instanceof JS.Scope) {
+ JS.Scope parent = (JS.Scope)obj;
+ while(parent.getParentScope() != null) parent = parent.getParentScope();
+ if (parent instanceof org.xwt.Box) {
+ if (curOP == ASSIGN_ADD) {
+ ((org.xwt.Box)parent).addTrap(key, val);
+ } else {
+ ((org.xwt.Box)parent).delTrap(key, val);
+ }
+ // skip over the "normal" implementation of +=/-=
+ pc += ((Integer)arg[pc]).intValue() - 1;
+ break;
+ }
+ }
+ }
+ // use the "normal" implementation
+ t.push(key);
+ t.push(old);
+ t.push(arg);
+ break;
+ }
+
+ case ADD: {
+ int count = ((Number)arg[pc]).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)));
+ } else {
+ Object[] args = new Object[count];
+ while(--count >= 0) args[count] = t.pop();
+ if(args[0] instanceof String) {
+ StringBuffer sb = new StringBuffer(64);
+ for(int i=0;i<args.length;i++) sb.append(JS.toString(args[i]));
+ t.push(sb.toString());
+ } else {
+ int numStrings = 0;
+ for(int i=0;i<args.length;i++) if(args[i] instanceof String) numStrings++;
+ if(numStrings == 0) {
+ double d = 0.0;
+ for(int i=0;i<args.length;i++) d += JS.toDouble(args[i]);
+ t.push(new Double(d));
+ } else {
+ double d=0.0;
+ int i=0;
+ do {
+ d += JS.toDouble(args[i++]);
+ } while(!(args[i] instanceof String));
+ StringBuffer sb = new StringBuffer(64);
+ sb.append(JS.toString(new Double(d)));
+ while(i < args.length) sb.append(JS.toString(args[i++]));
+ t.push(sb.toString());
+ }
+ }
+ }
+ break;
+ }
default: {
Object right = t.pop();
Object left = t.pop();
switch(op[pc]) {
-
- case ADD: {
- Object l = left;
- Object r = right;
- if (l instanceof String || r instanceof String) {
- if (l == null) l = "null";
- if (r == null) r = "null";
- if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
- l = new Long(((Number)l).longValue());
- if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
- r = new Long(((Number)r).longValue());
- t.push(l.toString() + r.toString()); break;
- }
- t.push(new Double(JS.toDouble(l) + JS.toDouble(r))); break;
- }
case BITOR: t.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
case BITXOR: t.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
default: throw new Error("unknown opcode " + op[pc]);
} }
}
- }
+ } catch(JS.Exn e) {
+ while(t.size() > 0) {
+ Object o = t.pop();
+ if (o instanceof CatchMarker || o instanceof TryMarker) {
+ boolean inCatch = o instanceof CatchMarker;
+ if(inCatch) {
+ o = t.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;
+ continue OUTER;
+ } else {
+ t.push(e);
+ t.push(new FinallyData(THROW));
+ s = ((TryMarker)o).scope;
+ 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!");
+ } // 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);
}
// FunctionScope /////////////////////////////////////////////////////////////////
- private class FunctionScope extends JS.Scope {
+ private static class FunctionScope extends JS.Scope {
String sourceName;
public FunctionScope(String sourceName, Scope parentScope) { super(parentScope); this.sourceName = sourceName; }
public String getSourceName() { return sourceName; }
// Markers //////////////////////////////////////////////////////////////////////
public static class CallMarker { public CallMarker() { } }
+ private static CallMarker callMarker = new CallMarker();
public static class CatchMarker { public CatchMarker() { } }
private static CatchMarker catchMarker = new CatchMarker();