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