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.impl.*;
15 import net.sourceforge.phpdt.internal.compiler.codegen.*;
16 import net.sourceforge.phpdt.internal.compiler.flow.*;
17 import net.sourceforge.phpdt.internal.compiler.lookup.*;
19 public class ConditionalExpression extends OperatorExpression {
21 public Expression condition, valueIfTrue, valueIfFalse;
22 private int returnTypeSlotSize = 1;
24 // for local variables table attributes
25 int thenInitStateIndex = -1;
26 int elseInitStateIndex = -1;
27 int mergedInitStateIndex = -1;
29 public ConditionalExpression(
31 Expression valueIfTrue,
32 Expression valueIfFalse) {
33 this.condition = condition;
34 this.valueIfTrue = valueIfTrue;
35 this.valueIfFalse = valueIfFalse;
36 sourceStart = condition.sourceStart;
37 sourceEnd = valueIfFalse.sourceEnd;
40 public FlowInfo analyseCode(
41 BlockScope currentScope,
42 FlowContext flowContext,
45 Constant conditionConstant = condition.conditionalConstant();
47 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, conditionConstant == NotAConstant);
49 if (conditionConstant != NotAConstant) {
50 if (conditionConstant.booleanValue() == true) {
51 // TRUE ? left : right
53 valueIfTrue.analyseCode(currentScope, flowContext, flowInfo.initsWhenTrue().unconditionalInits());
54 // analyse valueIfFalse, but do not take into account any of its infos
55 valueIfFalse.analyseCode(
58 flowInfo.initsWhenFalse().copy().unconditionalInits().markAsFakeReachable(true));
59 mergedInitStateIndex =
60 currentScope.methodScope().recordInitializationStates(resultInfo);
63 // FALSE ? left : right
64 // analyse valueIfTrue, but do not take into account any of its infos
65 valueIfTrue.analyseCode(
68 flowInfo.initsWhenTrue().copy().unconditionalInits().markAsFakeReachable(true));
70 valueIfFalse.analyseCode(currentScope, flowContext, flowInfo.initsWhenFalse().unconditionalInits());
71 mergedInitStateIndex =
72 currentScope.methodScope().recordInitializationStates(mergeInfo);
77 // store a copy of the merged info, so as to compute the local variable attributes afterwards
78 FlowInfo trueInfo = flowInfo.initsWhenTrue();
80 currentScope.methodScope().recordInitializationStates(trueInfo);
81 FlowInfo falseInfo = flowInfo.initsWhenFalse();
83 currentScope.methodScope().recordInitializationStates(falseInfo);
86 trueInfo = valueIfTrue.analyseCode(currentScope, flowContext, trueInfo.copy());
88 valueIfFalse.analyseCode(currentScope, flowContext, falseInfo.copy());
90 // merge back using a conditional info - 1GK2BLM
91 // if ((t && (v = t)) ? t : t && (v = f)) r = v; -- ok
94 trueInfo.initsWhenTrue().copy().unconditionalInits().mergedWith( // must copy, since could be shared with trueInfo.initsWhenFalse()...
95 falseInfo.initsWhenTrue().copy().unconditionalInits()),
96 trueInfo.initsWhenFalse().unconditionalInits().mergedWith(
97 falseInfo.initsWhenFalse().unconditionalInits()));
99 FlowInfo mergedInfo = valueIfTrue.analyseCode(
102 flowInfo.initsWhenTrue().copy()).
103 unconditionalInits().
105 valueIfFalse.analyseCode(
108 flowInfo.initsWhenFalse().copy()).
109 unconditionalInits());
111 mergedInitStateIndex =
112 currentScope.methodScope().recordInitializationStates(mergedInfo);
117 * Code generation for the conditional operator ?:
119 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
120 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
121 * @param valueRequired boolean
123 public void generateCode(
124 BlockScope currentScope,
125 CodeStream codeStream,
126 boolean valueRequired) {
128 int pc = codeStream.position;
129 Label endifLabel, falseLabel;
130 if (constant != NotAConstant) {
132 codeStream.generateConstant(constant, implicitConversion);
133 codeStream.recordPositionsFrom(pc, this.sourceStart);
136 Constant cst = condition.constant;
137 Constant condCst = condition.conditionalConstant();
138 boolean needTruePart =
139 !(((cst != NotAConstant) && (cst.booleanValue() == false))
140 || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
141 boolean needFalsePart =
142 !(((cst != NotAConstant) && (cst.booleanValue() == true))
143 || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
144 endifLabel = new Label(codeStream);
146 // Generate code for the condition
147 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
148 condition.generateOptimizedBoolean(
152 (falseLabel = new Label(codeStream)),
155 if (thenInitStateIndex != -1) {
156 codeStream.removeNotDefinitelyAssignedVariables(
159 codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex);
161 // Then code generation
163 valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
165 // Jump over the else part
166 int position = codeStream.position;
167 codeStream.goto_(endifLabel);
168 codeStream.updateLastRecordedEndPC(position);
169 // Tune codestream stack size
171 codeStream.decrStackSize(returnTypeSlotSize);
177 if (elseInitStateIndex != -1) {
178 codeStream.removeNotDefinitelyAssignedVariables(
181 codeStream.addDefinitelyAssignedVariables(currentScope, elseInitStateIndex);
183 valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
184 // End of if statement
187 // May loose some local variable initializations : affecting the local variable attributes
188 if (mergedInitStateIndex != -1) {
189 codeStream.removeNotDefinitelyAssignedVariables(
191 mergedInitStateIndex);
193 // implicit conversion
195 codeStream.generateImplicitConversion(implicitConversion);
196 codeStream.recordPositionsFrom(pc, this.sourceStart);
200 * Optimized boolean code generation for the conditional operator ?:
202 public void generateOptimizedBoolean(
203 BlockScope currentScope,
204 CodeStream codeStream,
207 boolean valueRequired) {
209 if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean) // constant
210 || (valueIfTrue.implicitConversion >> 4) != T_boolean) { // non boolean values
211 super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
214 int pc = codeStream.position;
215 Constant cst = condition.constant;
216 Constant condCst = condition.conditionalConstant();
217 boolean needTruePart =
218 !(((cst != NotAConstant) && (cst.booleanValue() == false))
219 || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
220 boolean needFalsePart =
221 !(((cst != NotAConstant) && (cst.booleanValue() == true))
222 || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
224 Label internalFalseLabel, endifLabel = new Label(codeStream);
226 // Generate code for the condition
227 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
228 condition.generateOptimizedBoolean(
232 internalFalseLabel = new Label(codeStream),
235 if (thenInitStateIndex != -1) {
236 codeStream.removeNotDefinitelyAssignedVariables(
239 codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex);
241 // Then code generation
243 valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
246 // Jump over the else part
247 int position = codeStream.position;
248 codeStream.goto_(endifLabel);
249 codeStream.updateLastRecordedEndPC(position);
250 // Tune codestream stack size
251 //if (valueRequired) {
252 // codeStream.decrStackSize(returnTypeSlotSize);
257 internalFalseLabel.place();
258 if (elseInitStateIndex != -1) {
259 codeStream.removeNotDefinitelyAssignedVariables(
262 codeStream.addDefinitelyAssignedVariables(currentScope, elseInitStateIndex);
264 valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
266 // End of if statement
269 // May loose some local variable initializations : affecting the local variable attributes
270 if (mergedInitStateIndex != -1) {
271 codeStream.removeNotDefinitelyAssignedVariables(
273 mergedInitStateIndex);
275 // no implicit conversion for boolean values
276 codeStream.recordPositionsFrom(pc, this.sourceStart);
279 public TypeBinding resolveType(BlockScope scope) {
281 constant = NotAConstant;
282 TypeBinding conditionType = condition.resolveTypeExpecting(scope, BooleanBinding);
283 TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope);
284 TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope);
285 if (conditionType == null || valueIfTrueType == null || valueIfFalseType == null)
288 // Propagate the constant value from the valueIfTrue and valueIFFalse expression if it is possible
289 if (condition.constant != NotAConstant
290 && valueIfTrue.constant != NotAConstant
291 && valueIfFalse.constant != NotAConstant) {
292 // all terms are constant expression so we can propagate the constant
293 // from valueIFTrue or valueIfFalse to teh receiver constant
295 (condition.constant.booleanValue())
296 ? valueIfTrue.constant
297 : valueIfFalse.constant;
299 if (valueIfTrueType == valueIfFalseType) { // harmed the implicit conversion
300 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
301 valueIfFalse.implicitConversion = valueIfTrue.implicitConversion;
302 if (valueIfTrueType == LongBinding || valueIfTrueType == DoubleBinding) {
303 returnTypeSlotSize = 2;
305 this.typeBinding = valueIfTrueType;
306 return valueIfTrueType;
308 // Determine the return type depending on argument types
310 if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) {
311 // (Short x Byte) or (Byte x Short)"
312 if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding)
313 || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding)) {
314 valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType);
315 valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType);
316 this.typeBinding = ShortBinding;
319 // <Byte|Short|Char> x constant(Int) ---> <Byte|Short|Char> and reciprocally
320 if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding || valueIfTrueType == CharBinding)
321 && (valueIfFalseType == IntBinding
322 && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, valueIfTrueType))) {
323 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
324 valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
325 this.typeBinding = valueIfTrueType;
326 return valueIfTrueType;
328 if ((valueIfFalseType == ByteBinding
329 || valueIfFalseType == ShortBinding
330 || valueIfFalseType == CharBinding)
331 && (valueIfTrueType == IntBinding
332 && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, valueIfFalseType))) {
333 valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
334 valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
335 this.typeBinding = valueIfFalseType;
336 return valueIfFalseType;
338 // Manual binary numeric promotion
340 if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int)
341 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) {
342 valueIfTrue.implicitWidening(IntBinding, valueIfTrueType);
343 valueIfFalse.implicitWidening(IntBinding, valueIfFalseType);
344 this.typeBinding = IntBinding;
348 if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long)
349 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) {
350 valueIfTrue.implicitWidening(LongBinding, valueIfTrueType);
351 valueIfFalse.implicitWidening(LongBinding, valueIfFalseType);
352 returnTypeSlotSize = 2;
353 this.typeBinding = LongBinding;
357 if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float)
358 && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) {
359 valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType);
360 valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType);
361 this.typeBinding = FloatBinding;
365 valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType);
366 valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType);
367 returnTypeSlotSize = 2;
368 this.typeBinding = DoubleBinding;
369 return DoubleBinding;
371 // Type references (null null is already tested)
372 if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
373 || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
374 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
380 if (scope.areTypesCompatible(valueIfFalseType, valueIfTrueType)) {
381 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
382 valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
383 this.typeBinding = valueIfTrueType;
384 return valueIfTrueType;
386 if (scope.areTypesCompatible(valueIfTrueType, valueIfFalseType)) {
387 valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
388 valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
389 this.typeBinding = valueIfFalseType;
390 return valueIfFalseType;
392 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
399 public String toStringExpressionNoParenthesis() {
400 return condition.toStringExpression() + " ? " + //$NON-NLS-1$
401 valueIfTrue.toStringExpression() + " : " + //$NON-NLS-1$
402 valueIfFalse.toStringExpression();
405 public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
406 if (visitor.visit(this, scope)) {
407 condition.traverse(visitor, scope);
408 valueIfTrue.traverse(visitor, scope);
409 valueIfFalse.traverse(visitor, scope);
411 visitor.endVisit(this, scope);