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