p.parseStatement(this, null);
if (s == size()) break;
}
- add(-1, Tokens.RETURN);
+ add(-1, LITERAL, null);
+ add(-1, RETURN);
}
public Object call(JS.Array args) throws JS.Exn { return call(args, new FunctionScope(sourceName, parentScope)); }
cx.stack.push(args);
eval(scope);
Object ret = cx.stack.pop();
- if (cx.stack.size() > size) Log.logJS(this, "warning, stack grew by " + (cx.stack.size() - size) + " elements during call");
+ // 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");
return ret;
} finally {
cx.currentCompiledFunction = saved;
size++;
return this;
}
+
+ public int getLine(int pc) {
+ if(pc < 0 || pc >= size) return -1;
+ return line[pc];
+ }
// Invoking the Bytecode ///////////////////////////////////////////////////////
- int pc = 0;
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<size; pc++) {
String label = null;
- cx.line = line[pc];
+ cx.pc = lastPC = pc;
switch(op[pc]) {
case LITERAL: t.push(arg[pc]); break;
case OBJECT: t.push(new JS.Obj()); break;
}
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 " + v);
+ if (v == null) throw je("tried to get the null key from " + o);
if (o instanceof String) {
ret = getFromString((String)o, v);
} else if (o instanceof Boolean) {
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());
} }
}
}
+ // 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);
}
+ // Debugging //////////////////////////////////////////////////////////////////////
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer(1024);
+ sb.append("\n" + sourceName + ": " + firstLine + "\n");
+ for (int i=0; i < size; i++) {
+ sb.append(i);
+ sb.append(": ");
+ if (op[i] < 0)
+ sb.append(bytecodeToString[-op[i]]);
+ else
+ sb.append(codeToString[op[i]]);
+ sb.append(" ");
+ sb.append(arg[i] == null ? "(no arg)" : arg[i]);
+ if((op[i] == JF || op[i] == JT || op[i] == JMP) && arg[i] != null && arg[i] instanceof Number) {
+ sb.append(" jump to ");
+ sb.append(i+((Number) arg[i]).intValue());
+ }
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
// Helpers for Number, String, and Boolean ////////////////////////////////////////
if (args.length() != 1) return null;
return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
} };
+ else if(v.equals("match")) return new JS.Callable() {
+ public Object call(JS.Array args) {
+ return Regexp.stringMatch(o,args);
+ } };
+ else if(v.equals("search")) return new JS.Callable() {
+ public Object call(JS.Array args) {
+ return Regexp.stringSearch(o,args);
+ } };
+ else if(v.equals("replace")) return new JS.Callable() {
+ public Object call(JS.Array args) {
+ return Regexp.stringReplace(o,args);
+ } };
+ else if(v.equals("split")) return new JS.Callable() {
+ public Object call(JS.Array args) {
+ return Regexp.stringSplit(o,args);
+ } };
+
+
throw new JS.Exn("Not Implemented: propery " + v + " on String objects");
}
// Exception Stuff ////////////////////////////////////////////////////////////////
static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } }
- EvaluatorException ee(String s) { throw new EvaluatorException(sourceName + ":" + line[pc] + " " + s); }
- JS.Exn je(String s) { throw new JS.Exn(sourceName + ":" + line[pc] + " " + 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); }
// FunctionScope /////////////////////////////////////////////////////////////////
public String getSourceName() { return sourceName; }
public Object get(Object key) throws JS.Exn {
if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
+ else if (key.equals("trapname")) return org.xwt.Trap.currentTrapname();
else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
return super.get(key);
}
}
+/** this class exists solely to work around a GCJ bug */
abstract class JSCallable extends JS.Callable {
public abstract Object call(JS.Array args) throws JS.Exn;
}