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.codegen.*;
16 import org.eclipse.jdt.internal.compiler.flow.*;
17 import org.eclipse.jdt.internal.compiler.lookup.*;
19 public class ConditionalExpression extends OperatorExpression {
21 public Expression condition, valueIfTrue, valueIfFalse;
22 public Constant optimizedBooleanConstant;
23 public Constant optimizedIfTrueConstant;
24 public Constant optimizedIfFalseConstant;
26 // for local variables table attributes
27 int trueInitStateIndex = -1;
28 int falseInitStateIndex = -1;
29 int mergedInitStateIndex = -1;
31 public ConditionalExpression(
33 Expression valueIfTrue,
34 Expression valueIfFalse) {
35 this.condition = condition;
36 this.valueIfTrue = valueIfTrue;
37 this.valueIfFalse = valueIfFalse;
38 sourceStart = condition.sourceStart;
39 sourceEnd = valueIfFalse.sourceEnd;
42 public FlowInfo analyseCode(
43 BlockScope currentScope,
44 FlowContext flowContext,
47 Constant cst = this.condition.optimizedBooleanConstant();
48 boolean isConditionOptimizedTrue = cst != NotAConstant && cst.booleanValue() == true;
49 boolean isConditionOptimizedFalse = cst != NotAConstant && cst.booleanValue() == false;
51 int mode = flowInfo.reachMode();
52 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, cst == NotAConstant);
54 // process the if-true part
55 FlowInfo trueFlowInfo = flowInfo.initsWhenTrue().copy();
56 if (isConditionOptimizedFalse) {
57 trueFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
59 trueInitStateIndex = currentScope.methodScope().recordInitializationStates(trueFlowInfo);
60 trueFlowInfo = valueIfTrue.analyseCode(currentScope, flowContext, trueFlowInfo);
62 // process the if-false part
63 FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy();
64 if (isConditionOptimizedTrue) {
65 falseFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
67 falseInitStateIndex = currentScope.methodScope().recordInitializationStates(falseFlowInfo);
68 falseFlowInfo = valueIfFalse.analyseCode(currentScope, flowContext, falseFlowInfo);
70 // merge if-true & if-false initializations
72 if (isConditionOptimizedTrue){
73 mergedInfo = trueFlowInfo.addPotentialInitializationsFrom(falseFlowInfo);
74 } else if (isConditionOptimizedFalse) {
75 mergedInfo = falseFlowInfo.addPotentialInitializationsFrom(trueFlowInfo);
77 // if ((t && (v = t)) ? t : t && (v = f)) r = v; -- ok
78 cst = this.optimizedIfTrueConstant;
79 boolean isValueIfTrueOptimizedTrue = cst != null && cst != NotAConstant && cst.booleanValue() == true;
80 boolean isValueIfTrueOptimizedFalse = cst != null && cst != NotAConstant && cst.booleanValue() == false;
82 cst = this.optimizedIfFalseConstant;
83 boolean isValueIfFalseOptimizedTrue = cst != null && cst != NotAConstant && cst.booleanValue() == true;
84 boolean isValueIfFalseOptimizedFalse = cst != null && cst != NotAConstant && cst.booleanValue() == false;
86 UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().copy().unconditionalInits();
87 if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
89 UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().copy().unconditionalInits();
90 if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
92 UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().copy().unconditionalInits();
93 if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
95 UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().copy().unconditionalInits();
96 if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
100 trueInfoWhenTrue.mergedWith(falseInfoWhenTrue),
101 trueInfoWhenFalse.mergedWith(falseInfoWhenFalse));
103 mergedInitStateIndex =
104 currentScope.methodScope().recordInitializationStates(mergedInfo);
105 mergedInfo.setReachMode(mode);
110 * Code generation for the conditional operator ?:
112 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
113 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
114 * @param valueRequired boolean
116 public void generateCode(
117 BlockScope currentScope,
118 CodeStream codeStream,
119 boolean valueRequired) {
121 int pc = codeStream.position;
122 Label endifLabel, falseLabel;
123 if (constant != NotAConstant) {
125 codeStream.generateConstant(constant, implicitConversion);
126 codeStream.recordPositionsFrom(pc, this.sourceStart);
129 Constant cst = condition.constant;
130 Constant condCst = condition.optimizedBooleanConstant();
131 boolean needTruePart =
132 !(((cst != NotAConstant) && (cst.booleanValue() == false))
133 || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
134 boolean needFalsePart =
135 !(((cst != NotAConstant) && (cst.booleanValue() == true))
136 || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
137 endifLabel = new Label(codeStream);
139 // Generate code for the condition
140 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
141 condition.generateOptimizedBoolean(
145 (falseLabel = new Label(codeStream)),
148 if (trueInitStateIndex != -1) {
149 codeStream.removeNotDefinitelyAssignedVariables(
152 codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
154 // Then code generation
156 valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
158 // Jump over the else part
159 int position = codeStream.position;
160 codeStream.goto_(endifLabel);
161 codeStream.updateLastRecordedEndPC(position);
162 // Tune codestream stack size
164 codeStream.decrStackSize(this.resolvedType == LongBinding || this.resolvedType == DoubleBinding ? 2 : 1);
170 if (falseInitStateIndex != -1) {
171 codeStream.removeNotDefinitelyAssignedVariables(
173 falseInitStateIndex);
174 codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
176 valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
177 // End of if statement
180 // May loose some local variable initializations : affecting the local variable attributes
181 if (mergedInitStateIndex != -1) {
182 codeStream.removeNotDefinitelyAssignedVariables(
184 mergedInitStateIndex);
186 // implicit conversion
188 codeStream.generateImplicitConversion(implicitConversion);
189 codeStream.recordPositionsFrom(pc, this.sourceStart);
193 * Optimized boolean code generation for the conditional operator ?:
195 public void generateOptimizedBoolean(
196 BlockScope currentScope,
197 CodeStream codeStream,
200 boolean valueRequired) {
202 if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean) // constant
203 || (valueIfTrue.implicitConversion >> 4) != T_boolean) { // non boolean values
204 super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
207 Constant cst = condition.constant;
208 Constant condCst = condition.optimizedBooleanConstant();
209 boolean needTruePart =
210 !(((cst != NotAConstant) && (cst.booleanValue() == false))
211 || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
212 boolean needFalsePart =
213 !(((cst != NotAConstant) && (cst.booleanValue() == true))
214 || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
216 Label internalFalseLabel, endifLabel = new Label(codeStream);
218 // Generate code for the condition
219 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
220 condition.generateOptimizedBoolean(
224 internalFalseLabel = new Label(codeStream),
227 if (trueInitStateIndex != -1) {
228 codeStream.removeNotDefinitelyAssignedVariables(
231 codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
233 // Then code generation
235 valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
238 // Jump over the else part
239 int position = codeStream.position;
240 codeStream.goto_(endifLabel);
241 codeStream.updateLastRecordedEndPC(position);
242 // No need to decrement codestream stack size
243 // since valueIfTrue was already consumed by branch bytecode
247 internalFalseLabel.place();
248 if (falseInitStateIndex != -1) {
249 codeStream.removeNotDefinitelyAssignedVariables(
251 falseInitStateIndex);
252 codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
254 valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
256 // End of if statement
259 // May loose some local variable initializations : affecting the local variable attributes
260 if (mergedInitStateIndex != -1) {
261 codeStream.removeNotDefinitelyAssignedVariables(
263 mergedInitStateIndex);
265 // no implicit conversion for boolean values
266 codeStream.updateLastRecordedEndPC(codeStream.position);
269 public Constant optimizedBooleanConstant() {
271 return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
274 public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
276 condition.printExpression(indent, output).append(" ? "); //$NON-NLS-1$
277 valueIfTrue.printExpression(0, output).append(" : "); //$NON-NLS-1$
278 return valueIfFalse.printExpression(0, output);
281 public TypeBinding resolveType(BlockScope scope) {
283 constant = NotAConstant;
284 TypeBinding conditionType = condition.resolveTypeExpecting(scope, BooleanBinding);
286 if (valueIfTrue instanceof CastExpression) valueIfTrue.bits |= IgnoreNeedForCastCheckMASK; // will check later on
287 TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope);
289 if (valueIfFalse instanceof CastExpression) valueIfFalse.bits |= IgnoreNeedForCastCheckMASK; // will check later on
290 TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope);
292 if (conditionType == null || valueIfTrueType == null || valueIfFalseType == null)
295 // Propagate the constant value from the valueIfTrue and valueIFFalse expression if it is possible
296 Constant condConstant, trueConstant, falseConstant;
297 if ((condConstant = condition.constant) != NotAConstant
298 && (trueConstant = valueIfTrue.constant) != NotAConstant
299 && (falseConstant = valueIfFalse.constant) != NotAConstant) {
300 // all terms are constant expression so we can propagate the constant
301 // from valueIFTrue or valueIfFalse to teh receiver constant
302 constant = condConstant.booleanValue() ? trueConstant : falseConstant;
304 if (valueIfTrueType == valueIfFalseType) { // harmed the implicit conversion
305 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
306 valueIfFalse.implicitConversion = valueIfTrue.implicitConversion;
307 if (valueIfTrueType == BooleanBinding) {
308 this.optimizedIfTrueConstant = valueIfTrue.optimizedBooleanConstant();
309 this.optimizedIfFalseConstant = valueIfFalse.optimizedBooleanConstant();
310 if (this.optimizedIfTrueConstant != NotAConstant
311 && this.optimizedIfFalseConstant != NotAConstant
312 && this.optimizedIfTrueConstant.booleanValue() == this.optimizedIfFalseConstant.booleanValue()) {
313 // a ? true : true / a ? false : false
314 this.optimizedBooleanConstant = optimizedIfTrueConstant;
315 } else if ((condConstant = condition.optimizedBooleanConstant()) != NotAConstant) { // Propagate the optimized boolean constant if possible
316 this.optimizedBooleanConstant = condConstant.booleanValue()
317 ? this.optimizedIfTrueConstant
318 : this.optimizedIfFalseConstant;
321 return this.resolvedType = valueIfTrueType;
323 // Determine the return type depending on argument types
325 if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) {
326 // (Short x Byte) or (Byte x Short)"
327 if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding)
328 || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding)) {
329 valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType);
330 valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType);
331 return this.resolvedType = ShortBinding;
333 // <Byte|Short|Char> x constant(Int) ---> <Byte|Short|Char> and reciprocally
334 if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding || valueIfTrueType == CharBinding)
335 && (valueIfFalseType == IntBinding
336 && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, valueIfTrueType))) {
337 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
338 valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
339 return this.resolvedType = valueIfTrueType;
341 if ((valueIfFalseType == ByteBinding
342 || valueIfFalseType == ShortBinding
343 || valueIfFalseType == CharBinding)
344 && (valueIfTrueType == IntBinding
345 && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, valueIfFalseType))) {
346 valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
347 valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
348 return this.resolvedType = valueIfFalseType;
350 // Manual binary numeric promotion
352 if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int)
353 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) {
354 valueIfTrue.implicitWidening(IntBinding, valueIfTrueType);
355 valueIfFalse.implicitWidening(IntBinding, valueIfFalseType);
356 return this.resolvedType = IntBinding;
359 if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long)
360 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) {
361 valueIfTrue.implicitWidening(LongBinding, valueIfTrueType);
362 valueIfFalse.implicitWidening(LongBinding, valueIfFalseType);
363 return this.resolvedType = LongBinding;
366 if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float)
367 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) {
368 valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType);
369 valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType);
370 return this.resolvedType = FloatBinding;
373 valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType);
374 valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType);
375 return this.resolvedType = DoubleBinding;
377 // Type references (null null is already tested)
378 if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
379 || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
380 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
386 if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) {
387 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
388 valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
389 return this.resolvedType = valueIfTrueType;
391 if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) {
392 valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
393 valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
394 return this.resolvedType = valueIfFalseType;
396 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
403 public void traverse(ASTVisitor visitor, BlockScope scope) {
404 if (visitor.visit(this, scope)) {
405 condition.traverse(visitor, scope);
406 valueIfTrue.traverse(visitor, scope);
407 valueIfFalse.traverse(visitor, scope);
409 visitor.endVisit(this, scope);