Showing the changes to java/pkg/sun/tools/tree/FinallyStatement.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,360 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package sun.tools.tree; | ||
| 27 | |||
| 28 | import sun.tools.java.*; | ||
| 29 | import sun.tools.asm.Assembler; | ||
| 30 | import sun.tools.asm.Label; | ||
| 31 | import sun.tools.asm.TryData; | ||
| 32 | import sun.tools.asm.CatchData; | ||
| 33 | import java.io.PrintStream; | ||
| 34 | import java.util.Hashtable; | ||
| 35 | import java.util.Enumeration; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * WARNING: The contents of this source file are not part of any | ||
| 39 | * supported API. Code that depends on them does so at its own risk: | ||
| 40 | * they are subject to change or removal without notice. | ||
| 41 | */ | ||
| 42 | public | ||
| 43 | class FinallyStatement extends Statement { | ||
| 44 | Statement body; | ||
| 45 | Statement finalbody; | ||
| 46 | boolean finallyCanFinish; // does finalBody never return? | ||
| 47 | boolean needReturnSlot; // set by inner return statement | ||
| 48 | Statement init; // try object expression or declaration from parser | ||
| 49 | LocalMember tryTemp; // temp holding the try object, if any | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Constructor | ||
| 53 | */ | ||
| 54 | public FinallyStatement(long where, Statement body, Statement finalbody) { | ||
| 55 | super(FINALLY, where); | ||
| 56 | this.body = body; | ||
| 57 | this.finalbody = finalbody; | ||
| 58 | } | ||
| 59 | |||
| 60 | // /** | ||
| 61 | // * Constructor for try (init) {body} | ||
| 62 | // */ | ||
| 63 | // public FinallyStatement(long where, Statement init, Statement body, int junk) { | ||
| 64 | // this(where, body, null); | ||
| 65 | // this.init = init; | ||
| 66 | // } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Check statement | ||
| 70 | */ | ||
| 71 | Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) { | ||
| 72 | vset = reach(env, vset); | ||
| 73 | Hashtable newexp = new Hashtable(); | ||
| 74 | |||
| 75 | // Handle the proposed 'try (init) { stmts } finally { stmts }' syntax. | ||
| 76 | // This feature has not been adopted, and support is presently disabled. | ||
| 77 | /*-----------------------------------------------------------* | ||
| 78 | if (init != null) { | ||
| 79 | ClassDefinition sourceClass = ctx.field.getClassDefinition(); | ||
| 80 | Expression tryExpr = null; | ||
| 81 | DeclarationStatement tryDecl = null; | ||
| 82 | long where = init.getWhere(); | ||
| 83 | // find out whether init is a simple expression or a declaration | ||
| 84 | if (init.getOp() == EXPRESSION) { | ||
| 85 | tryExpr = ((ExpressionStatement)init).expr; | ||
| 86 | init = null; // restore it below | ||
| 87 | vset = tryExpr.checkValue(env, ctx, vset, exp); | ||
| 88 | } else if (init.getOp() == DECLARATION) { | ||
| 89 | tryDecl = (DeclarationStatement) init; | ||
| 90 | init = null; // restore it below | ||
| 91 | vset = tryDecl.checkBlockStatement(env, ctx, vset, exp); | ||
| 92 | if (tryDecl.args.length != 1) { | ||
| 93 | env.error(where, "invalid.decl"); | ||
| 94 | } else { | ||
| 95 | LocalMember field = | ||
| 96 | ((VarDeclarationStatement) tryDecl.args[0]).field; | ||
| 97 | tryExpr = new IdentifierExpression(where, field); | ||
| 98 | tryExpr.type = field.getType(); | ||
| 99 | } | ||
| 100 | } else { | ||
| 101 | env.error(where, "invalid.expr"); | ||
| 102 | vset = init.check(env, ctx, vset, exp); | ||
| 103 | } | ||
| 104 | Type type = (tryExpr == null) ? Type.tError : tryExpr.getType(); | ||
| 105 | |||
| 106 | MemberDefinition tryEnter = null; | ||
| 107 | MemberDefinition tryExit = null; | ||
| 108 | if (!type.isType(TC_CLASS)) { | ||
| 109 | if (!type.isType(TC_ERROR)) { | ||
| 110 | env.error(where, "invalid.method.invoke", type); | ||
| 111 | } | ||
| 112 | } else { | ||
| 113 | Identifier idTryEnter = Identifier.lookup("tryEnter"); | ||
| 114 | Identifier idTryExit = Identifier.lookup("tryExit"); | ||
| 115 | Type tTryMethod = Type.tMethod(Type.tVoid); | ||
| 116 | try { | ||
| 117 | ClassDefinition tryClass = env.getClassDefinition(type); | ||
| 118 | tryEnter = tryClass.matchMethod(env, sourceClass, idTryEnter); | ||
| 119 | tryExit = tryClass.matchMethod(env, sourceClass, idTryExit); | ||
| 120 | if (tryEnter != null && !tryEnter.getType().equals(tTryMethod)) { | ||
| 121 | tryEnter = null; | ||
| 122 | } | ||
| 123 | if (tryExit != null && !tryExit.getType().equals(tTryMethod)) { | ||
| 124 | tryExit = null; | ||
| 125 | } | ||
| 126 | } catch (ClassNotFound ee) { | ||
| 127 | env.error(where, "class.not.found", ee.name, ctx.field); | ||
| 128 | } catch (AmbiguousMember ee) { | ||
| 129 | Identifier id = ee.field1.getName(); | ||
| 130 | env.error(where, "ambig.field", id, ee.field1, ee.field2); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | if (tryEnter == null || tryExit == null) { | ||
| 134 | // Make a better (more didactic) error here! | ||
| 135 | env.error(where, "invalid.method.invoke", type); | ||
| 136 | } else { | ||
| 137 | tryTemp = new LocalMember(where, sourceClass, 0, | ||
| 138 | type, Identifier.lookup("<try_object>")); | ||
| 139 | ctx = new Context(ctx, this); | ||
| 140 | ctx.declare(env, tryTemp); | ||
| 141 | |||
| 142 | Expression e; | ||
| 143 | e = new IdentifierExpression(where, tryTemp); | ||
| 144 | e = new AssignExpression(where, e, tryExpr); | ||
| 145 | e = new MethodExpression(where, e, tryEnter, new Expression[0]); | ||
| 146 | e.type = Type.tVoid; | ||
| 147 | Statement enterCall = new ExpressionStatement(where, e); | ||
| 148 | // store it on the init, for code generation | ||
| 149 | if (tryDecl != null) { | ||
| 150 | Statement args2[] = { tryDecl.args[0], enterCall }; | ||
| 151 | tryDecl.args = args2; | ||
| 152 | init = tryDecl; | ||
| 153 | } else { | ||
| 154 | init = enterCall; | ||
| 155 | } | ||
| 156 | e = new IdentifierExpression(where, tryTemp); | ||
| 157 | e = new MethodExpression(where, e, tryExit, new Expression[0]); | ||
| 158 | e.type = Type.tVoid; | ||
| 159 | Statement exitCall = new ExpressionStatement(where, e); | ||
| 160 | finalbody = exitCall; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | *-----------------------------------------------------------*/ | ||
| 164 | |||
| 165 | // Check the try part. We reach the end of the try part either by | ||
| 166 | // finishing normally, or doing a break to the label of the try/finally. | ||
| 167 | // NOTE: I don't think newctx1.vsBreak is ever used -- see TryStatement. | ||
| 168 | CheckContext newctx1 = new CheckContext(ctx, this); | ||
| 169 | Vset vset1 = body.check(env, newctx1, vset.copy(), newexp) | ||
| 170 | .join(newctx1.vsBreak); | ||
| 171 | // Check the finally part. | ||
| 172 | CheckContext newctx2 = new CheckContext(ctx, this); | ||
| 173 | // Should never access this field. The null indicates the finally part. | ||
| 174 | newctx2.vsContinue = null; | ||
| 175 | Vset vset2 = finalbody.check(env, newctx2, vset, exp); | ||
| 176 | finallyCanFinish = !vset2.isDeadEnd(); | ||
| 177 | vset2 = vset2.join(newctx2.vsBreak); | ||
| 178 | // If !finallyCanFinish, then the only possible exceptions that can | ||
| 179 | // occur at this point are the ones preceding the try/finally, or | ||
| 180 | // the ones generated by the finally. Anything in the try is | ||
| 181 | // irrelevant. Otherwise, we have to merge in all the exceptions | ||
| 182 | // generated by the body into exp. | ||
| 183 | if (finallyCanFinish) { | ||
| 184 | // Add newexp's back into exp; cf. ThrowStatement.check(). | ||
| 185 | for (Enumeration e = newexp.keys() ; e.hasMoreElements() ; ) { | ||
| 186 | Object def = e.nextElement(); | ||
| 187 | exp.put(def, newexp.get(def)); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | return ctx.removeAdditionalVars(vset1.addDAandJoinDU(vset2)); | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Inline | ||
| 195 | */ | ||
| 196 | public Statement inline(Environment env, Context ctx) { | ||
| 197 | if (tryTemp != null) { | ||
| 198 | ctx = new Context(ctx, this); | ||
| 199 | ctx.declare(env, tryTemp); | ||
| 200 | } | ||
| 201 | if (init != null) { | ||
| 202 | init = init.inline(env, ctx); | ||
| 203 | } | ||
| 204 | if (body != null) { | ||
| 205 | body = body.inline(env, ctx); | ||
| 206 | } | ||
| 207 | if (finalbody != null) { | ||
| 208 | finalbody = finalbody.inline(env, ctx); | ||
| 209 | } | ||
| 210 | if (body == null) { | ||
| 211 | return eliminate(env, finalbody); | ||
| 212 | } | ||
| 213 | if (finalbody == null) { | ||
| 214 | return eliminate(env, body); | ||
| 215 | } | ||
| 216 | return this; | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * Create a copy of the statement for method inlining | ||
| 221 | */ | ||
| 222 | public Statement copyInline(Context ctx, boolean valNeeded) { | ||
| 223 | FinallyStatement s = (FinallyStatement)clone(); | ||
| 224 | if (tryTemp != null) { | ||
| 225 | s.tryTemp = tryTemp.copyInline(ctx); | ||
| 226 | } | ||
| 227 | if (init != null) { | ||
| 228 | s.init = init.copyInline(ctx, valNeeded); | ||
| 229 | } | ||
| 230 | if (body != null) { | ||
| 231 | s.body = body.copyInline(ctx, valNeeded); | ||
| 232 | } | ||
| 233 | if (finalbody != null) { | ||
| 234 | s.finalbody = finalbody.copyInline(ctx, valNeeded); | ||
| 235 | } | ||
| 236 | return s; | ||
| 237 | } | ||
| 238 | |||
| 239 | /** | ||
| 240 | * Compute cost of inlining this statement | ||
| 241 | */ | ||
| 242 | public int costInline(int thresh, Environment env, Context ctx){ | ||
| 243 | int cost = 4; | ||
| 244 | if (init != null) { | ||
| 245 | cost += init.costInline(thresh, env,ctx); | ||
| 246 | if (cost >= thresh) return cost; | ||
| 247 | } | ||
| 248 | if (body != null) { | ||
| 249 | cost += body.costInline(thresh, env,ctx); | ||
| 250 | if (cost >= thresh) return cost; | ||
| 251 | } | ||
| 252 | if (finalbody != null) { | ||
| 253 | cost += finalbody.costInline(thresh, env,ctx); | ||
| 254 | } | ||
| 255 | return cost; | ||
| 256 | } | ||
| 257 | |||
| 258 | /** | ||
| 259 | * Code | ||
| 260 | */ | ||
| 261 | public void code(Environment env, Context ctx, Assembler asm) { | ||
| 262 | ctx = new Context(ctx); | ||
| 263 | Integer num1 = null, num2 = null; | ||
| 264 | Label endLabel = new Label(); | ||
| 265 | |||
| 266 | if (tryTemp != null) { | ||
| 267 | ctx.declare(env, tryTemp); | ||
| 268 | } | ||
| 269 | if (init != null) { | ||
| 270 | CodeContext exprctx = new CodeContext(ctx, this); | ||
| 271 | init.code(env, exprctx, asm); | ||
| 272 | } | ||
| 273 | |||
| 274 | if (finallyCanFinish) { | ||
| 275 | LocalMember f1, f2; | ||
| 276 | ClassDefinition thisClass = ctx.field.getClassDefinition(); | ||
| 277 | |||
| 278 | if (needReturnSlot) { | ||
| 279 | Type returnType = ctx.field.getType().getReturnType(); | ||
| 280 | LocalMember localfield = new LocalMember(0, thisClass, 0, | ||
| 281 | returnType, | ||
| 282 | idFinallyReturnValue); | ||
| 283 | ctx.declare(env, localfield); | ||
| 284 | env.debugOutput("Assigning return slot to " + localfield.number); | ||
| 285 | } | ||
| 286 | |||
| 287 | // allocate space for the exception and return address | ||
| 288 | f1 = new LocalMember(where, thisClass, 0, Type.tObject, null); | ||
| 289 | f2 = new LocalMember(where, thisClass, 0, Type.tInt, null); | ||
| 290 | num1 = new Integer(ctx.declare(env, f1)); | ||
| 291 | num2 = new Integer(ctx.declare(env, f2)); | ||
| 292 | } | ||
| 293 | |||
| 294 | TryData td = new TryData(); | ||
| 295 | td.add(null); | ||
| 296 | |||
| 297 | // Main body | ||
| 298 | CodeContext bodyctx = new CodeContext(ctx, this); | ||
| 299 | asm.add(where, opc_try, td); // start of protected code | ||
| 300 | body.code(env, bodyctx, asm); | ||
| 301 | asm.add(bodyctx.breakLabel); | ||
| 302 | asm.add(td.getEndLabel()); // end of protected code | ||
| 303 | |||
| 304 | // Cleanup afer body | ||
| 305 | if (finallyCanFinish) { | ||
| 306 | asm.add(where, opc_jsr, bodyctx.contLabel); | ||
| 307 | asm.add(where, opc_goto, endLabel); | ||
| 308 | } else { | ||
| 309 | // just goto the cleanup code. It will never return. | ||
| 310 | asm.add(where, opc_goto, bodyctx.contLabel); | ||
| 311 | } | ||
| 312 | |||
| 313 | // Catch code | ||
| 314 | CatchData cd = td.getCatch(0); | ||
| 315 | asm.add(cd.getLabel()); | ||
| 316 | if (finallyCanFinish) { | ||
| 317 | asm.add(where, opc_astore, num1); // store exception | ||
| 318 | asm.add(where, opc_jsr, bodyctx.contLabel); | ||
| 319 | asm.add(where, opc_aload, num1); // rethrow exception | ||
| 320 | asm.add(where, opc_athrow); | ||
| 321 | } else { | ||
| 322 | // pop exception off stack. Fall through to finally code | ||
| 323 | asm.add(where, opc_pop); | ||
| 324 | } | ||
| 325 | |||
| 326 | // The finally part, which is marked by the contLabel. Update | ||
| 327 | // breakLabel: since break's in the finally are different | ||
| 328 | // contLabel: to null to indicate no longer in the protected code. | ||
| 329 | asm.add(bodyctx.contLabel); | ||
| 330 | bodyctx.contLabel = null; | ||
| 331 | bodyctx.breakLabel = endLabel; | ||
| 332 | if (finallyCanFinish) { | ||
| 333 | asm.add(where, opc_astore, num2); // save the return address | ||
| 334 | finalbody.code(env, bodyctx, asm); // execute the cleanup code | ||
| 335 | asm.add(where, opc_ret, num2); // return | ||
| 336 | } else { | ||
| 337 | finalbody.code(env, bodyctx, asm); // execute the cleanup code | ||
| 338 | } | ||
| 339 | asm.add(endLabel); // breaks come here | ||
| 340 | } | ||
| 341 | |||
| 342 | /** | ||
| 343 | |||
| 344 | */ | ||
| 345 | public void print(PrintStream out, int indent) { | ||
| 346 | super.print(out, indent); | ||
| 347 | out.print("try "); | ||
| 348 | if (body != null) { | ||
| 349 | body.print(out, indent); | ||
| 350 | } else { | ||
| 351 | out.print("<empty>"); | ||
| 352 | } | ||
| 353 | out.print(" finally "); | ||
| 354 | if (finalbody != null) { | ||
| 355 | finalbody.print(out, indent); | ||
| 356 | } else { | ||
| 357 | out.print("<empty>"); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | } | ||