diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/sun/tools/tree/FinallyStatement.java --- /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("")); + 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(""); + } + out.print(" finally "); + if (finalbody != null) { + finalbody.print(out, indent); + } else { + out.print(""); + } + } +}