jdk/src/share/classes/sun/tools/tree/FinallyStatement.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/tools/tree/FinallyStatement.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,360 @@
+/*
+ * Copyright 1994-2004 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.tools.tree;
+
+import sun.tools.java.*;
+import sun.tools.asm.Assembler;
+import sun.tools.asm.Label;
+import sun.tools.asm.TryData;
+import sun.tools.asm.CatchData;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+ * WARNING: The contents of this source file are not part of any
+ * supported API.  Code that depends on them does so at its own risk:
+ * they are subject to change or removal without notice.
+ */
+public
+class FinallyStatement extends Statement {
+    Statement body;
+    Statement finalbody;
+    boolean finallyCanFinish; // does finalBody never return?
+    boolean needReturnSlot;   // set by inner return statement
+    Statement init;           // try object expression  or declaration from parser
+    LocalMember tryTemp;      // temp holding the try object, if any
+
+    /**
+     * Constructor
+     */
+    public FinallyStatement(long where, Statement body, Statement finalbody) {
+        super(FINALLY, where);
+        this.body = body;
+        this.finalbody = finalbody;
+    }
+
+//    /**
+//     * Constructor for  try (init) {body}
+//     */
+//    public FinallyStatement(long where, Statement init, Statement body, int junk) {
+//      this(where, body, null);
+//      this.init = init;
+//    }
+
+    /**
+     * Check statement
+     */
+    Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) {
+        vset = reach(env, vset);
+        Hashtable newexp = new Hashtable();
+
+        // Handle the proposed 'try (init) { stmts } finally { stmts }' syntax.
+        // This feature has not been adopted, and support is presently disabled.
+        /*-----------------------------------------------------------*
+        if (init != null) {
+            ClassDefinition sourceClass = ctx.field.getClassDefinition();
+            Expression tryExpr = null;
+            DeclarationStatement tryDecl = null;
+            long where = init.getWhere();
+            // find out whether init is a simple expression or a declaration
+            if (init.getOp() == EXPRESSION) {
+                tryExpr = ((ExpressionStatement)init).expr;
+                init = null;    // restore it below
+                vset = tryExpr.checkValue(env, ctx, vset, exp);
+            } else if (init.getOp() == DECLARATION) {
+                tryDecl = (DeclarationStatement) init;
+                init = null;    // restore it below
+                vset = tryDecl.checkBlockStatement(env, ctx, vset, exp);
+                if (tryDecl.args.length != 1) {
+                    env.error(where, "invalid.decl");
+                } else {
+                    LocalMember field =
+                        ((VarDeclarationStatement) tryDecl.args[0]).field;
+                    tryExpr = new IdentifierExpression(where, field);
+                    tryExpr.type = field.getType();
+                }
+            } else {
+                env.error(where, "invalid.expr");
+                vset = init.check(env, ctx, vset, exp);
+            }
+            Type type = (tryExpr == null) ? Type.tError : tryExpr.getType();
+
+            MemberDefinition tryEnter = null;
+            MemberDefinition tryExit = null;
+            if (!type.isType(TC_CLASS)) {
+                if (!type.isType(TC_ERROR)) {
+                    env.error(where, "invalid.method.invoke", type);
+                }
+            } else {
+                Identifier idTryEnter = Identifier.lookup("tryEnter");
+                Identifier idTryExit = Identifier.lookup("tryExit");
+                Type tTryMethod = Type.tMethod(Type.tVoid);
+                try {
+                    ClassDefinition tryClass = env.getClassDefinition(type);
+                    tryEnter = tryClass.matchMethod(env, sourceClass, idTryEnter);
+                    tryExit = tryClass.matchMethod(env, sourceClass, idTryExit);
+                    if (tryEnter != null && !tryEnter.getType().equals(tTryMethod)) {
+                        tryEnter = null;
+                    }
+                    if (tryExit != null && !tryExit.getType().equals(tTryMethod)) {
+                        tryExit = null;
+                    }
+                } catch (ClassNotFound ee) {
+                    env.error(where, "class.not.found", ee.name, ctx.field);
+                } catch (AmbiguousMember ee) {
+                    Identifier id = ee.field1.getName();
+                    env.error(where, "ambig.field", id, ee.field1, ee.field2);
+                }
+            }
+            if (tryEnter == null || tryExit == null) {
+                // Make a better (more didactic) error here!
+                env.error(where, "invalid.method.invoke", type);
+            } else {
+                tryTemp = new LocalMember(where, sourceClass, 0,
+                                          type, Identifier.lookup("<try_object>"));
+                ctx = new Context(ctx, this);
+                ctx.declare(env, tryTemp);
+
+                Expression e;
+                e = new IdentifierExpression(where, tryTemp);
+                e = new AssignExpression(where, e, tryExpr);
+                e = new MethodExpression(where, e, tryEnter, new Expression[0]);
+                e.type = Type.tVoid;
+                Statement enterCall = new ExpressionStatement(where, e);
+                // store it on the init, for code generation
+                if (tryDecl != null) {
+                    Statement args2[] = { tryDecl.args[0], enterCall };
+                    tryDecl.args = args2;
+                    init = tryDecl;
+                } else {
+                    init = enterCall;
+                }
+                e = new IdentifierExpression(where, tryTemp);
+                e = new MethodExpression(where, e, tryExit, new Expression[0]);
+                e.type = Type.tVoid;
+                Statement exitCall = new ExpressionStatement(where, e);
+                finalbody = exitCall;
+            }
+        }
+        *-----------------------------------------------------------*/
+
+        // Check the try part. We reach the end of the try part either by
+        // finishing normally, or doing a break to the label of the try/finally.
+        // NOTE: I don't think newctx1.vsBreak is ever used -- see TryStatement.
+        CheckContext newctx1 = new CheckContext(ctx, this);
+        Vset vset1 = body.check(env, newctx1, vset.copy(), newexp)
+            .join(newctx1.vsBreak);
+        // Check the finally part.
+        CheckContext newctx2 = new CheckContext(ctx, this);
+        // Should never access this field.  The null indicates the finally part.
+        newctx2.vsContinue = null;
+        Vset vset2 = finalbody.check(env, newctx2, vset, exp);
+        finallyCanFinish = !vset2.isDeadEnd();
+        vset2 = vset2.join(newctx2.vsBreak);
+        // If !finallyCanFinish, then the only possible exceptions that can
+        // occur at this point are the ones preceding the try/finally, or
+        // the ones generated by the finally.  Anything in the try is
+        // irrelevant. Otherwise, we have to merge in all the exceptions
+        // generated by the body into exp.
+        if (finallyCanFinish) {
+            // Add newexp's back into exp; cf. ThrowStatement.check().
+            for (Enumeration e = newexp.keys() ; e.hasMoreElements() ; ) {
+                Object def = e.nextElement();
+                exp.put(def, newexp.get(def));
+            }
+        }
+        return ctx.removeAdditionalVars(vset1.addDAandJoinDU(vset2));
+    }
+
+    /**
+     * Inline
+     */
+    public Statement inline(Environment env, Context ctx) {
+        if (tryTemp != null) {
+            ctx = new Context(ctx, this);
+            ctx.declare(env, tryTemp);
+        }
+        if (init != null) {
+            init = init.inline(env, ctx);
+        }
+        if (body != null) {
+            body = body.inline(env, ctx);
+        }
+        if (finalbody != null) {
+            finalbody = finalbody.inline(env, ctx);
+        }
+        if (body == null) {
+            return eliminate(env, finalbody);
+        }
+        if (finalbody == null) {
+            return eliminate(env, body);
+        }
+        return this;
+    }
+
+    /**
+     * Create a copy of the statement for method inlining
+     */
+    public Statement copyInline(Context ctx, boolean valNeeded) {
+        FinallyStatement s = (FinallyStatement)clone();
+        if (tryTemp != null) {
+            s.tryTemp = tryTemp.copyInline(ctx);
+        }
+        if (init != null) {
+            s.init = init.copyInline(ctx, valNeeded);
+        }
+        if (body != null) {
+            s.body = body.copyInline(ctx, valNeeded);
+        }
+        if (finalbody != null) {
+            s.finalbody = finalbody.copyInline(ctx, valNeeded);
+        }
+        return s;
+     }
+
+    /**
+     * Compute cost of inlining this statement
+     */
+    public int costInline(int thresh, Environment env, Context ctx){
+        int cost = 4;
+        if (init != null) {
+            cost += init.costInline(thresh, env,ctx);
+            if (cost >= thresh) return cost;
+        }
+        if (body != null) {
+            cost += body.costInline(thresh, env,ctx);
+            if (cost >= thresh) return cost;
+        }
+        if (finalbody != null) {
+            cost += finalbody.costInline(thresh, env,ctx);
+        }
+        return cost;
+    }
+
+    /**
+     * Code
+     */
+    public void code(Environment env, Context ctx, Assembler asm) {
+        ctx = new Context(ctx);
+        Integer num1 = null, num2 = null;
+        Label endLabel = new Label();
+
+        if (tryTemp != null) {
+            ctx.declare(env, tryTemp);
+        }
+        if (init != null) {
+            CodeContext exprctx = new CodeContext(ctx, this);
+            init.code(env, exprctx, asm);
+        }
+
+        if (finallyCanFinish) {
+            LocalMember f1, f2;
+            ClassDefinition thisClass = ctx.field.getClassDefinition();
+
+            if (needReturnSlot) {
+                Type returnType = ctx.field.getType().getReturnType();
+                LocalMember localfield = new LocalMember(0, thisClass, 0,
+                                                       returnType,
+                                                       idFinallyReturnValue);
+                ctx.declare(env, localfield);
+                env.debugOutput("Assigning return slot to " + localfield.number);
+            }
+
+            // allocate space for the exception and return address
+            f1 = new LocalMember(where, thisClass, 0, Type.tObject, null);
+            f2 = new LocalMember(where, thisClass, 0, Type.tInt, null);
+            num1 = new Integer(ctx.declare(env, f1));
+            num2 = new Integer(ctx.declare(env, f2));
+        }
+
+        TryData td = new TryData();
+        td.add(null);
+
+        // Main body
+        CodeContext bodyctx = new CodeContext(ctx, this);
+        asm.add(where, opc_try, td); // start of protected code
+        body.code(env, bodyctx, asm);
+        asm.add(bodyctx.breakLabel);
+        asm.add(td.getEndLabel());   // end of protected code
+
+        // Cleanup afer body
+        if (finallyCanFinish) {
+            asm.add(where, opc_jsr, bodyctx.contLabel);
+            asm.add(where, opc_goto, endLabel);
+        } else {
+            // just goto the cleanup code.  It will never return.
+            asm.add(where, opc_goto, bodyctx.contLabel);
+        }
+
+        // Catch code
+        CatchData cd = td.getCatch(0);
+        asm.add(cd.getLabel());
+        if (finallyCanFinish) {
+            asm.add(where, opc_astore, num1); // store exception
+            asm.add(where, opc_jsr, bodyctx.contLabel);
+            asm.add(where, opc_aload, num1); // rethrow exception
+            asm.add(where, opc_athrow);
+        } else {
+            // pop exception off stack.  Fall through to finally code
+            asm.add(where, opc_pop);
+        }
+
+        // The finally part, which is marked by the contLabel.  Update
+        //    breakLabel: since break's in the finally are different
+        //    contLabel:  to null to indicate no longer in the protected code.
+        asm.add(bodyctx.contLabel);
+        bodyctx.contLabel = null;
+        bodyctx.breakLabel = endLabel;
+        if (finallyCanFinish) {
+            asm.add(where, opc_astore, num2);  // save the return address
+            finalbody.code(env, bodyctx, asm); // execute the cleanup code
+            asm.add(where, opc_ret, num2);     // return
+        } else {
+            finalbody.code(env, bodyctx, asm); // execute the cleanup code
+        }
+        asm.add(endLabel);                     // breaks come here
+    }
+
+    /**
+     * Print
+     */
+    public void print(PrintStream out, int indent) {
+        super.print(out, indent);
+        out.print("try ");
+        if (body != null) {
+            body.print(out, indent);
+        } else {
+            out.print("<empty>");
+        }
+        out.print(" finally ");
+        if (finalbody != null) {
+            finalbody.print(out, indent);
+        } else {
+            out.print("<empty>");
+        }
+    }
+}