package org.eclipse.jdt.internal.compiler.ast;
import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.*;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class ConditionalExpression extends OperatorExpression implements IPolyExpression {
public Expression condition, valueIfTrue, valueIfFalse;
public Constant optimizedBooleanConstant;
public Constant optimizedIfTrueConstant;
public Constant optimizedIfFalseConstant;
int trueInitStateIndex = -1;
int falseInitStateIndex = -1;
int mergedInitStateIndex = -1;
private int nullStatus = FlowInfo.UNKNOWN;
int ifFalseNullStatus;
int ifTrueNullStatus;
private TypeBinding expectedType;
private ExpressionContext expressionContext = VANILLA_CONTEXT;
private boolean isPolyExpression = false;
private TypeBinding originalValueIfTrueType;
private TypeBinding originalValueIfFalseType;
private boolean use18specifics;
public ConditionalExpression(Expression condition, Expression valueIfTrue, Expression valueIfFalse) {
this.condition = condition;
this.valueIfTrue = valueIfTrue;
this.valueIfFalse = valueIfFalse;
this.sourceStart = condition.sourceStart;
this.sourceEnd = valueIfFalse.sourceEnd;
}
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
FlowInfo flowInfo) {
int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED;
Constant cst = this.condition.optimizedBooleanConstant();
boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
int mode = flowInfo.reachMode();
flowInfo = this.condition.analyseCode(currentScope, flowContext, flowInfo, cst == Constant.NotAConstant);
flowContext.conditionalLevel++;
FlowInfo trueFlowInfo = flowInfo.initsWhenTrue().copy();
final CompilerOptions compilerOptions = currentScope.compilerOptions();
if (isConditionOptimizedFalse) {
if ((mode & FlowInfo.UNREACHABLE) == 0) {
trueFlowInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
if (!isKnowDeadCodePattern(this.condition) || compilerOptions.reportDeadCodeInTrivialIfStatement) {
this.valueIfTrue.complainIfUnreachable(trueFlowInfo, currentScope, initialComplaintLevel, false);
}
}
this.trueInitStateIndex = currentScope.methodScope().recordInitializationStates(trueFlowInfo);
trueFlowInfo = this.valueIfTrue.analyseCode(currentScope, flowContext, trueFlowInfo);
this.valueIfTrue.checkNPEbyUnboxing(currentScope, flowContext, trueFlowInfo);
this.ifTrueNullStatus = -1;
if (compilerOptions.enableSyntacticNullAnalysisForFields) {
this.ifTrueNullStatus = this.valueIfTrue.nullStatus(trueFlowInfo, flowContext);
flowContext.expireNullCheckedFieldInfo();
}
FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy();
if (isConditionOptimizedTrue) {
if ((mode & FlowInfo.UNREACHABLE) == 0) {
falseFlowInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
if (!isKnowDeadCodePattern(this.condition) || compilerOptions.reportDeadCodeInTrivialIfStatement) {
this.valueIfFalse.complainIfUnreachable(falseFlowInfo, currentScope, initialComplaintLevel, true);
}
}
this.falseInitStateIndex = currentScope.methodScope().recordInitializationStates(falseFlowInfo);
falseFlowInfo = this.valueIfFalse.analyseCode(currentScope, flowContext, falseFlowInfo);
this.valueIfFalse.checkNPEbyUnboxing(currentScope, flowContext, falseFlowInfo);
flowContext.conditionalLevel--;
FlowInfo mergedInfo;
if (isConditionOptimizedTrue){
mergedInfo = trueFlowInfo.addPotentialInitializationsFrom(falseFlowInfo);
if (this.ifTrueNullStatus != -1) {
this.nullStatus = this.ifTrueNullStatus;
} else {
this.nullStatus = this.valueIfTrue.nullStatus(trueFlowInfo, flowContext);
}
} else if (isConditionOptimizedFalse) {
mergedInfo = falseFlowInfo.addPotentialInitializationsFrom(trueFlowInfo);
this.nullStatus = this.valueIfFalse.nullStatus(falseFlowInfo, flowContext);
} else {
computeNullStatus(trueFlowInfo, falseFlowInfo, flowContext);
cst = this.optimizedIfTrueConstant;
boolean isValueIfTrueOptimizedTrue = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == true;
boolean isValueIfTrueOptimizedFalse = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == false;
cst = this.optimizedIfFalseConstant;
boolean isValueIfFalseOptimizedTrue = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == true;
boolean isValueIfFalseOptimizedFalse = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == false;
UnconditionalFlowInfo trueFlowTowardsTrue = trueFlowInfo.initsWhenTrue().unconditionalCopy();
UnconditionalFlowInfo falseFlowTowardsTrue = falseFlowInfo.initsWhenTrue().unconditionalCopy();
UnconditionalFlowInfo trueFlowTowardsFalse = trueFlowInfo.initsWhenFalse().unconditionalInits();
UnconditionalFlowInfo falseFlowTowardsFalse = falseFlowInfo.initsWhenFalse().unconditionalInits();
if (isValueIfTrueOptimizedFalse) {
trueFlowTowardsTrue.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
if (isValueIfFalseOptimizedFalse) {
falseFlowTowardsTrue.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
if (isValueIfTrueOptimizedTrue) {
trueFlowTowardsFalse.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
if (isValueIfFalseOptimizedTrue) {
falseFlowTowardsFalse.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
mergedInfo =
FlowInfo.conditional(
trueFlowTowardsTrue.mergedWith(falseFlowTowardsTrue),
trueFlowTowardsFalse.mergedWith(falseFlowTowardsFalse));
}
this.mergedInitStateIndex =
currentScope.methodScope().recordInitializationStates(mergedInfo);
mergedInfo.setReachMode(mode);
return mergedInfo;
}
@Override
public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
if ((this.nullStatus & FlowInfo.NULL) != 0)
scope.problemReporter().expressionNullReference(this);
else if ((this.nullStatus & FlowInfo.POTENTIALLY_NULL) != 0)
scope.problemReporter().expressionPotentialNullReference(this);
return true;
}
private void computeNullStatus(FlowInfo trueBranchInfo, FlowInfo falseBranchInfo, FlowContext flowContext) {
if (this.ifTrueNullStatus == -1) {
this.ifTrueNullStatus = this.valueIfTrue.nullStatus(trueBranchInfo, flowContext);
}
this.ifFalseNullStatus = this.valueIfFalse.nullStatus(falseBranchInfo, flowContext);
if (this.ifTrueNullStatus == this.ifFalseNullStatus) {
this.nullStatus = this.ifTrueNullStatus;
return;
}
if (trueBranchInfo.reachMode() != FlowInfo.REACHABLE) {
this.nullStatus = this.ifFalseNullStatus;
return;
}
if (falseBranchInfo.reachMode() != FlowInfo.REACHABLE) {
this.nullStatus = this.ifTrueNullStatus;
return;
}
int combinedStatus = this.ifTrueNullStatus|this.ifFalseNullStatus;
int status = Expression.computeNullStatus(0, combinedStatus);
if (status > 0)
this.nullStatus = status;
}
@Override
public void generateCode(
BlockScope currentScope,
CodeStream codeStream,
boolean valueRequired) {
int pc = codeStream.position;
BranchLabel endifLabel, falseLabel;
if (this.constant != Constant.NotAConstant) {
if (valueRequired)
codeStream.generateConstant(this.constant, this.implicitConversion);
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
Constant cst = this.condition.optimizedBooleanConstant();
if (cst == Constant.NotAConstant) {
cst = this.condition.optimizedNullComparisonConstant();
}
boolean needTruePart = !(cst != Constant.NotAConstant && cst.booleanValue() == false);
boolean needFalsePart = !(cst != Constant.NotAConstant && cst.booleanValue() == true);
endifLabel = new BranchLabel(codeStream);
falseLabel = new BranchLabel(codeStream);
falseLabel.tagBits |= BranchLabel.USED;
this.condition.generateOptimizedBoolean(
currentScope,
codeStream,
null,
falseLabel,
cst == Constant.NotAConstant);
if (this.trueInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(
currentScope,
this.trueInitStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.trueInitStateIndex);
}
if (needTruePart) {
this.valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
if (needFalsePart) {
int position = codeStream.position;
codeStream.goto_(endifLabel);
codeStream.recordPositionsFrom(position, this.valueIfTrue.sourceEnd);
if (valueRequired) {
switch(this.resolvedType.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.decrStackSize(2);
break;
default :
codeStream.decrStackSize(1);
break;
}
}
}
}
if (needFalsePart) {
if (this.falseInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(
currentScope,
this.falseInitStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.falseInitStateIndex);
}
if (falseLabel.forwardReferenceCount() > 0) {
falseLabel.place();
}
this.valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
codeStream.recordExpressionType(this.resolvedType);
}
if (needTruePart) {
endifLabel.place();
}
}
if (this.mergedInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(
currentScope,
this.mergedInitStateIndex);
}
if (valueRequired)
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
@Override
public void generateOptimizedBoolean(
BlockScope currentScope,
CodeStream codeStream,
BranchLabel trueLabel,
BranchLabel falseLabel,
boolean valueRequired) {
int pc = codeStream.position;
if ((this.constant != Constant.NotAConstant) && (this.constant.typeID() == T_boolean)
|| ((this.valueIfTrue.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) != T_boolean
|| ((this.valueIfFalse.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) != T_boolean) {
super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
return;
}
Constant cst = this.condition.constant;
Constant condCst = this.condition.optimizedBooleanConstant();
boolean needTruePart =
!(((cst != Constant.NotAConstant) && (cst.booleanValue() == false))
|| ((condCst != Constant.NotAConstant) && (condCst.booleanValue() == false)));
boolean needFalsePart =
!(((cst != Constant.NotAConstant) && (cst.booleanValue() == true))
|| ((condCst != Constant.NotAConstant) && (condCst.booleanValue() == true)));
BranchLabel internalFalseLabel, endifLabel = new BranchLabel(codeStream);
boolean needConditionValue = (cst == Constant.NotAConstant) && (condCst == Constant.NotAConstant);
this.condition.generateOptimizedBoolean(
currentScope,
codeStream,
null,
internalFalseLabel = new BranchLabel(codeStream),
needConditionValue);
if (this.trueInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(
currentScope,
this.trueInitStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.trueInitStateIndex);
}
if (needTruePart) {
this.valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
if (needFalsePart) {
JumpEndif: {
if (falseLabel == null) {
if (trueLabel != null) {
cst = this.optimizedIfTrueConstant;
boolean isValueIfTrueOptimizedTrue = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == true;
if (isValueIfTrueOptimizedTrue) break JumpEndif;
}
} else {
if (trueLabel == null) {
cst = this.optimizedIfTrueConstant;
boolean isValueIfTrueOptimizedFalse = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == false;
if (isValueIfTrueOptimizedFalse) break JumpEndif;
} else {
}
}
int position = codeStream.position;
codeStream.goto_(endifLabel);
codeStream.recordPositionsFrom(position, this.valueIfTrue.sourceEnd);
}
}
}
if (needFalsePart) {
internalFalseLabel.place();
if (this.falseInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.falseInitStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.falseInitStateIndex);
}
this.valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
endifLabel.place();
}
if (this.mergedInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
}
codeStream.recordPositionsFrom(pc, this.sourceEnd);
}
@Override
public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
if ((this.implicitConversion & TypeIds.BOXING) != 0)
return FlowInfo.NON_NULL;
return this.nullStatus;
}
@Override
public Constant optimizedBooleanConstant() {
return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
}
@Override
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
this.condition.printExpression(indent, output).append(" ? ");
this.valueIfTrue.printExpression(0, output).append(" : ");
return this.valueIfFalse.printExpression(0, output);
}
@Override
public TypeBinding resolveType(BlockScope scope) {
LookupEnvironment env = scope.environment();
final long sourceLevel = scope.compilerOptions().sourceLevel;
boolean use15specifics = sourceLevel >= ClassFileConstants.JDK1_5;
this.use18specifics = sourceLevel >= ClassFileConstants.JDK1_8;
if (this.use18specifics) {
if (this.expressionContext == ASSIGNMENT_CONTEXT || this.expressionContext == INVOCATION_CONTEXT) {
this.valueIfTrue.setExpressionContext(this.expressionContext);
this.valueIfTrue.setExpectedType(this.expectedType);
this.valueIfFalse.setExpressionContext(this.expressionContext);
this.valueIfFalse.setExpectedType(this.expectedType);
}
}
if (this.constant != Constant.NotAConstant) {
this.constant = Constant.NotAConstant;
TypeBinding conditionType = this.condition.resolveTypeExpecting(scope, TypeBinding.BOOLEAN);
this.condition.computeConversion(scope, TypeBinding.BOOLEAN, conditionType);
if (this.valueIfTrue instanceof CastExpression) this.valueIfTrue.bits |= DisableUnnecessaryCastCheck;
this.originalValueIfTrueType = this.valueIfTrue.resolveType(scope);
if (this.valueIfFalse instanceof CastExpression) this.valueIfFalse.bits |= DisableUnnecessaryCastCheck;
this.originalValueIfFalseType = this.valueIfFalse.resolveType(scope);
if (conditionType == null || this.originalValueIfTrueType == null || this.originalValueIfFalseType == null)
return null;
} else {
if (this.originalValueIfTrueType.kind() == Binding.POLY_TYPE)
this.originalValueIfTrueType = this.valueIfTrue.resolveType(scope);
if (this.originalValueIfFalseType.kind() == Binding.POLY_TYPE)
this.originalValueIfFalseType = this.valueIfFalse.resolveType(scope);
if (this.originalValueIfTrueType == null || !this.originalValueIfTrueType.isValidBinding())
return this.resolvedType = null;
if (this.originalValueIfFalseType == null || !this.originalValueIfFalseType.isValidBinding())
return this.resolvedType = null;
}
if (isPolyExpression()) {
if (this.expectedType == null || !this.expectedType.isProperType(true)) {
return new PolyTypeBinding(this);
}
return this.resolvedType = computeConversions(scope, this.expectedType) ? this.expectedType : null;
}
TypeBinding valueIfTrueType = this.originalValueIfTrueType;
TypeBinding valueIfFalseType = this.originalValueIfFalseType;
if (use15specifics && TypeBinding.notEquals(valueIfTrueType, valueIfFalseType)) {
if (valueIfTrueType.isBaseType()) {
if (valueIfFalseType.isBaseType()) {
if (valueIfTrueType == TypeBinding.NULL) {
valueIfFalseType = env.computeBoxingType(valueIfFalseType);
} else if (valueIfFalseType == TypeBinding.NULL) {
valueIfTrueType = env.computeBoxingType(valueIfTrueType);
}
} else {
TypeBinding unboxedIfFalseType = valueIfFalseType.isBaseType() ? valueIfFalseType : env.computeBoxingType(valueIfFalseType);
if (valueIfTrueType.isNumericType() && unboxedIfFalseType.isNumericType()) {
valueIfFalseType = unboxedIfFalseType;
} else if (valueIfTrueType != TypeBinding.NULL) {
valueIfFalseType = env.computeBoxingType(valueIfFalseType);
}
}
} else if (valueIfFalseType.isBaseType()) {
TypeBinding unboxedIfTrueType = valueIfTrueType.isBaseType() ? valueIfTrueType : env.computeBoxingType(valueIfTrueType);
if (unboxedIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) {
valueIfTrueType = unboxedIfTrueType;
} else if (valueIfFalseType != TypeBinding.NULL) {
valueIfTrueType = env.computeBoxingType(valueIfTrueType);
}
} else {
TypeBinding unboxedIfTrueType = env.computeBoxingType(valueIfTrueType);
TypeBinding unboxedIfFalseType = env.computeBoxingType(valueIfFalseType);
if (unboxedIfTrueType.isNumericType() && unboxedIfFalseType.isNumericType()) {
valueIfTrueType = unboxedIfTrueType;
valueIfFalseType = unboxedIfFalseType;
}
}
}
Constant condConstant, trueConstant, falseConstant;
if ((condConstant = this.condition.constant) != Constant.NotAConstant
&& (trueConstant = this.valueIfTrue.constant) != Constant.NotAConstant
&& (falseConstant = this.valueIfFalse.constant) != Constant.NotAConstant) {
this.constant = condConstant.booleanValue() ? trueConstant : falseConstant;
}
if (TypeBinding.equalsEquals(valueIfTrueType, valueIfFalseType)) {
this.valueIfTrue.computeConversion(scope, valueIfTrueType, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, valueIfFalseType, this.originalValueIfFalseType);
if (TypeBinding.equalsEquals(valueIfTrueType, TypeBinding.BOOLEAN)) {
this.optimizedIfTrueConstant = this.valueIfTrue.optimizedBooleanConstant();
this.optimizedIfFalseConstant = this.valueIfFalse.optimizedBooleanConstant();
if (this.optimizedIfTrueConstant != Constant.NotAConstant
&& this.optimizedIfFalseConstant != Constant.NotAConstant
&& this.optimizedIfTrueConstant.booleanValue() == this.optimizedIfFalseConstant.booleanValue()) {
this.optimizedBooleanConstant = this.optimizedIfTrueConstant;
} else if ((condConstant = this.condition.optimizedBooleanConstant()) != Constant.NotAConstant) {
this.optimizedBooleanConstant = condConstant.booleanValue()
? this.optimizedIfTrueConstant
: this.optimizedIfFalseConstant;
}
}
return this.resolvedType = NullAnnotationMatching.moreDangerousType(valueIfTrueType, valueIfFalseType);
}
if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) {
if ((TypeBinding.equalsEquals(valueIfTrueType, TypeBinding.BYTE) && TypeBinding.equalsEquals(valueIfFalseType, TypeBinding.SHORT))
|| (TypeBinding.equalsEquals(valueIfTrueType, TypeBinding.SHORT) && TypeBinding.equalsEquals(valueIfFalseType, TypeBinding.BYTE))) {
this.valueIfTrue.computeConversion(scope, TypeBinding.SHORT, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, TypeBinding.SHORT, this.originalValueIfFalseType);
return this.resolvedType = TypeBinding.SHORT;
}
if ((TypeBinding.equalsEquals(valueIfTrueType, TypeBinding.BYTE) || TypeBinding.equalsEquals(valueIfTrueType, TypeBinding.SHORT) || TypeBinding.equalsEquals(valueIfTrueType, TypeBinding.CHAR))
&& (TypeBinding.equalsEquals(valueIfFalseType, TypeBinding.INT)
&& this.valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, valueIfTrueType))) {
this.valueIfTrue.computeConversion(scope, valueIfTrueType, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, valueIfTrueType, this.originalValueIfFalseType);
return this.resolvedType = valueIfTrueType;
}
if ((TypeBinding.equalsEquals(valueIfFalseType, TypeBinding.BYTE)
|| TypeBinding.equalsEquals(valueIfFalseType, TypeBinding.SHORT)
|| TypeBinding.equalsEquals(valueIfFalseType, TypeBinding.CHAR))
&& (TypeBinding.equalsEquals(valueIfTrueType, TypeBinding.INT)
&& this.valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, valueIfFalseType))) {
this.valueIfTrue.computeConversion(scope, valueIfFalseType, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, valueIfFalseType, this.originalValueIfFalseType);
return this.resolvedType = valueIfFalseType;
}
if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int)
&& BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) {
this.valueIfTrue.computeConversion(scope, TypeBinding.INT, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, TypeBinding.INT, this.originalValueIfFalseType);
return this.resolvedType = TypeBinding.INT;
}
if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long)
&& BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) {
this.valueIfTrue.computeConversion(scope, TypeBinding.LONG, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, TypeBinding.LONG, this.originalValueIfFalseType);
return this.resolvedType = TypeBinding.LONG;
}
if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float)
&& BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) {
this.valueIfTrue.computeConversion(scope, TypeBinding.FLOAT, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, TypeBinding.FLOAT, this.originalValueIfFalseType);
return this.resolvedType = TypeBinding.FLOAT;
}
this.valueIfTrue.computeConversion(scope, TypeBinding.DOUBLE, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, TypeBinding.DOUBLE, this.originalValueIfFalseType);
return this.resolvedType = TypeBinding.DOUBLE;
}
if (valueIfTrueType.isBaseType() && valueIfTrueType != TypeBinding.NULL) {
if (use15specifics) {
valueIfTrueType = env.computeBoxingType(valueIfTrueType);
} else {
scope.problemReporter().conditionalArgumentsIncompatibleTypes(this, valueIfTrueType, valueIfFalseType);
return null;
}
}
if (valueIfFalseType.isBaseType() && valueIfFalseType != TypeBinding.NULL) {
if (use15specifics) {
valueIfFalseType = env.computeBoxingType(valueIfFalseType);
} else {
scope.problemReporter().conditionalArgumentsIncompatibleTypes(this, valueIfTrueType, valueIfFalseType);
return null;
}
}
if (use15specifics) {
TypeBinding commonType = null;
if (valueIfTrueType == TypeBinding.NULL) {
commonType = valueIfFalseType;
} else if (valueIfFalseType == TypeBinding.NULL) {
commonType = valueIfTrueType;
} else {
commonType = scope.lowerUpperBound(new TypeBinding[] { valueIfTrueType, valueIfFalseType });
}
if (commonType != null) {
this.valueIfTrue.computeConversion(scope, commonType, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, commonType, this.originalValueIfFalseType);
return this.resolvedType = commonType.capture(scope, this.sourceStart, this.sourceEnd);
}
} else {
if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) {
this.valueIfTrue.computeConversion(scope, valueIfTrueType, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, valueIfTrueType, this.originalValueIfFalseType);
return this.resolvedType = valueIfTrueType;
} else if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) {
this.valueIfTrue.computeConversion(scope, valueIfFalseType, this.originalValueIfTrueType);
this.valueIfFalse.computeConversion(scope, valueIfFalseType, this.originalValueIfFalseType);
return this.resolvedType = valueIfFalseType;
}
}
scope.problemReporter().conditionalArgumentsIncompatibleTypes(
this,
valueIfTrueType,
valueIfFalseType);
return null;
}
protected boolean computeConversions(BlockScope scope, TypeBinding targetType) {
boolean ok = true;
if (this.originalValueIfTrueType != null && this.originalValueIfTrueType.isValidBinding()) {
if (this.valueIfTrue.isConstantValueOfTypeAssignableToType(this.originalValueIfTrueType, targetType)
|| this.originalValueIfTrueType.isCompatibleWith(targetType)) {
this.valueIfTrue.computeConversion(scope, targetType, this.originalValueIfTrueType);
if (this.originalValueIfTrueType.needsUncheckedConversion(targetType)) {
scope.problemReporter().unsafeTypeConversion(this.valueIfTrue, this.originalValueIfTrueType, targetType);
}
if (this.valueIfTrue instanceof CastExpression
&& (this.valueIfTrue.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) {
CastExpression.checkNeedForAssignedCast(scope, targetType, (CastExpression) this.valueIfTrue);
}
} else if (isBoxingCompatible(this.originalValueIfTrueType, targetType, this.valueIfTrue, scope)) {
this.valueIfTrue.computeConversion(scope, targetType, this.originalValueIfTrueType);
if (this.valueIfTrue instanceof CastExpression
&& (this.valueIfTrue.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) {
CastExpression.checkNeedForAssignedCast(scope, targetType, (CastExpression) this.valueIfTrue);
}
} else {
scope.problemReporter().typeMismatchError(this.originalValueIfTrueType, targetType, this.valueIfTrue, null);
ok = false;
}
}
if (this.originalValueIfFalseType != null && this.originalValueIfFalseType.isValidBinding()) {
if (this.valueIfFalse.isConstantValueOfTypeAssignableToType(this.originalValueIfFalseType, targetType)
|| this.originalValueIfFalseType.isCompatibleWith(targetType)) {
this.valueIfFalse.computeConversion(scope, targetType, this.originalValueIfFalseType);
if (this.originalValueIfFalseType.needsUncheckedConversion(targetType)) {
scope.problemReporter().unsafeTypeConversion(this.valueIfFalse, this.originalValueIfFalseType, targetType);
}
if (this.valueIfFalse instanceof CastExpression
&& (this.valueIfFalse.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) {
CastExpression.checkNeedForAssignedCast(scope, targetType, (CastExpression) this.valueIfFalse);
}
} else if (isBoxingCompatible(this.originalValueIfFalseType, targetType, this.valueIfFalse, scope)) {
this.valueIfFalse.computeConversion(scope, targetType, this.originalValueIfFalseType);
if (this.valueIfFalse instanceof CastExpression
&& (this.valueIfFalse.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) {
CastExpression.checkNeedForAssignedCast(scope, targetType, (CastExpression) this.valueIfFalse);
}
} else {
scope.problemReporter().typeMismatchError(this.originalValueIfFalseType, targetType, this.valueIfFalse, null);
ok = false;
}
}
return ok;
}
@Override
public void setExpectedType(TypeBinding expectedType) {
this.expectedType = expectedType;
}
@Override
public void setExpressionContext(ExpressionContext context) {
this.expressionContext = context;
}
@Override
public ExpressionContext getExpressionContext() {
return this.expressionContext;
}
@Override
public Expression[] getPolyExpressions() {
Expression [] truePolys = this.valueIfTrue.getPolyExpressions();
Expression [] falsePolys = this.valueIfFalse.getPolyExpressions();
if (truePolys.length == 0)
return falsePolys;
if (falsePolys.length == 0)
return truePolys;
Expression [] allPolys = new Expression [truePolys.length + falsePolys.length];
System.arraycopy(truePolys, 0, allPolys, 0, truePolys.length);
System.arraycopy(falsePolys, 0, allPolys, truePolys.length, falsePolys.length);
return allPolys;
}
@Override
public boolean isPertinentToApplicability(TypeBinding targetType, MethodBinding method) {
return this.valueIfTrue.isPertinentToApplicability(targetType, method)
&& this.valueIfFalse.isPertinentToApplicability(targetType, method);
}
@Override
public boolean isPotentiallyCompatibleWith(TypeBinding targetType, Scope scope) {
return this.valueIfTrue.isPotentiallyCompatibleWith(targetType, scope)
&& this.valueIfFalse.isPotentiallyCompatibleWith(targetType, scope);
}
@Override
public boolean isFunctionalType() {
return this.valueIfTrue.isFunctionalType() || this.valueIfFalse.isFunctionalType();
}
@Override
public boolean isPolyExpression() throws UnsupportedOperationException {
if (!this.use18specifics)
return false;
if (this.isPolyExpression)
return true;
if (this.expressionContext != ASSIGNMENT_CONTEXT && this.expressionContext != INVOCATION_CONTEXT)
return false;
if (this.originalValueIfTrueType == null || this.originalValueIfFalseType == null)
return false;
if (this.valueIfTrue.isPolyExpression() || this.valueIfFalse.isPolyExpression())
return true;
if (this.originalValueIfTrueType.isBaseType() || (this.originalValueIfTrueType.id >= TypeIds.T_JavaLangByte && this.originalValueIfTrueType.id <= TypeIds.T_JavaLangBoolean)) {
if (this.originalValueIfFalseType.isBaseType() || (this.originalValueIfFalseType.id >= TypeIds.T_JavaLangByte && this.originalValueIfFalseType.id <= TypeIds.T_JavaLangBoolean))
return false;
}
return this.isPolyExpression = true;
}
@Override
public boolean isCompatibleWith(TypeBinding left, Scope scope) {
return isPolyExpression() ? this.valueIfTrue.isCompatibleWith(left, scope) && this.valueIfFalse.isCompatibleWith(left, scope) :
super.isCompatibleWith(left, scope);
}
@Override
public boolean isBoxingCompatibleWith(TypeBinding targetType, Scope scope) {
return isPolyExpression() ? (this.valueIfTrue.isCompatibleWith(targetType, scope) ||
this.valueIfTrue.isBoxingCompatibleWith(targetType, scope)) &&
(this.valueIfFalse.isCompatibleWith(targetType, scope) ||
this.valueIfFalse.isBoxingCompatibleWith(targetType, scope)) :
super.isBoxingCompatibleWith(targetType, scope);
}
@Override
public boolean sIsMoreSpecific(TypeBinding s, TypeBinding t, Scope scope) {
if (super.sIsMoreSpecific(s, t, scope))
return true;
return isPolyExpression() ?
this.valueIfTrue.sIsMoreSpecific(s, t, scope) && this.valueIfFalse.sIsMoreSpecific(s, t, scope):
false;
}
@Override
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
this.condition.traverse(visitor, scope);
this.valueIfTrue.traverse(visitor, scope);
this.valueIfFalse.traverse(visitor, scope);
}
visitor.endVisit(this, scope);
}
}