2
|
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 |
}
|