From 650cc315549ff0d546472f8fc3b9426119e8d9fa Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 30 Jan 2004 07:03:24 +0000 Subject: [PATCH] 2003/07/06 13:42:15 darcs-hash:20040130070324-aa32f-2ef0a004df2a90c3164158b76518b6c77e46ee36.gz --- src/org/xwt/js/ByteCodes.java | 7 +- src/org/xwt/js/CompiledFunctionImpl.java | 116 +++++++++++++++++++++++------- src/org/xwt/js/Parser.java | 41 ++++++++--- src/org/xwt/js/ScopeImpl.java | 1 + 4 files changed, 126 insertions(+), 39 deletions(-) diff --git a/src/org/xwt/js/ByteCodes.java b/src/org/xwt/js/ByteCodes.java index bac280a..5dc2aff 100644 --- a/src/org/xwt/js/ByteCodes.java +++ b/src/org/xwt/js/ByteCodes.java @@ -77,10 +77,13 @@ interface ByteCodes { Has a similar effect a a GET followed by a CALL */ public static final byte CALLMETHOD = -23; - + /** finish a finally block and carry out whatever instruction initiated the finally block */ + public static final byte FINALLY_DONE = -24; + public static final String[] bytecodeToString = new String[] { "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE", "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS", - "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD" + "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD", + "FINALLY_DONE" }; } diff --git a/src/org/xwt/js/CompiledFunctionImpl.java b/src/org/xwt/js/CompiledFunctionImpl.java index 0f08f2b..e8fa95f 100644 --- a/src/org/xwt/js/CompiledFunctionImpl.java +++ b/src/org/xwt/js/CompiledFunctionImpl.java @@ -114,7 +114,15 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { 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; } @@ -175,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; } @@ -197,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; } @@ -278,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: { @@ -384,8 +432,11 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { 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(" 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"); } @@ -411,23 +462,36 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens { // 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); } + } } /** this class exists solely to work around a GCJ bug */ diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index 3a3dcd9..49b5d34 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -596,37 +596,56 @@ class Parser extends Lexer implements ByteCodes { } case TRY: { - b.add(parserLine, TRY); - int size = b.size(); - parseStatement(b, null); // parse the expression to be TRYed - b.add(parserLine, POP); // pop the TryMarker - b.add(parserLine, JMP); // jump forward to the end of the catch block - int size2 = b.size(); - b.set(size - 1, new Integer(b.size() - size + 1)); // the TRY argument points at the start of the CATCH block + b.add(parserLine, TRY); // try bytecode causes a TryMarker to be pushed + int tryInsn = b.size() - 1; + // parse the expression to be TRYed + parseStatement(b, null); + // pop the try marker. this is pushed when the TRY bytecode is executed + b.add(parserLine, POP); + // jump forward to the end of the catch block, start of the finally block + b.add(parserLine, JMP); + int successJMPInsn = b.size() - 1; + + if (peekToken() != CATCH && peekToken() != FINALLY) + throw pe("try without catch or finally"); + int catchJMPDistance = -1; if (peekToken() == CATCH) { + catchJMPDistance = b.size() - tryInsn; + String exceptionVar; getToken(); consume(LP); consume(NAME); + exceptionVar = string; consume(RP); b.add(parserLine, TOPSCOPE); // the exception is on top of the stack; put it to the chosen name b.add(parserLine, SWAP); - b.add(parserLine, LITERAL); + b.add(parserLine, LITERAL,exceptionVar); b.add(parserLine, SWAP); b.add(parserLine, PUT); b.add(parserLine, POP); b.add(parserLine, POP); parseStatement(b, null); + // pop the try and catch markers + b.add(parserLine,POP); + b.add(parserLine,POP); } // jump here if no exception was thrown - b.set(size2 - 1, new Integer(b.size() - size2 + 1)); - - // FIXME: not implemented correctly + b.set(successJMPInsn, new Integer(b.size() - successJMPInsn)); + + int finallyJMPDistance = -1; if (peekToken() == FINALLY) { + b.add(parserLine, LITERAL, null); // null FinallyData + finallyJMPDistance = b.size() - tryInsn; consume(FINALLY); parseStatement(b, null); + b.add(parserLine,FINALLY_DONE); } + + // setup the TRY arguments + b.set(tryInsn, new int[] { catchJMPDistance, finallyJMPDistance }); + break; } diff --git a/src/org/xwt/js/ScopeImpl.java b/src/org/xwt/js/ScopeImpl.java index 52a74f5..9185868 100644 --- a/src/org/xwt/js/ScopeImpl.java +++ b/src/org/xwt/js/ScopeImpl.java @@ -31,4 +31,5 @@ class ScopeImpl extends JS.Obj { if (isTransparent()) parentScope.declare(s); else super.put(s, NULL_PLACEHOLDER); } + public Scope getParentScope() { return parentScope; } } -- 1.7.10.4