fixed bug #1037094 (foreach)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / internal / compiler / ast / ConditionalExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpeclipse.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
16 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
17 import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
18 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
19 import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypeBinding;
20 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
21 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
22
23 public class ConditionalExpression extends OperatorExpression {
24
25         public Expression condition, valueIfTrue, valueIfFalse;
26         public Constant optimizedBooleanConstant;
27         public Constant optimizedIfTrueConstant;
28         public Constant optimizedIfFalseConstant;
29         
30         private int returnTypeSlotSize = 1;
31
32         // for local variables table attributes
33         int trueInitStateIndex = -1;
34         int falseInitStateIndex = -1;
35         int mergedInitStateIndex = -1;
36         
37         public ConditionalExpression(
38                 Expression condition,
39                 Expression valueIfTrue,
40                 Expression valueIfFalse) {
41                 this.condition = condition;
42                 this.valueIfTrue = valueIfTrue;
43                 this.valueIfFalse = valueIfFalse;
44                 sourceStart = condition.sourceStart;
45                 sourceEnd = valueIfFalse.sourceEnd;
46         }
47
48         public FlowInfo analyseCode(
49                 BlockScope currentScope,
50                 FlowContext flowContext,
51                 FlowInfo flowInfo) {
52
53                 Constant cst = this.condition.optimizedBooleanConstant();
54                 boolean isConditionOptimizedTrue = cst != NotAConstant && cst.booleanValue() == true;
55                 boolean isConditionOptimizedFalse = cst != NotAConstant && cst.booleanValue() == false;
56
57                 int mode = flowInfo.reachMode();
58                 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, cst == NotAConstant);
59                 
60                 // process the if-true part
61                 FlowInfo trueFlowInfo = flowInfo.initsWhenTrue().copy();
62                 if (isConditionOptimizedFalse) {
63                         trueFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
64                 }
65                 trueInitStateIndex = currentScope.methodScope().recordInitializationStates(trueFlowInfo);
66                 trueFlowInfo = valueIfTrue.analyseCode(currentScope, flowContext, trueFlowInfo);
67
68                 // process the if-false part
69                 FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy();
70                 if (isConditionOptimizedTrue) {
71                         falseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
72                 }
73                 falseInitStateIndex = currentScope.methodScope().recordInitializationStates(falseFlowInfo);
74                 falseFlowInfo = valueIfFalse.analyseCode(currentScope, flowContext, falseFlowInfo);
75
76                 // merge if-true & if-false initializations
77                 FlowInfo mergedInfo;
78                 if (isConditionOptimizedTrue){
79                         mergedInfo = trueFlowInfo.addPotentialInitializationsFrom(falseFlowInfo);
80                 } else if (isConditionOptimizedFalse) {
81                         mergedInfo = falseFlowInfo.addPotentialInitializationsFrom(trueFlowInfo);
82                 } else {
83                         // merge using a conditional info -  1GK2BLM
84                         // if ((t && (v = t)) ? t : t && (v = f)) r = v;  -- ok
85                         cst = this.optimizedIfTrueConstant;
86                         boolean isValueIfTrueOptimizedTrue = cst != null && cst != NotAConstant && cst.booleanValue() == true;
87                         boolean isValueIfTrueOptimizedFalse = cst != null && cst != NotAConstant && cst.booleanValue() == false;
88                         
89                         cst = this.optimizedIfFalseConstant;
90                         boolean isValueIfFalseOptimizedTrue = cst != null && cst != NotAConstant && cst.booleanValue() == true;
91                         boolean isValueIfFalseOptimizedFalse = cst != null && cst != NotAConstant && cst.booleanValue() == false;
92
93                         UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().copy().unconditionalInits();
94                         if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
95
96                         UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().copy().unconditionalInits();
97                         if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
98                         
99                         UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().copy().unconditionalInits();
100                         if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
101
102                         UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().copy().unconditionalInits();
103                         if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
104
105                         mergedInfo =
106                                 FlowInfo.conditional(
107                                         trueInfoWhenTrue.mergedWith(falseInfoWhenTrue),
108                                         trueInfoWhenFalse.mergedWith(falseInfoWhenFalse));
109                 }
110                 mergedInitStateIndex =
111                         currentScope.methodScope().recordInitializationStates(mergedInfo);
112                 mergedInfo.setReachMode(mode);
113                 return mergedInfo;
114         }
115
116         /**
117          * Code generation for the conditional operator ?:
118          *
119          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
120          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
121          * @param valueRequired boolean
122         */
123 //      public void generateCode(
124 //              BlockScope currentScope,
125 //              CodeStream codeStream,
126 //              boolean valueRequired) {
127 //
128 //              int pc = codeStream.position;
129 //              Label endifLabel, falseLabel;
130 //              if (constant != NotAConstant) {
131 //                      if (valueRequired)
132 //                              codeStream.generateConstant(constant, implicitConversion);
133 //                      codeStream.recordPositionsFrom(pc, this.sourceStart);
134 //                      return;
135 //              }
136 //              Constant cst = condition.constant;
137 //              Constant condCst = condition.optimizedBooleanConstant();
138 //              boolean needTruePart =
139 //                      !(((cst != NotAConstant) && (cst.booleanValue() == false))
140 //                              || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
141 //              boolean needFalsePart =
142 //                      !(((cst != NotAConstant) && (cst.booleanValue() == true))
143 //                              || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
144 //              endifLabel = new Label(codeStream);
145 //
146 //              // Generate code for the condition
147 //              boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
148 //              condition.generateOptimizedBoolean(
149 //                      currentScope,
150 //                      codeStream,
151 //                      null,
152 //                      (falseLabel = new Label(codeStream)),
153 //                      needConditionValue);
154 //
155 //              if (trueInitStateIndex != -1) {
156 //                      codeStream.removeNotDefinitelyAssignedVariables(
157 //                              currentScope,
158 //                              trueInitStateIndex);
159 //                      codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
160 //              }
161 //              // Then code generation
162 //              if (needTruePart) {
163 //                      valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
164 //                      if (needFalsePart) {
165 //                              // Jump over the else part
166 //                              int position = codeStream.position;
167 //                              codeStream.goto_(endifLabel);
168 //                              codeStream.updateLastRecordedEndPC(position);
169 //                              // Tune codestream stack size
170 //                              if (valueRequired) {
171 //                                      codeStream.decrStackSize(returnTypeSlotSize);
172 //                              }
173 //                      }
174 //              }
175 //              if (needFalsePart) {
176 //                      falseLabel.place();
177 //                      if (falseInitStateIndex != -1) {
178 //                              codeStream.removeNotDefinitelyAssignedVariables(
179 //                                      currentScope,
180 //                                      falseInitStateIndex);
181 //                              codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
182 //                      }
183 //                      valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
184 //                      // End of if statement
185 //                      endifLabel.place();
186 //              }
187 //              // May loose some local variable initializations : affecting the local variable attributes
188 //              if (mergedInitStateIndex != -1) {
189 //                      codeStream.removeNotDefinitelyAssignedVariables(
190 //                              currentScope,
191 //                              mergedInitStateIndex);
192 //              }
193 //              // implicit conversion
194 //              if (valueRequired)
195 //                      codeStream.generateImplicitConversion(implicitConversion);
196 //              codeStream.recordPositionsFrom(pc, this.sourceStart);
197 //      }
198 //
199 //      /**
200 //       * Optimized boolean code generation for the conditional operator ?:
201 //      */
202 //      public void generateOptimizedBoolean(
203 //              BlockScope currentScope,
204 //              CodeStream codeStream,
205 //              Label trueLabel,
206 //              Label falseLabel,
207 //              boolean valueRequired) {
208 //
209 //              if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean) // constant
210 //                      || (valueIfTrue.implicitConversion >> 4) != T_boolean) { // non boolean values
211 //                      super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
212 //                      return;
213 //              }
214 //              Constant cst = condition.constant;
215 //              Constant condCst = condition.optimizedBooleanConstant();
216 //              boolean needTruePart =
217 //                      !(((cst != NotAConstant) && (cst.booleanValue() == false))
218 //                              || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
219 //              boolean needFalsePart =
220 //                      !(((cst != NotAConstant) && (cst.booleanValue() == true))
221 //                              || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
222 //
223 //              Label internalFalseLabel, endifLabel = new Label(codeStream);
224 //
225 //              // Generate code for the condition
226 //              boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
227 //              condition.generateOptimizedBoolean(
228 //                              currentScope,
229 //                              codeStream,
230 //                              null,
231 //                              internalFalseLabel = new Label(codeStream),
232 //                              needConditionValue);
233 //
234 //              if (trueInitStateIndex != -1) {
235 //                      codeStream.removeNotDefinitelyAssignedVariables(
236 //                              currentScope,
237 //                              trueInitStateIndex);
238 //                      codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
239 //              }
240 //              // Then code generation
241 //              if (needTruePart) {
242 //                      valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
243 //                      
244 //                      if (needFalsePart) {
245 //                              // Jump over the else part
246 //                              int position = codeStream.position;
247 //                              codeStream.goto_(endifLabel);
248 //                              codeStream.updateLastRecordedEndPC(position);
249 //                              // No need to decrement codestream stack size
250 //                              // since valueIfTrue was already consumed by branch bytecode
251 //                      }
252 //              }
253 //              if (needFalsePart) {
254 //                      internalFalseLabel.place();
255 //                      if (falseInitStateIndex != -1) {
256 //                              codeStream.removeNotDefinitelyAssignedVariables(
257 //                                      currentScope,
258 //                                      falseInitStateIndex);
259 //                              codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
260 //                      }
261 //                      valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
262 //
263 //                      // End of if statement
264 //                      endifLabel.place();
265 //              }
266 //              // May loose some local variable initializations : affecting the local variable attributes
267 //              if (mergedInitStateIndex != -1) {
268 //                      codeStream.removeNotDefinitelyAssignedVariables(
269 //                              currentScope,
270 //                              mergedInitStateIndex);
271 //              }
272 //              // no implicit conversion for boolean values
273 //              codeStream.updateLastRecordedEndPC(codeStream.position);
274 //      }
275 //
276 //      public Constant optimizedBooleanConstant() {
277 //
278 //              return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
279 //      }
280 //      
281 //      public TypeBinding resolveType(BlockScope scope) {
282 //              // specs p.368
283 //              constant = NotAConstant;
284 //              TypeBinding conditionType = condition.resolveTypeExpecting(scope, BooleanBinding);
285 //              TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope);
286 //              TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope);
287 //              if (conditionType == null || valueIfTrueType == null || valueIfFalseType == null)
288 //                      return null;
289 //
290 //              // Propagate the constant value from the valueIfTrue and valueIFFalse expression if it is possible
291 //              Constant condConstant, trueConstant, falseConstant;
292 //              if ((condConstant = condition.constant) != NotAConstant
293 //                      && (trueConstant = valueIfTrue.constant) != NotAConstant
294 //                      && (falseConstant = valueIfFalse.constant) != NotAConstant) {
295 //                      // all terms are constant expression so we can propagate the constant
296 //                      // from valueIFTrue or valueIfFalse to teh receiver constant
297 //                      constant = condConstant.booleanValue() ? trueConstant : falseConstant;
298 //              }
299 //              if (valueIfTrueType == valueIfFalseType) { // harmed the implicit conversion 
300 //                      valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
301 //                      valueIfFalse.implicitConversion = valueIfTrue.implicitConversion;
302 //                      if (valueIfTrueType == LongBinding || valueIfTrueType == DoubleBinding) {
303 //                              returnTypeSlotSize = 2;
304 //                      }
305 //
306 //                      if (valueIfTrueType == BooleanBinding) {
307 //                              this.optimizedIfTrueConstant = valueIfTrue.optimizedBooleanConstant();
308 //                              this.optimizedIfFalseConstant = valueIfFalse.optimizedBooleanConstant();
309 //                      
310 //                              // Propagate the optimized boolean constant if possible
311 //                              if ((condConstant = condition.optimizedBooleanConstant()) != NotAConstant) {
312 //                                      
313 //                                      this.optimizedBooleanConstant = condConstant.booleanValue()
314 //                                              ? optimizedIfTrueConstant
315 //                                              : optimizedIfFalseConstant;
316 //                              }
317 //                      }
318 //                      return this.resolvedType = valueIfTrueType;
319 //              }
320 //              // Determine the return type depending on argument types
321 //              // Numeric types
322 //              if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) {
323 //                      // (Short x Byte) or (Byte x Short)"
324 //                      if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding)
325 //                              || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding)) {
326 //                              valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType);
327 //                              valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType);
328 //                              this.resolvedType = ShortBinding;
329 //                              return ShortBinding;
330 //                      }
331 //                      // <Byte|Short|Char> x constant(Int)  ---> <Byte|Short|Char>   and reciprocally
332 //                      if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding || valueIfTrueType == CharBinding)
333 //                              && (valueIfFalseType == IntBinding
334 //                                      && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, valueIfTrueType))) {
335 //                              valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
336 //                              valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
337 //                              this.resolvedType = valueIfTrueType;
338 //                              return valueIfTrueType;
339 //                      }
340 //                      if ((valueIfFalseType == ByteBinding
341 //                              || valueIfFalseType == ShortBinding
342 //                              || valueIfFalseType == CharBinding)
343 //                              && (valueIfTrueType == IntBinding
344 //                                      && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, valueIfFalseType))) {
345 //                              valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
346 //                              valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
347 //                              this.resolvedType = valueIfFalseType;
348 //                              return valueIfFalseType;
349 //                      }
350 //                      // Manual binary numeric promotion
351 //                      // int
352 //                      if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int)
353 //                              && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) {
354 //                              valueIfTrue.implicitWidening(IntBinding, valueIfTrueType);
355 //                              valueIfFalse.implicitWidening(IntBinding, valueIfFalseType);
356 //                              this.resolvedType = IntBinding;
357 //                              return IntBinding;
358 //                      }
359 //                      // long
360 //                      if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long)
361 //                              && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) {
362 //                              valueIfTrue.implicitWidening(LongBinding, valueIfTrueType);
363 //                              valueIfFalse.implicitWidening(LongBinding, valueIfFalseType);
364 //                              returnTypeSlotSize = 2;
365 //                              this.resolvedType = LongBinding;
366 //                              return LongBinding;
367 //                      }
368 //                      // float
369 //                      if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float)
370 //                              && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) {
371 //                              valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType);
372 //                              valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType);
373 //                              this.resolvedType = FloatBinding;
374 //                              return FloatBinding;
375 //                      }
376 //                      // double
377 //                      valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType);
378 //                      valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType);
379 //                      returnTypeSlotSize = 2;
380 //                      this.resolvedType = DoubleBinding;
381 //                      return DoubleBinding;
382 //              }
383 //              // Type references (null null is already tested)
384 //              if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
385 //                      || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
386 //                      scope.problemReporter().conditionalArgumentsIncompatibleTypes(
387 //                              this,
388 //                              valueIfTrueType,
389 //                              valueIfFalseType);
390 //                      return null;
391 //              }
392 //              if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) {
393 //                      valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
394 //                      valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
395 //                      this.resolvedType = valueIfTrueType;
396 //                      return valueIfTrueType;
397 //              }
398 //              if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) {
399 //                      valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
400 //                      valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
401 //                      this.resolvedType = valueIfFalseType;
402 //                      return valueIfFalseType;
403 //              }
404 //              scope.problemReporter().conditionalArgumentsIncompatibleTypes(
405 //                      this,
406 //                      valueIfTrueType,
407 //                      valueIfFalseType);
408 //              return null;
409 //      }
410         
411         public String toStringExpressionNoParenthesis() {
412                 return condition.toStringExpression() + " ? " + //$NON-NLS-1$
413                 valueIfTrue.toStringExpression() + " : " + //$NON-NLS-1$
414                 valueIfFalse.toStringExpression();
415         }
416
417         public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
418                 if (visitor.visit(this, scope)) {
419                         condition.traverse(visitor, scope);
420                         valueIfTrue.traverse(visitor, scope);
421                         valueIfFalse.traverse(visitor, scope);
422                 }
423                 visitor.endVisit(this, scope);
424         }
425 }