001: /*
002: * Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.tree;
027:
028: import sun.tools.java.*;
029: import sun.tools.asm.Assembler;
030: import sun.tools.asm.Label;
031: import sun.tools.asm.TryData;
032: import sun.tools.asm.CatchData;
033: import java.io.PrintStream;
034: import java.util.Hashtable;
035: import java.util.Enumeration;
036:
037: /**
038: * WARNING: The contents of this source file are not part of any
039: * supported API. Code that depends on them does so at its own risk:
040: * they are subject to change or removal without notice.
041: */
042: public class FinallyStatement extends Statement {
043: Statement body;
044: Statement finalbody;
045: boolean finallyCanFinish; // does finalBody never return?
046: boolean needReturnSlot; // set by inner return statement
047: Statement init; // try object expression or declaration from parser
048: LocalMember tryTemp; // temp holding the try object, if any
049:
050: /**
051: * Constructor
052: */
053: public FinallyStatement(long where, Statement body,
054: Statement finalbody) {
055: super (FINALLY, where);
056: this .body = body;
057: this .finalbody = finalbody;
058: }
059:
060: // /**
061: // * Constructor for try (init) {body}
062: // */
063: // public FinallyStatement(long where, Statement init, Statement body, int junk) {
064: // this(where, body, null);
065: // this.init = init;
066: // }
067:
068: /**
069: * Check statement
070: */
071: Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) {
072: vset = reach(env, vset);
073: Hashtable newexp = new Hashtable();
074:
075: // Handle the proposed 'try (init) { stmts } finally { stmts }' syntax.
076: // This feature has not been adopted, and support is presently disabled.
077: /*-----------------------------------------------------------*
078: if (init != null) {
079: ClassDefinition sourceClass = ctx.field.getClassDefinition();
080: Expression tryExpr = null;
081: DeclarationStatement tryDecl = null;
082: long where = init.getWhere();
083: // find out whether init is a simple expression or a declaration
084: if (init.getOp() == EXPRESSION) {
085: tryExpr = ((ExpressionStatement)init).expr;
086: init = null; // restore it below
087: vset = tryExpr.checkValue(env, ctx, vset, exp);
088: } else if (init.getOp() == DECLARATION) {
089: tryDecl = (DeclarationStatement) init;
090: init = null; // restore it below
091: vset = tryDecl.checkBlockStatement(env, ctx, vset, exp);
092: if (tryDecl.args.length != 1) {
093: env.error(where, "invalid.decl");
094: } else {
095: LocalMember field =
096: ((VarDeclarationStatement) tryDecl.args[0]).field;
097: tryExpr = new IdentifierExpression(where, field);
098: tryExpr.type = field.getType();
099: }
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)
247: return cost;
248: }
249: if (body != null) {
250: cost += body.costInline(thresh, env, ctx);
251: if (cost >= thresh)
252: return cost;
253: }
254: if (finalbody != null) {
255: cost += finalbody.costInline(thresh, env, ctx);
256: }
257: return cost;
258: }
259:
260: /**
261: * Code
262: */
263: public void code(Environment env, Context ctx, Assembler asm) {
264: ctx = new Context(ctx);
265: Integer num1 = null, num2 = null;
266: Label endLabel = new Label();
267:
268: if (tryTemp != null) {
269: ctx.declare(env, tryTemp);
270: }
271: if (init != null) {
272: CodeContext exprctx = new CodeContext(ctx, this );
273: init.code(env, exprctx, asm);
274: }
275:
276: if (finallyCanFinish) {
277: LocalMember f1, f2;
278: ClassDefinition this Class = ctx.field.getClassDefinition();
279:
280: if (needReturnSlot) {
281: Type returnType = ctx.field.getType().getReturnType();
282: LocalMember localfield = new LocalMember(0, this Class,
283: 0, returnType, idFinallyReturnValue);
284: ctx.declare(env, localfield);
285: env.debugOutput("Assigning return slot to "
286: + localfield.number);
287: }
288:
289: // allocate space for the exception and return address
290: f1 = new LocalMember(where, this Class, 0, Type.tObject,
291: null);
292: f2 = new LocalMember(where, this Class, 0, Type.tInt, null);
293: num1 = new Integer(ctx.declare(env, f1));
294: num2 = new Integer(ctx.declare(env, f2));
295: }
296:
297: TryData td = new TryData();
298: td.add(null);
299:
300: // Main body
301: CodeContext bodyctx = new CodeContext(ctx, this );
302: asm.add(where, opc_try, td); // start of protected code
303: body.code(env, bodyctx, asm);
304: asm.add(bodyctx.breakLabel);
305: asm.add(td.getEndLabel()); // end of protected code
306:
307: // Cleanup afer body
308: if (finallyCanFinish) {
309: asm.add(where, opc_jsr, bodyctx.contLabel);
310: asm.add(where, opc_goto, endLabel);
311: } else {
312: // just goto the cleanup code. It will never return.
313: asm.add(where, opc_goto, bodyctx.contLabel);
314: }
315:
316: // Catch code
317: CatchData cd = td.getCatch(0);
318: asm.add(cd.getLabel());
319: if (finallyCanFinish) {
320: asm.add(where, opc_astore, num1); // store exception
321: asm.add(where, opc_jsr, bodyctx.contLabel);
322: asm.add(where, opc_aload, num1); // rethrow exception
323: asm.add(where, opc_athrow);
324: } else {
325: // pop exception off stack. Fall through to finally code
326: asm.add(where, opc_pop);
327: }
328:
329: // The finally part, which is marked by the contLabel. Update
330: // breakLabel: since break's in the finally are different
331: // contLabel: to null to indicate no longer in the protected code.
332: asm.add(bodyctx.contLabel);
333: bodyctx.contLabel = null;
334: bodyctx.breakLabel = endLabel;
335: if (finallyCanFinish) {
336: asm.add(where, opc_astore, num2); // save the return address
337: finalbody.code(env, bodyctx, asm); // execute the cleanup code
338: asm.add(where, opc_ret, num2); // return
339: } else {
340: finalbody.code(env, bodyctx, asm); // execute the cleanup code
341: }
342: asm.add(endLabel); // breaks come here
343: }
344:
345: /**
346: * Print
347: */
348: public void print(PrintStream out, int indent) {
349: super .print(out, indent);
350: out.print("try ");
351: if (body != null) {
352: body.print(out, indent);
353: } else {
354: out.print("<empty>");
355: }
356: out.print(" finally ");
357: if (finalbody != null) {
358: finalbody.print(out, indent);
359: } else {
360: out.print("<empty>");
361: }
362: }
363: }
|