|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.tree; |
|
|
|
import sun.tools.java.*; |
|
import sun.tools.asm.Assembler; |
|
import sun.tools.asm.Label; |
|
import sun.tools.asm.SwitchData; |
|
import java.io.PrintStream; |
|
import java.util.Hashtable; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
class SwitchStatement extends Statement { |
|
Expression expr; |
|
Statement args[]; |
|
|
|
|
|
|
|
*/ |
|
public SwitchStatement(long where, Expression expr, Statement args[]) { |
|
super(SWITCH, where); |
|
this.expr = expr; |
|
this.args = args; |
|
} |
|
|
|
|
|
|
|
*/ |
|
Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) { |
|
checkLabel(env, ctx); |
|
CheckContext newctx = new CheckContext(ctx, this); |
|
vset = expr.checkValue(env, newctx, reach(env, vset), exp); |
|
Type switchType = expr.type; |
|
|
|
expr = convert(env, newctx, Type.tInt, expr); |
|
|
|
Hashtable tab = new Hashtable(); |
|
boolean hasDefault = false; |
|
// Note that vs is reset to vset.copy() on every case label. |
|
|
|
Vset vs = DEAD_END; |
|
|
|
for (int i = 0 ; i < args.length ; i++) { |
|
Statement s = args[i]; |
|
|
|
if (s.op == CASE) { |
|
|
|
vs = s.check(env, newctx, vs.join(vset.copy()), exp); |
|
|
|
Expression lbl = ((CaseStatement)s).expr; |
|
if (lbl != null) { |
|
if (lbl instanceof IntegerExpression) { |
|
Integer Ivalue = |
|
(Integer)(((IntegerExpression)lbl).getValue()); |
|
int ivalue = Ivalue.intValue(); |
|
if (tab.get(lbl) != null) { |
|
env.error(s.where, "duplicate.label", Ivalue); |
|
} else { |
|
tab.put(lbl, s); |
|
boolean overflow; |
|
switch (switchType.getTypeCode()) { |
|
case TC_BYTE: |
|
overflow = (ivalue != (byte)ivalue); break; |
|
case TC_SHORT: |
|
overflow = (ivalue != (short)ivalue); break; |
|
case TC_CHAR: |
|
overflow = (ivalue != (char)ivalue); break; |
|
default: |
|
overflow = false; |
|
} |
|
if (overflow) { |
|
env.error(s.where, "switch.overflow", |
|
Ivalue, switchType); |
|
} |
|
} |
|
} else { |
|
// Suppose a class got an error early on during |
|
// checking. It will set all of its members to |
|
// have the status "ERROR". Now suppose that a |
|
// case label refers to one of this class's |
|
// fields. When we check the case label, the |
|
// compiler will try to inline the FieldExpression. |
|
// Since the expression has ERROR status, it doesn't |
|
// inline. This means that instead of the case |
|
// label being an IntegerExpression, it will still |
|
// be a FieldExpression, and we will end up in this |
|
// else block. So, before we just assume that |
|
// the expression isn't constant, do a check to |
|
// see if it was constant but unable to inline. |
|
// This eliminates some spurious error messages. |
|
|
|
if (!lbl.isConstant() || |
|
lbl.getType() != Type.tInt) { |
|
env.error(s.where, "const.expr.required"); |
|
} |
|
} |
|
} else { |
|
if (hasDefault) { |
|
env.error(s.where, "duplicate.default"); |
|
} |
|
hasDefault = true; |
|
} |
|
} else { |
|
vs = s.checkBlockStatement(env, newctx, vs, exp); |
|
} |
|
} |
|
if (!vs.isDeadEnd()) { |
|
newctx.vsBreak = newctx.vsBreak.join(vs); |
|
} |
|
if (hasDefault) |
|
vset = newctx.vsBreak; |
|
return ctx.removeAdditionalVars(vset); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Statement inline(Environment env, Context ctx) { |
|
ctx = new Context(ctx, this); |
|
expr = expr.inlineValue(env, ctx); |
|
for (int i = 0 ; i < args.length ; i++) { |
|
if (args[i] != null) { |
|
args[i] = args[i].inline(env, ctx); |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Statement copyInline(Context ctx, boolean valNeeded) { |
|
SwitchStatement s = (SwitchStatement)clone(); |
|
s.expr = expr.copyInline(ctx); |
|
s.args = new Statement[args.length]; |
|
for (int i = 0 ; i < args.length ; i++) { |
|
if (args[i] != null) { |
|
s.args[i] = args[i].copyInline(ctx, valNeeded); |
|
} |
|
} |
|
return s; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int costInline(int thresh, Environment env, Context ctx) { |
|
int cost = expr.costInline(thresh, env, ctx); |
|
for (int i = 0 ; (i < args.length) && (cost < thresh) ; i++) { |
|
if (args[i] != null) { |
|
cost += args[i].costInline(thresh, env, ctx); |
|
} |
|
} |
|
return cost; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void code(Environment env, Context ctx, Assembler asm) { |
|
CodeContext newctx = new CodeContext(ctx, this); |
|
|
|
expr.codeValue(env, newctx, asm); |
|
|
|
SwitchData sw = new SwitchData(); |
|
boolean hasDefault = false; |
|
|
|
for (int i = 0 ; i < args.length ; i++) { |
|
Statement s = args[i]; |
|
if ((s != null) && (s.op == CASE)) { |
|
Expression e = ((CaseStatement)s).expr; |
|
if (e != null) { |
|
sw.add(((IntegerExpression)e).value, new Label()); |
|
} |
|
|
|
else { |
|
hasDefault = true; |
|
} |
|
// end JCOV |
|
} |
|
} |
|
|
|
|
|
if (env.coverage()) |
|
sw.initTableCase(); |
|
|
|
asm.add(where, opc_tableswitch, sw); |
|
|
|
for (int i = 0 ; i < args.length ; i++) { |
|
Statement s = args[i]; |
|
if (s != null) { |
|
if (s.op == CASE) { |
|
Expression e = ((CaseStatement)s).expr; |
|
if (e != null) { |
|
asm.add(sw.get(((IntegerExpression)e).value)); |
|
|
|
sw.addTableCase(((IntegerExpression)e).value, s.where); |
|
// end JCOV |
|
} else { |
|
asm.add(sw.getDefaultLabel()); |
|
|
|
sw.addTableDefault(s.where); |
|
// end JCOV |
|
/* JCOV hasDefault = true; end JCOV */ |
|
} |
|
} else { |
|
s.code(env, newctx, asm); |
|
} |
|
} |
|
} |
|
|
|
if (!hasDefault) { |
|
asm.add(sw.getDefaultLabel()); |
|
} |
|
asm.add(newctx.breakLabel); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void print(PrintStream out, int indent) { |
|
super.print(out, indent); |
|
out.print("switch ("); |
|
expr.print(out); |
|
out.print(") {\n"); |
|
for (int i = 0 ; i < args.length ; i++) { |
|
if (args[i] != null) { |
|
printIndent(out, indent + 1); |
|
args[i].print(out, indent + 1); |
|
out.print("\n"); |
|
} |
|
} |
|
printIndent(out, indent); |
|
out.print("}"); |
|
} |
|
} |