2003/07/06 13:42:15
authorbrian <brian@xwt.org>
Fri, 30 Jan 2004 07:03:24 +0000 (07:03 +0000)
committerbrian <brian@xwt.org>
Fri, 30 Jan 2004 07:03:24 +0000 (07:03 +0000)
darcs-hash:20040130070324-aa32f-2ef0a004df2a90c3164158b76518b6c77e46ee36.gz

src/org/xwt/js/ByteCodes.java
src/org/xwt/js/CompiledFunctionImpl.java
src/org/xwt/js/Parser.java
src/org/xwt/js/ScopeImpl.java

index bac280a..5dc2aff 100644 (file)
@@ -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"
     };
 }
index 0f08f2b..e8fa95f 100644 (file)
@@ -114,7 +114,15 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
         OUTER: for(pc=0; pc<size; pc++) {
             String label = null;
             cx.pc = lastPC = pc;
-            switch(op[pc]) {
+            int curOP = op[pc];
+            Object curArg = arg[pc];
+            if(curOP == FINALLY_DONE) {
+                FinallyData fd = (FinallyData) t.pop();
+                if(fd == null) continue OUTER; // NOP
+                curOP = fd.op;
+                curArg = fd.arg;
+            }
+            switch(curOP) {
             case LITERAL: t.push(arg[pc]); break;
             case OBJECT: t.push(new JS.Obj()); break;
             case ARRAY: t.push(new JS.Array(JS.toNumber(arg[pc]).intValue())); break;
@@ -126,8 +134,8 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
             case POP: t.pop(); break;
             case SWAP: { Object o1 = t.pop(); Object o2 = t.pop(); t.push(o1); t.push(o2); break; }
             case DUP: t.push(t.peek()); break;
-            case NEWSCOPE: /*s = new JS.Scope(s);*/ break;
-            case OLDSCOPE: /*s = s.getParentScope();*/ break;
+            case NEWSCOPE: s = new JS.Scope(s); break;
+            case OLDSCOPE: s = s.getParentScope(); break;
             case ASSERT: if (!JS.toBoolean(t.pop())) throw je("assertion failed"); break;
             case BITNOT: t.push(new Long(~JS.toLong(t.pop()))); break;
             case BANG: t.push(new Boolean(!JS.toBoolean(t.pop()))); break;
@@ -164,8 +172,7 @@ class CompiledFunctionImpl extends JSCallable implements ByteCodes, Tokens {
 
             case LABEL: break;
             case LOOP: {
-                t.push(s);
-                t.push(new LoopMarker(pc, 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 */
index 3a3dcd9..49b5d34 100644 (file)
@@ -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;
         }
             
index 52a74f5..9185868 100644 (file)
@@ -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; }
 }