X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fxwt%2Fjs%2FCompiledFunctionImpl.java;h=e8fa95fe9222f1ae49ee627aa9b6e50816138a2e;hb=650cc315549ff0d546472f8fc3b9426119e8d9fa;hp=6bcff6eaa8e3042d398fddc5b5f9e528f704cae1;hpb=5323fded028920414c575845c2e1436a7c4c4785;p=org.ibex.core.git diff --git a/src/org/xwt/js/CompiledFunctionImpl.java b/src/org/xwt/js/CompiledFunctionImpl.java index 6bcff6e..e8fa95f 100644 --- a/src/org/xwt/js/CompiledFunctionImpl.java +++ b/src/org/xwt/js/CompiledFunctionImpl.java @@ -33,7 +33,6 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { /** 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; - // Constructors //////////////////////////////////////////////////////// private CompiledFunctionImpl cloneWithNewParentScope(JS.Scope s) throws IOException { @@ -53,7 +52,8 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { 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)); } @@ -67,7 +67,10 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { 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; @@ -94,18 +97,32 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { 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 0 && op[pc - 1] == LABEL ? (String)arg[pc - 1] : (String)null)); + t.push(new LoopMarker(pc, pc > 0 && op[pc - 1] == LABEL ? (String)arg[pc - 1] : (String)null,s)); t.push(Boolean.TRUE); break; } @@ -167,21 +182,31 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { while(t.size() > 0) { Object o = t.pop(); if (o instanceof CallMarker) ee("break or continue not within a loop"); - if (o != null && o instanceof LoopMarker) { - if (arg[pc] == null || arg[pc].equals(((LoopMarker)o).label)) { + 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; + 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 = (JS.Scope)t.pop(); - if (op[pc] == CONTINUE) { t.push(s); t.push(o); t.push(Boolean.FALSE); } - pc = op[pc] == BREAK ? endOfLoop - 1 : loopInstructionLocation; + s = ((LoopMarker)o).scope; + if (curOP == CONTINUE) { t.push(o); t.push(Boolean.FALSE); } + pc = curOP == BREAK ? endOfLoop - 1 : loopInstructionLocation; continue OUTER; } } } - throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + sourceName + ":" + line); + throw new Error("CONTINUE/BREAK invoked but couldn't find a LoopMarker at " + sourceName + ":" + getLine(pc)); case TRY: { - t.push(new TryMarker(pc + ((Integer)arg[pc]).intValue(), s)); + int[] jmps = (int[]) arg[pc]; + // 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)); break; } @@ -189,7 +214,15 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { Object retval = t.pop(); while(t.size() > 0) { Object o = t.pop(); - if (o != null && o instanceof CallMarker) { + 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; + continue OUTER; + } + if (o instanceof CallMarker) { t.push(retval); return; } @@ -205,6 +238,8 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { throw je("tried to put a value to the " + key + " property on the null value"); if (!(target instanceof JS)) 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"); ((JS)target).put(key, val); t.push(val); break; @@ -223,29 +258,41 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { } 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 (o instanceof String) { - ret = getFromString((String)o, v); - } else if (o instanceof Boolean) { - throw je("Not Implemented: properties on Boolean objects"); - } else if (o instanceof Number) { - Log.log(this, "Not Implemented: properties on Number objects"); - } else if (o instanceof JS) { + 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) ret = ((JS)o).get(v); - } + else + throw je("tried to get property " + v + " from a " + o.getClass().getName()); t.push(ret); break; } - - case CALL: { + + case CALLMETHOD: + case CALL: + { JS.Array arguments = new JS.Array(); int numArgs = JS.toNumber(arg[pc]).intValue(); arguments.setSize(numArgs); for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j); - JS.Callable f = (JS.Callable)t.pop(); - if (f == null) throw je("attempted to call null"); + Object o = t.pop(); + if(o == null) throw je("attempted to call null"); try { - t.push(f.call(arguments)); + 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); @@ -256,14 +303,37 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { Object exn = t.pop(); while(t.size() > 0) { Object o = t.pop(); - if (o instanceof TryMarker) { - t.push(exn); - pc = ((TryMarker)o).location - 1; - s = ((TryMarker)o).scope; - continue OUTER; + 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 JS.Exn(exn); + throw new Error("error: THROW invoked but couldn't find a Try or Call Marker!"); } case INC: case DEC: { @@ -331,6 +401,7 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { 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()); @@ -342,52 +413,41 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { } } } } + // 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); } - - // Helpers for Number, String, and Boolean //////////////////////////////////////// - - private static Object getFromString(final String o, final Object v) { - if (v.equals("length")) return new Integer(((String)o).length()); - else if (v.equals("substring")) return new JS.Callable() { - public Object call(JS.Array args) { - if (args.length() == 1) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue()); - else if (args.length() == 2) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue(), - JS.toNumber(args.elementAt(1)).intValue()); - else throw new JS.Exn("String.substring() can only take one or two arguments"); - } - }; - else if (v.equals("toLowerCase")) return new JS.Callable() { - public Object call(JS.Array args) { - return ((String)o).toLowerCase(); - } }; - else if (v.equals("toUpperCase")) return new JS.Callable() { - public Object call(JS.Array args) { - return ((String)o).toString().toUpperCase(); - } }; - else if (v.equals("charAt")) return new JS.Callable() { - public Object call(JS.Array args) { - return ((String)o).charAt(JS.toNumber(args.elementAt(0)).intValue()) + ""; - } }; - else if (v.equals("lastIndexOf")) return new JS.Callable() { - public Object call(JS.Array args) { - if (args.length() != 1) return null; - return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString())); - } }; - else if (v.equals("indexOf")) return new JS.Callable() { - public Object call(JS.Array args) { - if (args.length() != 1) return null; - return new Integer(((String)o).indexOf(args.elementAt(0).toString())); - } }; - throw new JS.Exn("Not Implemented: propery " + v + " on String objects"); - } - + // 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 ").append(i+((Number) arg[i]).intValue()); + } else if(op[i] == TRY) { + int[] jmps = (int[]) arg[i]; + sb.append(" catch: ").append(jmps[0] < 0 ? "No catch block" : ""+(i+jmps[0])); + sb.append(" finally: ").append(jmps[1] < 0 ? "No finally block" : ""+(i+jmps[1])); + } + sb.append("\n"); + } + return sb.toString(); + } // 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 ///////////////////////////////////////////////////////////////// @@ -396,36 +456,45 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { String sourceName; public FunctionScope(String sourceName, Scope parentScope) { super(parentScope); this.sourceName = sourceName; } 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("cascade")) return org.xwt.Trap.cascadeFunction; - return super.get(key); - } } // Markers ////////////////////////////////////////////////////////////////////// public static class CallMarker { public CallMarker() { } } + + public static class CatchMarker { public CatchMarker() { } } + private static CatchMarker catchMarker = new CatchMarker(); + public static class LoopMarker { public int location; public String label; - public LoopMarker(int location, String label) { + public JS.Scope scope; + public LoopMarker(int location, String label, JS.Scope scope) { this.location = location; this.label = label; + this.scope = scope; } } public static class TryMarker { - public int location; + public int catchLoc; + public int finallyLoc; public JS.Scope scope; - public TryMarker(int location, JS.Scope scope) { - this.location = location; + public TryMarker(int catchLoc, int finallyLoc, JS.Scope scope) { + this.catchLoc = catchLoc; + this.finallyLoc = finallyLoc; this.scope = scope; } } - + public static class FinallyData { + public int op; + public Object arg; + public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; } + public FinallyData(int op) { this(op,null); } + } } -class JSCallable extends JS.Callable { +/** 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; }