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
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
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;
23 public class ConditionalExpression extends OperatorExpression {
25 public Expression condition, valueIfTrue, valueIfFalse;
26 private int returnTypeSlotSize = 1;
28 // for local variables table attributes
29 int thenInitStateIndex = -1;
30 int elseInitStateIndex = -1;
31 int mergedInitStateIndex = -1;
33 public ConditionalExpression(
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;
44 public FlowInfo analyseCode(
45 BlockScope currentScope,
46 FlowContext flowContext,
49 Constant conditionConstant = condition.conditionalConstant();
51 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, conditionConstant == NotAConstant);
53 if (conditionConstant != NotAConstant) {
54 if (conditionConstant.booleanValue() == true) {
55 // TRUE ? left : right
57 valueIfTrue.analyseCode(currentScope, flowContext, flowInfo.initsWhenTrue().unconditionalInits());
58 // analyse valueIfFalse, but do not take into account any of its infos
59 valueIfFalse.analyseCode(
62 flowInfo.initsWhenFalse().copy().unconditionalInits().markAsFakeReachable(true));
63 mergedInitStateIndex =
64 currentScope.methodScope().recordInitializationStates(resultInfo);
67 // FALSE ? left : right
68 // analyse valueIfTrue, but do not take into account any of its infos
69 valueIfTrue.analyseCode(
72 flowInfo.initsWhenTrue().copy().unconditionalInits().markAsFakeReachable(true));
74 valueIfFalse.analyseCode(currentScope, flowContext, flowInfo.initsWhenFalse().unconditionalInits());
75 mergedInitStateIndex =
76 currentScope.methodScope().recordInitializationStates(mergeInfo);
81 // store a copy of the merged info, so as to compute the local variable attributes afterwards
82 FlowInfo trueInfo = flowInfo.initsWhenTrue();
84 currentScope.methodScope().recordInitializationStates(trueInfo);
85 FlowInfo falseInfo = flowInfo.initsWhenFalse();
87 currentScope.methodScope().recordInitializationStates(falseInfo);
90 trueInfo = valueIfTrue.analyseCode(currentScope, flowContext, trueInfo.copy());
92 valueIfFalse.analyseCode(currentScope, flowContext, falseInfo.copy());
94 // merge back using a conditional info - 1GK2BLM
95 // if ((t && (v = t)) ? t : t && (v = f)) r = v; -- ok
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()));
103 FlowInfo mergedInfo = valueIfTrue.analyseCode(
106 flowInfo.initsWhenTrue().copy()).
107 unconditionalInits().
109 valueIfFalse.analyseCode(
112 flowInfo.initsWhenFalse().copy()).
113 unconditionalInits());
115 mergedInitStateIndex =
116 currentScope.methodScope().recordInitializationStates(mergedInfo);
121 * Code generation for the conditional operator ?:
123 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
124 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
125 * @param valueRequired boolean
127 public void generateCode(
128 BlockScope currentScope,
129 CodeStream codeStream,
130 boolean valueRequired) {
132 int pc = codeStream.position;
133 Label endifLabel, falseLabel;
134 if (constant != NotAConstant) {
136 codeStream.generateConstant(constant, implicitConversion);
137 codeStream.recordPositionsFrom(pc, this.sourceStart);
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);
150 // Generate code for the condition
151 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
152 condition.generateOptimizedBoolean(
156 (falseLabel = new Label(codeStream)),
159 if (thenInitStateIndex != -1) {
160 codeStream.removeNotDefinitelyAssignedVariables(
163 codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex);
165 // Then code generation
167 valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
169 // Jump over the else part
170 int position = codeStream.position;
171 codeStream.goto_(endifLabel);
172 codeStream.updateLastRecordedEndPC(position);
173 // Tune codestream stack size
175 codeStream.decrStackSize(returnTypeSlotSize);
181 if (elseInitStateIndex != -1) {
182 codeStream.removeNotDefinitelyAssignedVariables(
185 codeStream.addDefinitelyAssignedVariables(currentScope, elseInitStateIndex);
187 valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
188 // End of if statement
191 // May loose some local variable initializations : affecting the local variable attributes
192 if (mergedInitStateIndex != -1) {
193 codeStream.removeNotDefinitelyAssignedVariables(
195 mergedInitStateIndex);
197 // implicit conversion
199 codeStream.generateImplicitConversion(implicitConversion);
200 codeStream.recordPositionsFrom(pc, this.sourceStart);
204 * Optimized boolean code generation for the conditional operator ?:
206 public void generateOptimizedBoolean(
207 BlockScope currentScope,
208 CodeStream codeStream,
211 boolean valueRequired) {
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);
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)));
228 Label internalFalseLabel, endifLabel = new Label(codeStream);
230 // Generate code for the condition
231 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
232 condition.generateOptimizedBoolean(
236 internalFalseLabel = new Label(codeStream),
239 if (thenInitStateIndex != -1) {
240 codeStream.removeNotDefinitelyAssignedVariables(
243 codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex);
245 // Then code generation
247 valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
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);
261 internalFalseLabel.place();
262 if (elseInitStateIndex != -1) {
263 codeStream.removeNotDefinitelyAssignedVariables(
266 codeStream.addDefinitelyAssignedVariables(currentScope, elseInitStateIndex);
268 valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
270 // End of if statement
273 // May loose some local variable initializations : affecting the local variable attributes
274 if (mergedInitStateIndex != -1) {
275 codeStream.removeNotDefinitelyAssignedVariables(
277 mergedInitStateIndex);
279 // no implicit conversion for boolean values
280 codeStream.recordPositionsFrom(pc, this.sourceStart);
283 public TypeBinding resolveType(BlockScope scope) {
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)
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
299 (condition.constant.booleanValue())
300 ? valueIfTrue.constant
301 : valueIfFalse.constant;
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;
309 this.typeBinding = valueIfTrueType;
310 return valueIfTrueType;
312 // Determine the return type depending on argument 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;
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;
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;
342 // Manual binary numeric promotion
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;
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;
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;
369 valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType);
370 valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType);
371 returnTypeSlotSize = 2;
372 this.typeBinding = DoubleBinding;
373 return DoubleBinding;
375 // Type references (null null is already tested)
376 if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
377 || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
378 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
384 if (BlockScope.areTypesCompatible(valueIfFalseType, valueIfTrueType)) {
385 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
386 valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
387 this.typeBinding = valueIfTrueType;
388 return valueIfTrueType;
390 if (BlockScope.areTypesCompatible(valueIfTrueType, valueIfFalseType)) {
391 valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
392 valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
393 this.typeBinding = valueIfFalseType;
394 return valueIfFalseType;
396 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
403 public String toStringExpressionNoParenthesis() {
404 return condition.toStringExpression() + " ? " + //$NON-NLS-1$
405 valueIfTrue.toStringExpression() + " : " + //$NON-NLS-1$
406 valueIfFalse.toStringExpression();
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);
415 visitor.endVisit(this, scope);