1 package gnu.expr; 2 3 import gnu.bytecode.*; 4 import gnu.kawa.reflect.LazyType; 5 import gnu.mapping.Values; 6 7 /** This is the Target of a boolean expression, in a conditional context. 8 * If the expression evaluates to true, transfer to the ifTrue label; 9 * if false, transfer to the ifFalse label. */ 10 11 public class ConditionalTarget extends Target 12 { 13 public Label ifTrue, ifFalse; 14 Language language; 15 16 public static final Method isTrueMethod = 17 ClassType.make("gnu.expr.KawaConvert").getDeclaredMethod("isTrue", 1); 18 19 /** 20 * @param ifTrue label to jump to if this evaluates to true 21 * @param ifFalse label to jump to if true 22 * @param language specifies what values are true 23 */ 24 ConditionalTarget(Label ifTrue, Label ifFalse, Language language)25 public ConditionalTarget (Label ifTrue, Label ifFalse, 26 Language language) 27 { 28 this.ifTrue = ifTrue; 29 this.ifFalse = ifFalse; 30 this.language = language; 31 } 32 33 /** True if the ifTrue label comes before the ifFalse label. 34 * This is used in the hope we can optimize away a branch followed by 35 * its target. */ 36 public boolean trueBranchComesFirst = true; 37 getType()38 public Type getType() { return Type.booleanType; } 39 compileFromStack(Compilation comp, Type stackType)40 public void compileFromStack(Compilation comp, Type stackType) { 41 CodeAttr code = comp.getCode(); 42 43 stackType = comp.asBooleanValue(this, stackType); 44 if (stackType == null) 45 return; 46 char sig = stackType.getSignature().charAt(0); 47 if (language != null) { 48 // For primitive types, check if zero is considered true. 49 // If so any value of the type can be considered true. 50 // FIXME This should probably be done at validate-apply time. 51 Object zero; 52 switch (sig) { 53 case 'I': case 'J': case 'B': case 'S': case 'F': case 'D': 54 zero = Integer.valueOf(0); 55 break; 56 case 'C': 57 zero = Character.valueOf('\0'); 58 break; 59 default: 60 zero = null; 61 } 62 if (zero != null && language.booleanValue(zero) > 0) { 63 code.emitPop(1); 64 code.emitGoto(ifTrue); 65 return; 66 } 67 } 68 69 switch (sig) 70 { 71 case 'V': 72 Label lab; 73 int voidValue = language.booleanValue(Values.empty); 74 if (voidValue > 0) 75 lab = ifTrue; 76 else { 77 if (voidValue < 0) // hopefully caught earlier ... 78 comp.error('e', "invalid void value in condition"); 79 lab = ifFalse; 80 } 81 code.emitGoto(lab); 82 return; 83 case 'J': 84 code.emitPushLong(0); 85 break; 86 case 'D': 87 code.emitPushDouble(0.0); 88 break; 89 case 'F': 90 code.emitPushFloat(0.0f); 91 break; 92 default: 93 if (trueBranchComesFirst) 94 code.emitGotoIfIntEqZero(ifFalse); 95 else 96 code.emitGotoIfIntNeZero(ifTrue); 97 emitGotoFirstBranch(code); 98 return; 99 case 'L': case '[': 100 if (language.booleanValue(null) == 0) { 101 if (Type.javalangBooleanType.compare(stackType) == -3 102 && ! LazyType.maybeLazy(stackType)) 103 comp.compileConstant(null); 104 else { 105 code.emitInvokeStatic(isTrueMethod); 106 if (trueBranchComesFirst) 107 code.emitGotoIfIntEqZero(ifFalse); 108 else 109 code.emitGotoIfIntNeZero(ifTrue); 110 emitGotoFirstBranch(code); // Usually a no-op. 111 return; 112 } 113 } 114 else 115 comp.compileConstant(language.booleanObject(false)); 116 break; 117 } 118 if (trueBranchComesFirst) 119 code.emitGotoIfEq(ifFalse); 120 else 121 code.emitGotoIfNE(ifTrue); 122 emitGotoFirstBranch(code); // Usually a no-op. 123 } 124 125 /** Goto whichever of IfTrue or ifFalse is specified by trueBranchComesFirst. 126 * Normally, the goto should get optimized away as a no-op. */ emitGotoFirstBranch(CodeAttr code)127 public final void emitGotoFirstBranch(CodeAttr code) 128 { 129 code.emitGoto(trueBranchComesFirst ? ifTrue : ifFalse); 130 } 131 } 132