d234618e8a7b8a4e8455186acbfc9ef91e2f2a37
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / ConditionalExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.impl.*;
15 import net.sourceforge.phpdt.internal.compiler.codegen.*;
16 import net.sourceforge.phpdt.internal.compiler.flow.*;
17 import net.sourceforge.phpdt.internal.compiler.lookup.*;
18
19 public class ConditionalExpression extends OperatorExpression {
20
21         public Expression condition, valueIfTrue, valueIfFalse;
22         private int returnTypeSlotSize = 1;
23
24         // for local variables table attributes
25         int thenInitStateIndex = -1;
26         int elseInitStateIndex = -1;
27         int mergedInitStateIndex = -1;
28         
29         public ConditionalExpression(
30                 Expression condition,
31                 Expression valueIfTrue,
32                 Expression valueIfFalse) {
33                 this.condition = condition;
34                 this.valueIfTrue = valueIfTrue;
35                 this.valueIfFalse = valueIfFalse;
36                 sourceStart = condition.sourceStart;
37                 sourceEnd = valueIfFalse.sourceEnd;
38         }
39
40         public FlowInfo analyseCode(
41                 BlockScope currentScope,
42                 FlowContext flowContext,
43                 FlowInfo flowInfo) {
44
45                 Constant conditionConstant = condition.conditionalConstant();
46
47                 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, conditionConstant == NotAConstant);
48
49                 if (conditionConstant != NotAConstant) {
50                         if (conditionConstant.booleanValue() == true) {
51                                 // TRUE ? left : right
52                                 FlowInfo resultInfo =
53                                         valueIfTrue.analyseCode(currentScope, flowContext, flowInfo.initsWhenTrue().unconditionalInits());
54                                 // analyse valueIfFalse, but do not take into account any of its infos
55                                 valueIfFalse.analyseCode(
56                                         currentScope,
57                                         flowContext,
58                                         flowInfo.initsWhenFalse().copy().unconditionalInits().markAsFakeReachable(true));
59                                 mergedInitStateIndex =
60                                         currentScope.methodScope().recordInitializationStates(resultInfo);
61                                 return resultInfo;
62                         } else {
63                                 // FALSE ? left : right
64                                 // analyse valueIfTrue, but do not take into account any of its infos                   
65                                 valueIfTrue.analyseCode(
66                                         currentScope,
67                                         flowContext,
68                                         flowInfo.initsWhenTrue().copy().unconditionalInits().markAsFakeReachable(true));
69                                 FlowInfo mergeInfo =
70                                         valueIfFalse.analyseCode(currentScope, flowContext, flowInfo.initsWhenFalse().unconditionalInits());
71                                 mergedInitStateIndex =
72                                         currentScope.methodScope().recordInitializationStates(mergeInfo);
73                                 return mergeInfo;
74                         }
75                 }
76
77                 // store a copy of the merged info, so as to compute the local variable attributes afterwards
78                 FlowInfo trueInfo = flowInfo.initsWhenTrue();
79                 thenInitStateIndex =
80                         currentScope.methodScope().recordInitializationStates(trueInfo);
81                 FlowInfo falseInfo = flowInfo.initsWhenFalse();
82                 elseInitStateIndex =
83                         currentScope.methodScope().recordInitializationStates(falseInfo);
84
85                 // propagate analysis
86                 trueInfo = valueIfTrue.analyseCode(currentScope, flowContext, trueInfo.copy());
87                 falseInfo =
88                         valueIfFalse.analyseCode(currentScope, flowContext, falseInfo.copy());
89
90                 // merge back using a conditional info -  1GK2BLM
91                 // if ((t && (v = t)) ? t : t && (v = f)) r = v;  -- ok
92                 FlowInfo mergedInfo =
93                         FlowInfo.conditional(
94                                 trueInfo.initsWhenTrue().copy().unconditionalInits().mergedWith( // must copy, since could be shared with trueInfo.initsWhenFalse()...
95                                         falseInfo.initsWhenTrue().copy().unconditionalInits()),
96                                 trueInfo.initsWhenFalse().unconditionalInits().mergedWith(
97                                         falseInfo.initsWhenFalse().unconditionalInits()));
98                 /*                      
99                         FlowInfo mergedInfo = valueIfTrue.analyseCode(
100                                 currentScope,
101                                 flowContext,
102                                 flowInfo.initsWhenTrue().copy()).
103                                         unconditionalInits().
104                                                 mergedWith(
105                                                         valueIfFalse.analyseCode(
106                                                                 currentScope,
107                                                                 flowContext,
108                                                                 flowInfo.initsWhenFalse().copy()).
109                                                                         unconditionalInits());
110                 */
111                 mergedInitStateIndex =
112                         currentScope.methodScope().recordInitializationStates(mergedInfo);
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.conditionalConstant();
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 (thenInitStateIndex != -1) {
156                         codeStream.removeNotDefinitelyAssignedVariables(
157                                 currentScope,
158                                 thenInitStateIndex);
159                         codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex);
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 (elseInitStateIndex != -1) {
178                                 codeStream.removeNotDefinitelyAssignedVariables(
179                                         currentScope,
180                                         elseInitStateIndex);
181                                 codeStream.addDefinitelyAssignedVariables(currentScope, elseInitStateIndex);
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                 int pc = codeStream.position;
215                 Constant cst = condition.constant;
216                 Constant condCst = condition.conditionalConstant();
217                 boolean needTruePart =
218                         !(((cst != NotAConstant) && (cst.booleanValue() == false))
219                                 || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
220                 boolean needFalsePart =
221                         !(((cst != NotAConstant) && (cst.booleanValue() == true))
222                                 || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
223
224                 Label internalFalseLabel, endifLabel = new Label(codeStream);
225
226                 // Generate code for the condition
227                 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
228                 condition.generateOptimizedBoolean(
229                                 currentScope,
230                                 codeStream,
231                                 null,
232                                 internalFalseLabel = new Label(codeStream),
233                                 needConditionValue);
234
235                 if (thenInitStateIndex != -1) {
236                         codeStream.removeNotDefinitelyAssignedVariables(
237                                 currentScope,
238                                 thenInitStateIndex);
239                         codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex);
240                 }
241                 // Then code generation
242                 if (needTruePart) {
243                         valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
244                         
245                         if (needFalsePart) {
246                                 // Jump over the else part
247                                 int position = codeStream.position;
248                                 codeStream.goto_(endifLabel);
249                                 codeStream.updateLastRecordedEndPC(position);
250                                 // Tune codestream stack size
251                                 //if (valueRequired) {
252                                 //      codeStream.decrStackSize(returnTypeSlotSize);
253                                 //}
254                         }
255                 }
256                 if (needFalsePart) {
257                         internalFalseLabel.place();
258                         if (elseInitStateIndex != -1) {
259                                 codeStream.removeNotDefinitelyAssignedVariables(
260                                         currentScope,
261                                         elseInitStateIndex);
262                                 codeStream.addDefinitelyAssignedVariables(currentScope, elseInitStateIndex);
263                         }
264                         valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
265
266                         // End of if statement
267                         endifLabel.place();
268                 }
269                 // May loose some local variable initializations : affecting the local variable attributes
270                 if (mergedInitStateIndex != -1) {
271                         codeStream.removeNotDefinitelyAssignedVariables(
272                                 currentScope,
273                                 mergedInitStateIndex);
274                 }
275                 // no implicit conversion for boolean values
276                 codeStream.recordPositionsFrom(pc, this.sourceStart);
277         }
278
279         public TypeBinding resolveType(BlockScope scope) {
280                 // specs p.368
281                 constant = NotAConstant;
282                 TypeBinding conditionType = condition.resolveTypeExpecting(scope, BooleanBinding);
283                 TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope);
284                 TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope);
285                 if (conditionType == null || valueIfTrueType == null || valueIfFalseType == null)
286                         return null;
287
288                 // Propagate the constant value from the valueIfTrue and valueIFFalse expression if it is possible
289                 if (condition.constant != NotAConstant
290                         && valueIfTrue.constant != NotAConstant
291                         && 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 =
295                                 (condition.constant.booleanValue())
296                                         ? valueIfTrue.constant
297                                         : valueIfFalse.constant;
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                         this.typeBinding = valueIfTrueType;
306                         return valueIfTrueType;
307                 }
308                 // Determine the return type depending on argument types
309                 // Numeric types
310                 if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) {
311                         // (Short x Byte) or (Byte x Short)"
312                         if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding)
313                                 || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding)) {
314                                 valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType);
315                                 valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType);
316                                 this.typeBinding = ShortBinding;
317                                 return ShortBinding;
318                         }
319                         // <Byte|Short|Char> x constant(Int)  ---> <Byte|Short|Char>   and reciprocally
320                         if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding || valueIfTrueType == CharBinding)
321                                 && (valueIfFalseType == IntBinding
322                                         && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, valueIfTrueType))) {
323                                 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
324                                 valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
325                                 this.typeBinding = valueIfTrueType;
326                                 return valueIfTrueType;
327                         }
328                         if ((valueIfFalseType == ByteBinding
329                                 || valueIfFalseType == ShortBinding
330                                 || valueIfFalseType == CharBinding)
331                                 && (valueIfTrueType == IntBinding
332                                         && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, valueIfFalseType))) {
333                                 valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
334                                 valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
335                                 this.typeBinding = valueIfFalseType;
336                                 return valueIfFalseType;
337                         }
338                         // Manual binary numeric promotion
339                         // int
340                         if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int)
341                                 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) {
342                                 valueIfTrue.implicitWidening(IntBinding, valueIfTrueType);
343                                 valueIfFalse.implicitWidening(IntBinding, valueIfFalseType);
344                                 this.typeBinding = IntBinding;
345                                 return IntBinding;
346                         }
347                         // long
348                         if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long)
349                                 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) {
350                                 valueIfTrue.implicitWidening(LongBinding, valueIfTrueType);
351                                 valueIfFalse.implicitWidening(LongBinding, valueIfFalseType);
352                                 returnTypeSlotSize = 2;
353                                 this.typeBinding = LongBinding;
354                                 return LongBinding;
355                         }
356                         // float
357                         if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float)
358                                 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) {
359                                 valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType);
360                                 valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType);
361                                 this.typeBinding = FloatBinding;
362                                 return FloatBinding;
363                         }
364                         // double
365                         valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType);
366                         valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType);
367                         returnTypeSlotSize = 2;
368                         this.typeBinding = DoubleBinding;
369                         return DoubleBinding;
370                 }
371                 // Type references (null null is already tested)
372                 if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
373                         || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
374                         scope.problemReporter().conditionalArgumentsIncompatibleTypes(
375                                 this,
376                                 valueIfTrueType,
377                                 valueIfFalseType);
378                         return null;
379                 }
380                 if (scope.areTypesCompatible(valueIfFalseType, valueIfTrueType)) {
381                         valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
382                         valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
383                         this.typeBinding = valueIfTrueType;
384                         return valueIfTrueType;
385                 }
386                 if (scope.areTypesCompatible(valueIfTrueType, valueIfFalseType)) {
387                         valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
388                         valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
389                         this.typeBinding = valueIfFalseType;
390                         return valueIfFalseType;
391                 }
392                 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
393                         this,
394                         valueIfTrueType,
395                         valueIfFalseType);
396                 return null;
397         }
398         
399         public String toStringExpressionNoParenthesis() {
400                 return condition.toStringExpression() + " ? " + //$NON-NLS-1$
401                 valueIfTrue.toStringExpression() + " : " + //$NON-NLS-1$
402                 valueIfFalse.toStringExpression();
403         }
404
405         public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
406                 if (visitor.visit(this, scope)) {
407                         condition.traverse(visitor, scope);
408                         valueIfTrue.traverse(visitor, scope);
409                         valueIfFalse.traverse(visitor, scope);
410                 }
411                 visitor.endVisit(this, scope);
412         }
413 }