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