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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
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.*;
20 public class ConditionalExpression extends OperatorExpression {
22 public Expression condition, valueIfTrue, valueIfFalse;
23 public Constant optimizedBooleanConstant;
24 public Constant optimizedIfTrueConstant;
25 public Constant optimizedIfFalseConstant;
27 // for local variables table attributes
28 int trueInitStateIndex = -1;
29 int falseInitStateIndex = -1;
30 int mergedInitStateIndex = -1;
32 public ConditionalExpression(
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;
43 public FlowInfo analyseCode(
44 BlockScope currentScope,
45 FlowContext flowContext,
48 Constant cst = this.condition.optimizedBooleanConstant();
49 boolean isConditionOptimizedTrue = cst != NotAConstant && cst.booleanValue() == true;
50 boolean isConditionOptimizedFalse = cst != NotAConstant && cst.booleanValue() == false;
52 int mode = flowInfo.reachMode();
53 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, cst == NotAConstant);
55 // process the if-true part
56 FlowInfo trueFlowInfo = flowInfo.initsWhenTrue().copy();
57 if (isConditionOptimizedFalse) {
58 trueFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
60 trueInitStateIndex = currentScope.methodScope().recordInitializationStates(trueFlowInfo);
61 trueFlowInfo = valueIfTrue.analyseCode(currentScope, flowContext, trueFlowInfo);
63 // process the if-false part
64 FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy();
65 if (isConditionOptimizedTrue) {
66 falseFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
68 falseInitStateIndex = currentScope.methodScope().recordInitializationStates(falseFlowInfo);
69 falseFlowInfo = valueIfFalse.analyseCode(currentScope, flowContext, falseFlowInfo);
71 // merge if-true & if-false initializations
73 if (isConditionOptimizedTrue){
74 mergedInfo = trueFlowInfo.addPotentialInitializationsFrom(falseFlowInfo);
75 } else if (isConditionOptimizedFalse) {
76 mergedInfo = falseFlowInfo.addPotentialInitializationsFrom(trueFlowInfo);
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;
83 cst = this.optimizedIfFalseConstant;
84 boolean isValueIfFalseOptimizedTrue = cst != null && cst != NotAConstant && cst.booleanValue() == true;
85 boolean isValueIfFalseOptimizedFalse = cst != null && cst != NotAConstant && cst.booleanValue() == false;
87 UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().copy().unconditionalInits();
88 if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
90 UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().copy().unconditionalInits();
91 if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
93 UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().copy().unconditionalInits();
94 if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
96 UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().copy().unconditionalInits();
97 if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
100 FlowInfo.conditional(
101 trueInfoWhenTrue.mergedWith(falseInfoWhenTrue),
102 trueInfoWhenFalse.mergedWith(falseInfoWhenFalse));
104 mergedInitStateIndex =
105 currentScope.methodScope().recordInitializationStates(mergedInfo);
106 mergedInfo.setReachMode(mode);
111 * Code generation for the conditional operator ?:
113 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
114 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
115 * @param valueRequired boolean
117 public void generateCode(
118 BlockScope currentScope,
119 CodeStream codeStream,
120 boolean valueRequired) {
122 int pc = codeStream.position;
123 Label endifLabel, falseLabel;
124 if (constant != NotAConstant) {
126 codeStream.generateConstant(constant, implicitConversion);
127 codeStream.recordPositionsFrom(pc, this.sourceStart);
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);
140 // Generate code for the condition
141 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
142 condition.generateOptimizedBoolean(
146 (falseLabel = new Label(codeStream)),
149 if (trueInitStateIndex != -1) {
150 codeStream.removeNotDefinitelyAssignedVariables(
153 codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
155 // Then code generation
157 valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
159 // Jump over the else part
160 int position = codeStream.position;
161 codeStream.goto_(endifLabel);
162 codeStream.updateLastRecordedEndPC(position);
163 // Tune codestream stack size
165 codeStream.decrStackSize(this.resolvedType == LongBinding || this.resolvedType == DoubleBinding ? 2 : 1);
171 if (falseInitStateIndex != -1) {
172 codeStream.removeNotDefinitelyAssignedVariables(
174 falseInitStateIndex);
175 codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
177 valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
178 // End of if statement
181 // May loose some local variable initializations : affecting the local variable attributes
182 if (mergedInitStateIndex != -1) {
183 codeStream.removeNotDefinitelyAssignedVariables(
185 mergedInitStateIndex);
187 // implicit conversion
189 codeStream.generateImplicitConversion(implicitConversion);
190 codeStream.recordPositionsFrom(pc, this.sourceStart);
194 * Optimized boolean code generation for the conditional operator ?:
196 public void generateOptimizedBoolean(
197 BlockScope currentScope,
198 CodeStream codeStream,
201 boolean valueRequired) {
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);
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)));
217 Label internalFalseLabel, endifLabel = new Label(codeStream);
219 // Generate code for the condition
220 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
221 condition.generateOptimizedBoolean(
225 internalFalseLabel = new Label(codeStream),
228 if (trueInitStateIndex != -1) {
229 codeStream.removeNotDefinitelyAssignedVariables(
232 codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
234 // Then code generation
236 valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
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
248 internalFalseLabel.place();
249 if (falseInitStateIndex != -1) {
250 codeStream.removeNotDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
251 codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
253 valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
255 // End of if statement
258 // May loose some local variable initializations : affecting the local variable attributes
259 if (mergedInitStateIndex != -1) {
260 codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
262 // no implicit conversion for boolean values
263 codeStream.updateLastRecordedEndPC(codeStream.position);
266 public Constant optimizedBooleanConstant() {
268 return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
271 public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
273 condition.printExpression(indent, output).append(" ? "); //$NON-NLS-1$
274 valueIfTrue.printExpression(0, output).append(" : "); //$NON-NLS-1$
275 return valueIfFalse.printExpression(0, output);
278 public TypeBinding resolveType(BlockScope scope) {
280 constant = NotAConstant;
281 LookupEnvironment env = scope.environment();
282 boolean use15specifics = env.options.sourceLevel >= ClassFileConstants.JDK1_5;
283 TypeBinding conditionType = condition.resolveTypeExpecting(scope, BooleanBinding);
285 if (valueIfTrue instanceof CastExpression) valueIfTrue.bits |= IgnoreNeedForCastCheckMASK; // will check later on
286 TypeBinding originalValueIfTrueType = valueIfTrue.resolveType(scope);
288 if (valueIfFalse instanceof CastExpression) valueIfFalse.bits |= IgnoreNeedForCastCheckMASK; // will check later on
289 TypeBinding originalValueIfFalseType = valueIfFalse.resolveType(scope);
291 if (conditionType == null || originalValueIfTrueType == null || originalValueIfFalseType == null)
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);
301 } else if (valueIfFalseType != NullBinding && valueIfFalseType.isBaseType()) {
302 valueIfTrueType = env.computeBoxingType(valueIfTrueType);
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;
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;
331 return this.resolvedType = valueIfTrueType;
333 // Determine the return type depending on argument 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;
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;
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;
360 // Manual binary numeric promotion
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;
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;
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;
383 valueIfTrue.computeConversion(scope, DoubleBinding, originalValueIfTrueType);
384 valueIfFalse.computeConversion(scope, DoubleBinding, originalValueIfFalseType);
385 return this.resolvedType = DoubleBinding;
387 // Type references (null null is already tested)
388 if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
389 || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
390 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
396 if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) {
397 valueIfTrue.computeConversion(scope, valueIfTrueType, originalValueIfTrueType);
398 valueIfFalse.computeConversion(scope, valueIfTrueType, originalValueIfFalseType);
399 return this.resolvedType = valueIfTrueType;
401 if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) {
402 valueIfTrue.computeConversion(scope, valueIfFalseType, originalValueIfTrueType);
403 valueIfFalse.computeConversion(scope, valueIfFalseType, originalValueIfFalseType);
404 return this.resolvedType = valueIfFalseType;
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;
415 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
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);
428 visitor.endVisit(this, scope);