001: /*
002: * Copyright 1994-2003 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 java.io.PrintStream;
032: import java.util.Hashtable;
033:
034: /**
035: * WARNING: The contents of this source file are not part of any
036: * supported API. Code that depends on them does so at its own risk:
037: * they are subject to change or removal without notice.
038: */
039: public class IfStatement extends Statement {
040: Expression cond;
041: Statement ifTrue;
042: Statement ifFalse;
043:
044: /**
045: * Constructor
046: */
047: public IfStatement(long where, Expression cond, Statement ifTrue,
048: Statement ifFalse) {
049: super (IF, where);
050: this .cond = cond;
051: this .ifTrue = ifTrue;
052: this .ifFalse = ifFalse;
053: }
054:
055: /**
056: * Check statement
057: */
058: Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) {
059: checkLabel(env, ctx);
060: CheckContext newctx = new CheckContext(ctx, this );
061: // Vset vsExtra = vset.copy(); // See comment below.
062: ConditionVars cvars = cond.checkCondition(env, newctx, reach(
063: env, vset), exp);
064: cond = convert(env, newctx, Type.tBoolean, cond);
065: // The following code, now deleted, was apparently an erroneous attempt
066: // at providing better error diagnostics. The comment read: 'If either
067: // the true clause or the false clause is unreachable, do a reasonable
068: // check on the child anyway.'
069: // Vset vsTrue = cvars.vsTrue.isDeadEnd() ? vsExtra : cvars.vsTrue;
070: // Vset vsFalse = cvars.vsFalse.isDeadEnd() ? vsExtra : cvars.vsFalse;
071: // Unfortunately, this violates the rules laid out in the JLS, and leads to
072: // blatantly incorrect results. For example, 'i' will not be recognized
073: // as definitely assigned following the statement 'if (true) i = 1;'.
074: // It is best to slavishly follow the JLS here. A cleverer approach could
075: // only correctly issue warnings, as JLS 16.2.6 is quite explicit, and it
076: // is OK for a dead branch of an if-statement to omit an assignment that
077: // would be required in the other branch. A complication: This code also
078: // had the effect of implementing the special-case rules for 'if-then' and
079: // 'if-then-else' in JLS 14.19, "Unreachable Statements". We now use
080: // 'Vset.clearDeadEnd' to remove the dead-end status of unreachable branches
081: // without affecting the definite-assignment status of the variables, thus
082: // maintaining a correct implementation of JLS 16.2.6. Fixes 4094353.
083: // Note that the code below will not consider the branches unreachable if
084: // the entire statement is unreachable. This is consistent with the error
085: // recovery policy that reports the only the first unreachable statement
086: // along an acyclic execution path.
087: Vset vsTrue = cvars.vsTrue.clearDeadEnd();
088: Vset vsFalse = cvars.vsFalse.clearDeadEnd();
089: vsTrue = ifTrue.check(env, newctx, vsTrue, exp);
090: if (ifFalse != null)
091: vsFalse = ifFalse.check(env, newctx, vsFalse, exp);
092: vset = vsTrue.join(vsFalse.join(newctx.vsBreak));
093: return ctx.removeAdditionalVars(vset);
094: }
095:
096: /**
097: * Inline
098: */
099: public Statement inline(Environment env, Context ctx) {
100: ctx = new Context(ctx, this );
101: cond = cond.inlineValue(env, ctx);
102:
103: // The compiler currently needs to perform inlining on both
104: // branches of the if statement -- even if `cond' is a constant
105: // true or false. Why? The compiler will later try to compile
106: // all classes that it has seen; this includes classes that
107: // appear in dead code. If we don't inline the dead branch here
108: // then the compiler will never perform inlining on any local
109: // classes appearing on the dead code. When the compiler tries
110: // to compile an un-inlined local class with uplevel references,
111: // it dies. (bug 4059492)
112: //
113: // A better solution to this would be to walk the dead branch and
114: // mark any local classes appearing therein as unneeded. Then the
115: // compilation phase could skip these classes.
116: if (ifTrue != null) {
117: ifTrue = ifTrue.inline(env, ctx);
118: }
119: if (ifFalse != null) {
120: ifFalse = ifFalse.inline(env, ctx);
121: }
122: if (cond.equals(true)) {
123: return eliminate(env, ifTrue);
124: }
125: if (cond.equals(false)) {
126: return eliminate(env, ifFalse);
127: }
128: if ((ifTrue == null) && (ifFalse == null)) {
129: return eliminate(env, new ExpressionStatement(where, cond)
130: .inline(env, ctx));
131: }
132: if (ifTrue == null) {
133: cond = new NotExpression(cond.where, cond).inlineValue(env,
134: ctx);
135: return eliminate(env, new IfStatement(where, cond, ifFalse,
136: null));
137: }
138: return this ;
139: }
140:
141: /**
142: * Create a copy of the statement for method inlining
143: */
144: public Statement copyInline(Context ctx, boolean valNeeded) {
145: IfStatement s = (IfStatement) clone();
146: s.cond = cond.copyInline(ctx);
147: if (ifTrue != null) {
148: s.ifTrue = ifTrue.copyInline(ctx, valNeeded);
149: }
150: if (ifFalse != null) {
151: s.ifFalse = ifFalse.copyInline(ctx, valNeeded);
152: }
153: return s;
154: }
155:
156: /**
157: * The cost of inlining this statement
158: */
159: public int costInline(int thresh, Environment env, Context ctx) {
160: int cost = 1 + cond.costInline(thresh, env, ctx);
161: if (ifTrue != null) {
162: cost += ifTrue.costInline(thresh, env, ctx);
163: }
164: if (ifFalse != null) {
165: cost += ifFalse.costInline(thresh, env, ctx);
166: }
167: return cost;
168: }
169:
170: /**
171: * Code
172: */
173: public void code(Environment env, Context ctx, Assembler asm) {
174: CodeContext newctx = new CodeContext(ctx, this );
175:
176: Label l1 = new Label();
177: cond.codeBranch(env, newctx, asm, l1, false);
178: ifTrue.code(env, newctx, asm);
179: if (ifFalse != null) {
180: Label l2 = new Label();
181: asm.add(true, where, opc_goto, l2);
182: asm.add(l1);
183: ifFalse.code(env, newctx, asm);
184: asm.add(l2);
185: } else {
186: asm.add(l1);
187: }
188:
189: asm.add(newctx.breakLabel);
190: }
191:
192: /**
193: * Print
194: */
195: public void print(PrintStream out, int indent) {
196: super .print(out, indent);
197: out.print("if ");
198: cond.print(out);
199: out.print(" ");
200: ifTrue.print(out, indent);
201: if (ifFalse != null) {
202: out.print(" else ");
203: ifFalse.print(out, indent);
204: }
205: }
206: }
|