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