|
1 /* |
|
2 * Copyright 1994-2003 Sun Microsystems, Inc. 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. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any 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 java.io.PrintStream; |
|
32 import java.util.Hashtable; |
|
33 |
|
34 /** |
|
35 * WARNING: The contents of this source file are not part of any |
|
36 * supported API. Code that depends on them does so at its own risk: |
|
37 * they are subject to change or removal without notice. |
|
38 */ |
|
39 public |
|
40 class Statement extends Node { |
|
41 public static final Vset DEAD_END = Vset.DEAD_END; |
|
42 Identifier labels[] = null; |
|
43 |
|
44 /** |
|
45 * Constructor |
|
46 */ |
|
47 Statement(int op, long where) { |
|
48 super(op, where); |
|
49 } |
|
50 |
|
51 /** |
|
52 * An empty statement. Its costInline is infinite. |
|
53 */ |
|
54 public static final Statement empty = new Statement(STAT, 0); |
|
55 |
|
56 /** |
|
57 * The largest possible interesting inline cost value. |
|
58 */ |
|
59 public static final int MAXINLINECOST = |
|
60 Integer.getInteger("javac.maxinlinecost", |
|
61 30).intValue(); |
|
62 |
|
63 /** |
|
64 * Insert a bit of code at the front of a statement. |
|
65 * Side-effect s2, if it is a CompoundStatement. |
|
66 */ |
|
67 public static Statement insertStatement(Statement s1, Statement s2) { |
|
68 if (s2 == null) { |
|
69 s2 = s1; |
|
70 } else if (s2 instanceof CompoundStatement) { |
|
71 // Do not add another level of block nesting. |
|
72 ((CompoundStatement)s2).insertStatement(s1); |
|
73 } else { |
|
74 Statement body[] = { s1, s2 }; |
|
75 s2 = new CompoundStatement(s1.getWhere(), body); |
|
76 } |
|
77 return s2; |
|
78 } |
|
79 |
|
80 /** |
|
81 * Set the label of a statement |
|
82 */ |
|
83 public void setLabel(Environment env, Expression e) { |
|
84 if (e.op == IDENT) { |
|
85 if (labels == null) { |
|
86 labels = new Identifier[1]; |
|
87 } else { |
|
88 // this should almost never happen. Multiple labels on |
|
89 // the same statement. But handle it gracefully. |
|
90 Identifier newLabels[] = new Identifier[labels.length + 1]; |
|
91 System.arraycopy(labels, 0, newLabels, 1, labels.length); |
|
92 labels = newLabels; |
|
93 } |
|
94 labels[0] = ((IdentifierExpression)e).id; |
|
95 } else { |
|
96 env.error(e.where, "invalid.label"); |
|
97 } |
|
98 } |
|
99 |
|
100 /** |
|
101 * Check a statement |
|
102 */ |
|
103 public Vset checkMethod(Environment env, Context ctx, Vset vset, Hashtable exp) { |
|
104 // Set up ctx.getReturnContext() for the sake of ReturnStatement.check(). |
|
105 CheckContext mctx = new CheckContext(ctx, new Statement(METHOD, 0)); |
|
106 ctx = mctx; |
|
107 |
|
108 vset = check(env, ctx, vset, exp); |
|
109 |
|
110 // Check for return |
|
111 if (!ctx.field.getType().getReturnType().isType(TC_VOID)) { |
|
112 // In general, we suppress further error messages due to |
|
113 // unreachable statements after reporting the first error |
|
114 // along a flow path (using 'clearDeadEnd'). Here, we |
|
115 // report an error anyway, because the end of the method |
|
116 // should be unreachable despite the earlier error. The |
|
117 // difference in treatment is due to the fact that, in this |
|
118 // case, the error is reachability, not unreachability. |
|
119 // NOTE: In addition to this subtle difference in the quality |
|
120 // of the error diagnostics, this treatment is essential to |
|
121 // preserve the correctness of using 'clearDeadEnd' to implement |
|
122 // the special-case reachability rules for if-then and if-then-else. |
|
123 if (!vset.isDeadEnd()) { |
|
124 env.error(ctx.field.getWhere(), "return.required.at.end", ctx.field); |
|
125 } |
|
126 } |
|
127 |
|
128 // Simulate a return at the end. |
|
129 vset = vset.join(mctx.vsBreak); |
|
130 |
|
131 return vset; |
|
132 } |
|
133 Vset checkDeclaration(Environment env, Context ctx, Vset vset, int mod, Type t, Hashtable exp) { |
|
134 throw new CompilerError("checkDeclaration"); |
|
135 } |
|
136 |
|
137 /** |
|
138 * Make sure the labels on this statement do not duplicate the |
|
139 * labels on any enclosing statement. Provided as a convenience |
|
140 * for subclasses. |
|
141 */ |
|
142 protected void checkLabel(Environment env, Context ctx) { |
|
143 if (labels != null) { |
|
144 loop: for (int i = 0; i < labels.length; i++) { |
|
145 // Make sure there is not a double label on this statement. |
|
146 for (int j = i+1; j < labels.length; j++) { |
|
147 if (labels[i] == labels[j]) { |
|
148 env.error(where, "nested.duplicate.label", labels[i]); |
|
149 continue loop; |
|
150 } |
|
151 } |
|
152 |
|
153 // Make sure no enclosing statement has the same label. |
|
154 CheckContext destCtx = |
|
155 (CheckContext) ctx.getLabelContext(labels[i]); |
|
156 |
|
157 if (destCtx != null) { |
|
158 // Check to make sure the label is in not uplevel. |
|
159 if (destCtx.frameNumber == ctx.frameNumber) { |
|
160 env.error(where, "nested.duplicate.label", labels[i]); |
|
161 } |
|
162 } |
|
163 } // end loop |
|
164 } |
|
165 } |
|
166 |
|
167 Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) { |
|
168 throw new CompilerError("check"); |
|
169 } |
|
170 |
|
171 /** This is called in contexts where declarations are valid. */ |
|
172 Vset checkBlockStatement(Environment env, Context ctx, Vset vset, Hashtable exp) { |
|
173 return check(env, ctx, vset, exp); |
|
174 } |
|
175 |
|
176 Vset reach(Environment env, Vset vset) { |
|
177 if (vset.isDeadEnd()) { |
|
178 env.error(where, "stat.not.reached"); |
|
179 vset = vset.clearDeadEnd(); |
|
180 } |
|
181 return vset; |
|
182 } |
|
183 |
|
184 /** |
|
185 * Inline |
|
186 */ |
|
187 public Statement inline(Environment env, Context ctx) { |
|
188 return this; |
|
189 } |
|
190 |
|
191 /** |
|
192 * Eliminate this statement, which is only possible if it has no label. |
|
193 */ |
|
194 public Statement eliminate(Environment env, Statement s) { |
|
195 if ((s != null) && (labels != null)) { |
|
196 Statement args[] = {s}; |
|
197 s = new CompoundStatement(where, args); |
|
198 s.labels = labels; |
|
199 } |
|
200 return s; |
|
201 } |
|
202 |
|
203 |
|
204 /** |
|
205 * Code |
|
206 */ |
|
207 public void code(Environment env, Context ctx, Assembler asm) { |
|
208 throw new CompilerError("code"); |
|
209 } |
|
210 |
|
211 /** |
|
212 * Generate the code to call all finally's for a break, continue, or |
|
213 * return statement. We must call "jsr" on all the cleanup code between |
|
214 * the current context "ctx", and the destination context "stopctx". |
|
215 * If 'save' isn't null, there is also a value on the top of the stack |
|
216 */ |
|
217 void codeFinally(Environment env, Context ctx, Assembler asm, |
|
218 Context stopctx, Type save) { |
|
219 Integer num = null; |
|
220 boolean haveCleanup = false; // there is a finally or synchronize; |
|
221 boolean haveNonLocalFinally = false; // some finally doesn't return; |
|
222 |
|
223 for (Context c = ctx; (c != null) && (c != stopctx); c = c.prev) { |
|
224 if (c.node == null) |
|
225 continue; |
|
226 if (c.node.op == SYNCHRONIZED) { |
|
227 haveCleanup = true; |
|
228 } else if (c.node.op == FINALLY |
|
229 && ((CodeContext)c).contLabel != null) { |
|
230 // c.contLabel == null indicates we're in the "finally" part |
|
231 haveCleanup = true; |
|
232 FinallyStatement st = ((FinallyStatement)(c.node)); |
|
233 if (!st.finallyCanFinish) { |
|
234 haveNonLocalFinally = true; |
|
235 // after hitting a non-local finally, no need generating |
|
236 // further code, because it won't get executed. |
|
237 break; |
|
238 } |
|
239 } |
|
240 } |
|
241 if (!haveCleanup) { |
|
242 // there is no cleanup that needs to be done. Just quit. |
|
243 return; |
|
244 } |
|
245 if (save != null) { |
|
246 // This statement has a return value on the stack. |
|
247 ClassDefinition def = ctx.field.getClassDefinition(); |
|
248 if (!haveNonLocalFinally) { |
|
249 // Save the return value in the register which should have |
|
250 // been reserved. |
|
251 LocalMember lf = ctx.getLocalField(idFinallyReturnValue); |
|
252 num = new Integer(lf.number); |
|
253 asm.add(where, opc_istore + save.getTypeCodeOffset(), num); |
|
254 } else { |
|
255 // Pop the return value. |
|
256 switch(ctx.field.getType().getReturnType().getTypeCode()) { |
|
257 case TC_VOID: |
|
258 break; |
|
259 case TC_DOUBLE: case TC_LONG: |
|
260 asm.add(where, opc_pop2); break; |
|
261 default: |
|
262 asm.add(where, opc_pop); break; |
|
263 } |
|
264 } |
|
265 } |
|
266 // Call each of the cleanup functions, as necessary. |
|
267 for (Context c = ctx ; (c != null) && (c != stopctx) ; c = c.prev) { |
|
268 if (c.node == null) |
|
269 continue; |
|
270 if (c.node.op == SYNCHRONIZED) { |
|
271 asm.add(where, opc_jsr, ((CodeContext)c).contLabel); |
|
272 } else if (c.node.op == FINALLY |
|
273 && ((CodeContext)c).contLabel != null) { |
|
274 FinallyStatement st = ((FinallyStatement)(c.node)); |
|
275 Label label = ((CodeContext)c).contLabel; |
|
276 if (st.finallyCanFinish) { |
|
277 asm.add(where, opc_jsr, label); |
|
278 } else { |
|
279 // the code never returns, so we're done. |
|
280 asm.add(where, opc_goto, label); |
|
281 break; |
|
282 } |
|
283 } |
|
284 } |
|
285 // Move the return value from the register back to the stack. |
|
286 if (num != null) { |
|
287 asm.add(where, opc_iload + save.getTypeCodeOffset(), num); |
|
288 } |
|
289 } |
|
290 |
|
291 /* |
|
292 * Return true if the statement has the given label |
|
293 */ |
|
294 public boolean hasLabel (Identifier lbl) { |
|
295 Identifier labels[] = this.labels; |
|
296 if (labels != null) { |
|
297 for (int i = labels.length; --i >= 0; ) { |
|
298 if (labels[i].equals(lbl)) { |
|
299 return true; |
|
300 } |
|
301 } |
|
302 } |
|
303 return false; |
|
304 } |
|
305 |
|
306 /** |
|
307 * Check if the first thing is a constructor invocation |
|
308 */ |
|
309 public Expression firstConstructor() { |
|
310 return null; |
|
311 } |
|
312 |
|
313 /** |
|
314 * Create a copy of the statement for method inlining |
|
315 */ |
|
316 public Statement copyInline(Context ctx, boolean valNeeded) { |
|
317 return (Statement)clone(); |
|
318 } |
|
319 |
|
320 public int costInline(int thresh, Environment env, Context ctx) { |
|
321 return thresh; |
|
322 } |
|
323 |
|
324 |
|
325 /** |
|
326 * Print |
|
327 */ |
|
328 void printIndent(PrintStream out, int indent) { |
|
329 for (int i = 0 ; i < indent ; i++) { |
|
330 out.print(" "); |
|
331 } |
|
332 } |
|
333 public void print(PrintStream out, int indent) { |
|
334 if (labels != null) { |
|
335 for (int i = labels.length; --i >= 0; ) |
|
336 out.print(labels[i] + ": "); |
|
337 } |
|
338 } |
|
339 public void print(PrintStream out) { |
|
340 print(out, 0); |
|
341 } |
|
342 } |