package org.eclipse.jdt.internal.compiler.ast;
import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.*;
import java.util.HashMap;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.IrritantSet;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ImplicitNullAnnotationVerifier;
import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18;
import org.eclipse.jdt.internal.compiler.lookup.InferenceVariable;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolyParameterizedGenericMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolymorphicMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
public class MessageSend extends Expression implements IPolyExpression, Invocation {
public Expression receiver;
public char[] selector;
public Expression[] arguments;
public MethodBinding binding;
public MethodBinding syntheticAccessor;
public TypeBinding expectedType;
public long nameSourcePosition ;
public TypeBinding actualReceiverType;
public TypeBinding valueCast;
public TypeReference[] typeArguments;
public TypeBinding[] genericTypeArguments;
public ExpressionContext expressionContext = VANILLA_CONTEXT;
private SimpleLookupTable inferenceContexts;
private HashMap<TypeBinding, MethodBinding> solutionsPerTargetType;
private InferenceContext18 outerInferenceContext;
private boolean receiverIsType;
protected boolean argsContainCast;
public TypeBinding[] argumentTypes = Binding.NO_PARAMETERS;
public boolean argumentsHaveErrors = false;
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
boolean nonStatic = !this.binding.isStatic();
boolean wasInsideAssert = ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0);
flowInfo = this.receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits();
yieldQualifiedCheck(currentScope);
CompilerOptions compilerOptions = currentScope.compilerOptions();
boolean analyseResources = compilerOptions.analyseResourceLeaks;
if (analyseResources) {
if (nonStatic) {
if (CharOperation.equals(TypeConstants.CLOSE, this.selector)) {
recordCallingClose(currentScope, flowContext, flowInfo, this.receiver);
}
} else if (this.arguments != null && this.arguments.length > 0 && FakedTrackingVariable.isAnyCloseable(this.arguments[0].resolvedType)) {
for (int i=0; i<TypeConstants.closeMethods.length; i++) {
CloseMethodRecord record = TypeConstants.closeMethods[i];
if (CharOperation.equals(record.selector, this.selector)
&& CharOperation.equals(record.typeName, this.binding.declaringClass.compoundName))
{
int len = Math.min(record.numCloseableArgs, this.arguments.length);
for (int j=0; j<len; j++)
recordCallingClose(currentScope, flowContext, flowInfo, this.arguments[j]);
break;
}
}
}
}
if (compilerOptions.isAnyEnabled(IrritantSet.UNLIKELY_ARGUMENT_TYPE) && this.binding.isValidBinding()
&& this.arguments != null) {
if (this.arguments.length == 1 && !this.binding.isStatic()) {
UnlikelyArgumentCheck argumentChecks = UnlikelyArgumentCheck.determineCheckForNonStaticSingleArgumentMethod(
this.argumentTypes[0], currentScope, this.selector, this.actualReceiverType, this.binding.parameters);
if (argumentChecks != null && argumentChecks.isDangerous(currentScope)) {
currentScope.problemReporter().unlikelyArgumentType(this.arguments[0], this.binding,
this.argumentTypes[0], argumentChecks.typeToReport, argumentChecks.dangerousMethod);
}
} else if (this.arguments.length == 2 && this.binding.isStatic()) {
UnlikelyArgumentCheck argumentChecks = UnlikelyArgumentCheck.determineCheckForStaticTwoArgumentMethod(
this.argumentTypes[1], currentScope, this.selector, this.argumentTypes[0],
this.binding.parameters, this.actualReceiverType);
if (argumentChecks != null && argumentChecks.isDangerous(currentScope)) {
currentScope.problemReporter().unlikelyArgumentType(this.arguments[1], this.binding,
this.argumentTypes[1], argumentChecks.typeToReport, argumentChecks.dangerousMethod);
}
}
}
if (nonStatic) {
int timeToLive = ((this.bits & ASTNode.InsideExpressionStatement) != 0) ? 3 : 2;
this.receiver.checkNPE(currentScope, flowContext, flowInfo, timeToLive);
}
if (this.arguments != null) {
int length = this.arguments.length;
for (int i = 0; i < length; i++) {
Expression argument = this.arguments[i];
argument.checkNPEbyUnboxing(currentScope, flowContext, flowInfo);
switch (detectAssertionUtility(i)) {
case TRUE_ASSERTION:
flowInfo = analyseBooleanAssertion(currentScope, argument, flowContext, flowInfo, wasInsideAssert, true);
break;
case FALSE_ASSERTION:
flowInfo = analyseBooleanAssertion(currentScope, argument, flowContext, flowInfo, wasInsideAssert, false);
break;
case NONNULL_ASSERTION:
flowInfo = analyseNullAssertion(currentScope, argument, flowContext, flowInfo, false);
break;
case NULL_ASSERTION:
flowInfo = analyseNullAssertion(currentScope, argument, flowContext, flowInfo, true);
break;
default:
flowInfo = argument.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
}
if (analyseResources) {
flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, argument, flowInfo, flowContext, false);
}
}
analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments);
}
ReferenceBinding[] thrownExceptions;
if ((thrownExceptions = this.binding.thrownExceptions) != Binding.NO_EXCEPTIONS) {
if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
thrownExceptions = currentScope.environment().convertToRawTypes(this.binding.thrownExceptions, true, true);
}
flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo.copy(), currentScope);
}
manageSyntheticAccessIfNecessary(currentScope, flowInfo);
flowContext.recordAbruptExit();
flowContext.expireNullCheckedFieldInfo();
return flowInfo;
}
private void yieldQualifiedCheck(BlockScope currentScope) {
long sourceLevel = currentScope.compilerOptions().sourceLevel;
if (sourceLevel < ClassFileConstants.JDK13 || !this.receiverIsImplicitThis())
return;
if (this.selector == null || !("yield".equals(new String(this.selector))))
return;
if (sourceLevel == ClassFileConstants.JDK13 && currentScope.compilerOptions().enablePreviewFeatures) {
currentScope.problemReporter().switchExpressionsYieldUnqualifiedMethodError(this);
} else {
currentScope.problemReporter().switchExpressionsYieldUnqualifiedMethodWarning(this);
}
}
private void recordCallingClose(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Expression closeTarget) {
FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(closeTarget, flowInfo, flowContext);
if (trackingVariable != null) {
if (trackingVariable.methodScope == currentScope.methodScope()) {
trackingVariable.markClose(flowInfo, flowContext);
} else {
trackingVariable.markClosedInNestedMethod();
}
}
}
private static final int TRUE_ASSERTION = 1;
private static final int FALSE_ASSERTION = 2;
private static final int NULL_ASSERTION = 3;
private static final int NONNULL_ASSERTION = 4;
private int detectAssertionUtility(int argumentIdx) {
TypeBinding[] parameters = this.binding.original().parameters;
if (argumentIdx < parameters.length) {
TypeBinding parameterType = parameters[argumentIdx];
TypeBinding declaringClass = this.binding.declaringClass;
if (declaringClass != null && parameterType != null) {
switch (declaringClass.id) {
case TypeIds.T_OrgEclipseCoreRuntimeAssert:
if (parameterType.id == TypeIds.T_boolean)
return TRUE_ASSERTION;
if (parameterType.id == TypeIds.T_JavaLangObject && CharOperation.equals(TypeConstants.IS_NOTNULL, this.selector))
return NONNULL_ASSERTION;
break;
case TypeIds.T_JunitFrameworkAssert:
case TypeIds.T_OrgJunitAssert:
if (parameterType.id == TypeIds.T_boolean) {
if (CharOperation.equals(TypeConstants.ASSERT_TRUE, this.selector))
return TRUE_ASSERTION;
if (CharOperation.equals(TypeConstants.ASSERT_FALSE, this.selector))
return FALSE_ASSERTION;
} else if (parameterType.id == TypeIds.T_JavaLangObject) {
if (CharOperation.equals(TypeConstants.ASSERT_NOTNULL, this.selector))
return NONNULL_ASSERTION;
if (CharOperation.equals(TypeConstants.ASSERT_NULL, this.selector))
return NULL_ASSERTION;
}
break;
case TypeIds.T_OrgApacheCommonsLangValidate:
if (parameterType.id == TypeIds.T_boolean) {
if (CharOperation.equals(TypeConstants.IS_TRUE, this.selector))
return TRUE_ASSERTION;
} else if (parameterType.id == TypeIds.T_JavaLangObject) {
if (CharOperation.equals(TypeConstants.NOT_NULL, this.selector))
return NONNULL_ASSERTION;
}
break;
case TypeIds.T_OrgApacheCommonsLang3Validate:
if (parameterType.id == TypeIds.T_boolean) {
if (CharOperation.equals(TypeConstants.IS_TRUE, this.selector))
return TRUE_ASSERTION;
} else if (parameterType.isTypeVariable()) {
if (CharOperation.equals(TypeConstants.NOT_NULL, this.selector))
return NONNULL_ASSERTION;
}
break;
case TypeIds.T_ComGoogleCommonBasePreconditions:
if (parameterType.id == TypeIds.T_boolean) {
if (CharOperation.equals(TypeConstants.CHECK_ARGUMENT, this.selector)
|| CharOperation.equals(TypeConstants.CHECK_STATE, this.selector))
return TRUE_ASSERTION;
} else if (parameterType.isTypeVariable()) {
if (CharOperation.equals(TypeConstants.CHECK_NOT_NULL, this.selector))
return NONNULL_ASSERTION;
}
break;
case TypeIds.T_JavaUtilObjects:
if (parameterType.isTypeVariable()) {
if (CharOperation.equals(TypeConstants.REQUIRE_NON_NULL, this.selector))
return NONNULL_ASSERTION;
}
break;
}
}
}
return 0;
}
private FlowInfo analyseBooleanAssertion(BlockScope currentScope, Expression argument,
FlowContext flowContext, FlowInfo flowInfo, boolean wasInsideAssert, boolean passOnTrue)
{
Constant cst = argument.optimizedBooleanConstant();
boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true;
boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false;
int tagBitsSave = flowContext.tagBits;
flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING;
if (!passOnTrue)
flowContext.tagBits |= FlowContext.INSIDE_NEGATION;
FlowInfo conditionFlowInfo = argument.analyseCode(currentScope, flowContext, flowInfo.copy());
flowContext.extendTimeToLiveForNullCheckedField(2);
flowContext.tagBits = tagBitsSave;
UnconditionalFlowInfo assertWhenPassInfo;
FlowInfo assertWhenFailInfo;
boolean isOptimizedPassing;
boolean isOptimizedFailing;
if (passOnTrue) {
assertWhenPassInfo = conditionFlowInfo.initsWhenTrue().unconditionalInits();
assertWhenFailInfo = conditionFlowInfo.initsWhenFalse();
isOptimizedPassing = isOptimizedTrueAssertion;
isOptimizedFailing = isOptimizedFalseAssertion;
} else {
assertWhenPassInfo = conditionFlowInfo.initsWhenFalse().unconditionalInits();
assertWhenFailInfo = conditionFlowInfo.initsWhenTrue();
isOptimizedPassing = isOptimizedFalseAssertion;
isOptimizedFailing = isOptimizedTrueAssertion;
}
if (isOptimizedPassing) {
assertWhenFailInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
if (!isOptimizedFailing) {
flowInfo = flowInfo.mergedWith(assertWhenFailInfo.nullInfoLessUnconditionalCopy()).
addInitializationsFrom(assertWhenPassInfo.discardInitializationInfo());
}
return flowInfo;
}
private FlowInfo analyseNullAssertion(BlockScope currentScope, Expression argument,
FlowContext flowContext, FlowInfo flowInfo, boolean expectingNull)
{
int nullStatus = argument.nullStatus(flowInfo, flowContext);
boolean willFail = (nullStatus == (expectingNull ? FlowInfo.NON_NULL : FlowInfo.NULL));
flowInfo = argument.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
LocalVariableBinding local = argument.localVariableBinding();
if (local != null) {
if (expectingNull)
flowInfo.markAsDefinitelyNull(local);
else
flowInfo.markAsDefinitelyNonNull(local);
} else {
if (!expectingNull
&& argument instanceof Reference
&& currentScope.compilerOptions().enableSyntacticNullAnalysisForFields)
{
FieldBinding field = ((Reference)argument).lastFieldBinding();
if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
flowContext.recordNullCheckedFieldReference((Reference) argument, 3);
}
}
}
if (willFail)
flowInfo.setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
return flowInfo;
}
@Override
public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
int nullStatus = nullStatus(flowInfo, flowContext);
if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) {
if(this.binding.returnType.isTypeVariable() && nullStatus == FlowInfo.FREE_TYPEVARIABLE && scope.environment().globalOptions.pessimisticNullAnalysisForFreeTypeVariablesEnabled) {
scope.problemReporter().methodReturnTypeFreeTypeVariableReference(this.binding, this);
} else {
scope.problemReporter().messageSendPotentialNullReference(this.binding, this);
}
} else if ((this.resolvedType.tagBits & TagBits.AnnotationNonNull) != 0) {
NullAnnotationMatching nonNullStatus = NullAnnotationMatching.okNonNullStatus(this);
if (nonNullStatus.wantToReport())
nonNullStatus.report(scope);
}
return true;
}
@Override
public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
if (runtimeTimeType == null || compileTimeType == null)
return;
if (this.binding != null && this.binding.isValidBinding()) {
MethodBinding originalBinding = this.binding.original();
TypeBinding originalType = originalBinding.returnType;
if (ArrayBinding.isArrayClone(this.actualReceiverType, this.binding)
&& runtimeTimeType.id != TypeIds.T_JavaLangObject
&& scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
this.valueCast = runtimeTimeType;
} else if (originalType.leafComponentType().isTypeVariable()) {
TypeBinding targetType = (!compileTimeType.isBaseType() && runtimeTimeType.isBaseType())
? compileTimeType
: runtimeTimeType;
this.valueCast = originalType.genericCast(targetType);
}
if (this.valueCast instanceof ReferenceBinding) {
ReferenceBinding referenceCast = (ReferenceBinding) this.valueCast;
if (!referenceCast.canBeSeenBy(scope)) {
scope.problemReporter().invalidType(this,
new ProblemReferenceBinding(
CharOperation.splitOn('.', referenceCast.shortReadableName()),
referenceCast,
ProblemReasons.NotVisible));
}
}
}
super.computeConversion(scope, runtimeTimeType, compileTimeType);
}
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
cleanUpInferenceContexts();
int pc = codeStream.position;
MethodBinding codegenBinding = this.binding instanceof PolymorphicMethodBinding ? this.binding : this.binding.original();
boolean isStatic = codegenBinding.isStatic();
if (isStatic) {
this.receiver.generateCode(currentScope, codeStream, false);
} else if ((this.bits & ASTNode.DepthMASK) != 0 && this.receiver.isImplicitThis()) {
ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
Object[] path = currentScope.getEmulationPath(targetType, true , false);
codeStream.generateOuterAccess(path, this, targetType, currentScope);
} else {
this.receiver.generateCode(currentScope, codeStream, true);
if ((this.bits & NeedReceiverGenericCast) != 0) {
codeStream.checkcast(this.actualReceiverType);
}
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
generateArguments(this.binding, this.arguments, currentScope, codeStream);
pc = codeStream.position;
if (this.syntheticAccessor == null){
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
if (isStatic){
codeStream.invoke(Opcodes.OPC_invokestatic, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
} else if((this.receiver.isSuper()) ||
(!currentScope.enclosingSourceType().isNestmateOf(this.binding.declaringClass) && codegenBinding.isPrivate())){
codeStream.invoke(Opcodes.OPC_invokespecial, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
} else if (constantPoolDeclaringClass.isInterface()) {
codeStream.invoke(Opcodes.OPC_invokeinterface, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
} else {
codeStream.invoke(Opcodes.OPC_invokevirtual, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
}
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessor, null , this.typeArguments);
}
if (this.valueCast != null) codeStream.checkcast(this.valueCast);
if (valueRequired){
codeStream.generateImplicitConversion(this.implicitConversion);
} else {
boolean isUnboxing = (this.implicitConversion & TypeIds.UNBOXING) != 0;
if (isUnboxing) codeStream.generateImplicitConversion(this.implicitConversion);
switch (isUnboxing ? postConversionType(currentScope).id : codegenBinding.returnType.id) {
case T_long :
case T_double :
codeStream.pop2();
break;
case T_void :
break;
default :
codeStream.pop();
}
}
codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>> 32));
}
@Override
public TypeBinding[] genericTypeArguments() {
return this.genericTypeArguments;
}
@Override
public boolean isSuperAccess() {
return this.receiver.isSuper();
}
@Override
public boolean isTypeAccess() {
return this.receiver != null && this.receiver.isTypeReference();
}
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo){
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return;
MethodBinding codegenBinding = this.binding.original();
if (this.binding.isPrivate()){
boolean useNesting = currentScope.enclosingSourceType().isNestmateOf(codegenBinding.declaringClass) &&
!(this.receiver instanceof QualifiedSuperReference);
if (!useNesting &&
TypeBinding.notEquals(currentScope.enclosingSourceType(), codegenBinding.declaringClass)){
this.syntheticAccessor = ((SourceTypeBinding)codegenBinding.declaringClass).addSyntheticMethod(codegenBinding, false );
currentScope.problemReporter().needToEmulateMethodAccess(codegenBinding, this);
return;
}
} else if (this.receiver instanceof QualifiedSuperReference) {
if (this.actualReceiverType.isInterface())
return;
SourceTypeBinding destinationType = (SourceTypeBinding)(((QualifiedSuperReference)this.receiver).currentCompatibleType);
this.syntheticAccessor = destinationType.addSyntheticMethod(codegenBinding, isSuperAccess());
currentScope.problemReporter().needToEmulateMethodAccess(codegenBinding, this);
return;
} else if (this.binding.isProtected()){
SourceTypeBinding enclosingSourceType;
if (((this.bits & ASTNode.DepthMASK) != 0)
&& codegenBinding.declaringClass.getPackage()
!= (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()){
SourceTypeBinding currentCompatibleType = (SourceTypeBinding)enclosingSourceType.enclosingTypeAt((this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
this.syntheticAccessor = currentCompatibleType.addSyntheticMethod(codegenBinding, isSuperAccess());
currentScope.problemReporter().needToEmulateMethodAccess(codegenBinding, this);
return;
}
}
}
@Override
public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
if ((this.implicitConversion & TypeIds.BOXING) != 0)
return FlowInfo.NON_NULL;
if (this.binding.isValidBinding()) {
long tagBits = this.binding.tagBits;
if ((tagBits & TagBits.AnnotationNullMASK) == 0L)
tagBits = this.binding.returnType.tagBits & TagBits.AnnotationNullMASK;
if(tagBits == 0L && this.binding.returnType.isFreeTypeVariable()) {
return FlowInfo.FREE_TYPEVARIABLE;
}
return FlowInfo.tagBitsToNullStatus(tagBits);
}
return FlowInfo.UNKNOWN;
}
@Override
public TypeBinding postConversionType(Scope scope) {
TypeBinding convertedType = this.resolvedType;
if (this.valueCast != null)
convertedType = this.valueCast;
int runtimeType = (this.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
switch (runtimeType) {
case T_boolean :
convertedType = TypeBinding.BOOLEAN;
break;
case T_byte :
convertedType = TypeBinding.BYTE;
break;
case T_short :
convertedType = TypeBinding.SHORT;
break;
case T_char :
convertedType = TypeBinding.CHAR;
break;
case T_int :
convertedType = TypeBinding.INT;
break;
case T_float :
convertedType = TypeBinding.FLOAT;
break;
case T_long :
convertedType = TypeBinding.LONG;
break;
case T_double :
convertedType = TypeBinding.DOUBLE;
break;
default :
}
if ((this.implicitConversion & TypeIds.BOXING) != 0) {
convertedType = scope.environment().computeBoxingType(convertedType);
}
return convertedType;
}
@Override
public StringBuffer printExpression(int indent, StringBuffer output){
if (!this.receiver.isImplicitThis()) this.receiver.printExpression(0, output).append('.');
if (this.typeArguments != null) {
output.append('<');
int max = this.typeArguments.length - 1;
for (int j = 0; j < max; j++) {
this.typeArguments[j].print(0, output);
output.append(", ");
}
this.typeArguments[max].print(0, output);
output.append('>');
}
output.append(this.selector).append('(') ;
if (this.arguments != null) {
for (int i = 0; i < this.arguments.length ; i ++) {
if (i > 0) output.append(", ");
this.arguments[i].printExpression(0, output);
}
}
return output.append(')');
}
@Override
public TypeBinding resolveType(BlockScope scope) {
if (this.constant != Constant.NotAConstant) {
this.constant = Constant.NotAConstant;
long sourceLevel = scope.compilerOptions().sourceLevel;
boolean receiverCast = false;
if (this.receiver instanceof CastExpression) {
this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck;
receiverCast = true;
}
this.actualReceiverType = this.receiver.resolveType(scope);
if (this.actualReceiverType instanceof InferenceVariable) {
return null;
}
this.receiverIsType = this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0;
if (receiverCast && this.actualReceiverType != null) {
TypeBinding resolvedType2 = ((CastExpression)this.receiver).expression.resolvedType;
if (TypeBinding.equalsEquals(resolvedType2, this.actualReceiverType)) {
if (!scope.environment().usesNullTypeAnnotations() || !NullAnnotationMatching.analyse(this.actualReceiverType, resolvedType2, -1).isAnyMismatch()) {
scope.problemReporter().unnecessaryCast((CastExpression) this.receiver);
}
}
}
if (this.typeArguments != null) {
int length = this.typeArguments.length;
this.argumentsHaveErrors = sourceLevel < ClassFileConstants.JDK1_5;
this.genericTypeArguments = new TypeBinding[length];
for (int i = 0; i < length; i++) {
TypeReference typeReference = this.typeArguments[i];
if ((this.genericTypeArguments[i] = typeReference.resolveType(scope, true , Binding.DefaultLocationTypeArgument)) == null) {
this.argumentsHaveErrors = true;
}
if (this.argumentsHaveErrors && typeReference instanceof Wildcard) {
scope.problemReporter().illegalUsageOfWildcard(typeReference);
}
}
if (this.argumentsHaveErrors) {
if (this.arguments != null) {
for (int i = 0, max = this.arguments.length; i < max; i++) {
this.arguments[i].resolveType(scope);
}
}
return null;
}
}
if (this.arguments != null) {
this.argumentsHaveErrors = false;
int length = this.arguments.length;
this.argumentTypes = new TypeBinding[length];
for (int i = 0; i < length; i++){
Expression argument = this.arguments[i];
if (this.arguments[i].resolvedType != null)
scope.problemReporter().genericInferenceError("Argument was unexpectedly found resolved", this);
if (argument instanceof CastExpression) {
argument.bits |= ASTNode.DisableUnnecessaryCastCheck;
this.argsContainCast = true;
}
argument.setExpressionContext(INVOCATION_CONTEXT);
if ((this.argumentTypes[i] = argument.resolveType(scope)) == null){
this.argumentsHaveErrors = true;
}
}
if (this.argumentsHaveErrors) {
if (this.actualReceiverType instanceof ReferenceBinding) {
TypeBinding[] pseudoArgs = new TypeBinding[length];
for (int i = length; --i >= 0;)
pseudoArgs[i] = this.argumentTypes[i] == null ? TypeBinding.NULL : this.argumentTypes[i];
this.binding = this.receiver.isImplicitThis() ?
scope.getImplicitMethod(this.selector, pseudoArgs, this) :
scope.findMethod((ReferenceBinding) this.actualReceiverType, this.selector, pseudoArgs, this, false);
if (this.binding != null && !this.binding.isValidBinding()) {
MethodBinding closestMatch = ((ProblemMethodBinding)this.binding).closestMatch;
if (closestMatch != null) {
if (closestMatch.original().typeVariables != Binding.NO_TYPE_VARIABLES) {
closestMatch = scope.environment().createParameterizedGenericMethod(closestMatch.original(), (RawTypeBinding)null);
}
this.binding = closestMatch;
MethodBinding closestMatchOriginal = closestMatch.original();
if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) {
closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
}
}
}
}
return null;
}
}
if (this.actualReceiverType == null) {
return null;
}
if (this.actualReceiverType.isBaseType()) {
scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, this.argumentTypes);
return null;
}
}
TypeBinding methodType = findMethodBinding(scope);
if (methodType != null && methodType.isPolyType()) {
this.resolvedType = this.binding.returnType.capture(scope, this.sourceStart, this.sourceEnd);
return methodType;
}
if (!this.binding.isValidBinding()) {
if (this.binding.declaringClass == null) {
if (this.actualReceiverType instanceof ReferenceBinding) {
this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType;
} else {
scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, this.argumentTypes);
return null;
}
}
ReferenceBinding declaringClass = this.binding.declaringClass;
boolean avoidSecondary = declaringClass != null &&
declaringClass.isAnonymousType() &&
declaringClass.superclass() instanceof MissingTypeBinding;
if (!avoidSecondary)
scope.problemReporter().invalidMethod(this, this.binding, scope);
MethodBinding closestMatch = ((ProblemMethodBinding)this.binding).closestMatch;
switch (this.binding.problemId()) {
case ProblemReasons.Ambiguous :
break;
case ProblemReasons.InferredApplicableMethodInapplicable:
case ProblemReasons.InvocationTypeInferenceFailure:
if (this.expressionContext != INVOCATION_CONTEXT)
break;
case ProblemReasons.NotVisible :
case ProblemReasons.NonStaticReferenceInConstructorInvocation :
case ProblemReasons.NonStaticReferenceInStaticContext :
case ProblemReasons.ReceiverTypeNotVisible :
case ProblemReasons.ParameterBoundMismatch :
if (closestMatch != null) this.resolvedType = closestMatch.returnType;
break;
case ProblemReasons.ContradictoryNullAnnotations :
if (closestMatch != null && closestMatch.returnType != null)
this.resolvedType = closestMatch.returnType.withoutToplevelNullAnnotation();
break;
}
if (closestMatch != null) {
this.binding = closestMatch;
MethodBinding closestMatchOriginal = closestMatch.original();
if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) {
closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
}
}
return (this.resolvedType != null && (this.resolvedType.tagBits & TagBits.HasMissingType) == 0)
? this.resolvedType
: null;
}
final CompilerOptions compilerOptions = scope.compilerOptions();
if (compilerOptions.complianceLevel <= ClassFileConstants.JDK1_6
&& this.binding.isPolymorphic()) {
scope.problemReporter().polymorphicMethodNotBelow17(this);
return null;
}
if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) {
ImplicitNullAnnotationVerifier.ensureNullnessIsKnown(this.binding, scope);
if (compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8) {
if (this.binding instanceof ParameterizedGenericMethodBinding && this.typeArguments != null) {
TypeVariableBinding[] typeVariables = this.binding.original().typeVariables();
for (int i = 0; i < this.typeArguments.length; i++)
this.typeArguments[i].checkNullConstraints(scope, (ParameterizedGenericMethodBinding) this.binding, typeVariables, i);
}
}
}
if (((this.bits & ASTNode.InsideExpressionStatement) != 0)
&& this.binding.isPolymorphic()) {
this.binding = scope.environment().updatePolymorphicMethodReturnType((PolymorphicMethodBinding) this.binding, TypeBinding.VOID);
}
if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
scope.problemReporter().missingTypeInMethod(this, this.binding);
}
if (!this.binding.isStatic()) {
if (this.receiverIsType) {
scope.problemReporter().mustUseAStaticMethod(this, this.binding);
if (this.actualReceiverType.isRawType()
&& (this.receiver.bits & ASTNode.IgnoreRawTypeCheck) == 0
&& compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) {
scope.problemReporter().rawTypeReference(this.receiver, this.actualReceiverType);
}
} else {
TypeBinding oldReceiverType = this.actualReceiverType;
this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(this.binding.declaringClass);
this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType);
if (TypeBinding.notEquals(this.actualReceiverType, oldReceiverType) && TypeBinding.notEquals(this.receiver.postConversionType(scope), this.actualReceiverType)) {
this.bits |= NeedReceiverGenericCast;
}
}
} else {
if (this.binding.declaringClass.isInterface() && !((isTypeAccess() || this.receiver.isImplicitThis()) && TypeBinding.equalsEquals(this.binding.declaringClass, this.actualReceiverType))) {
scope.problemReporter().nonStaticOrAlienTypeReceiver(this, this.binding);
} else if (!(this.receiver.isImplicitThis() || this.receiver.isSuper() || this.receiverIsType)) {
scope.problemReporter().nonStaticAccessToStaticMethod(this, this.binding);
}
if (!this.receiver.isImplicitThis() && TypeBinding.notEquals(this.binding.declaringClass, this.actualReceiverType)) {
scope.problemReporter().indirectAccessToStaticMethod(this, this.binding);
}
}
if (checkInvocationArguments(scope, this.receiver, this.actualReceiverType, this.binding, this.arguments, this.argumentTypes, this.argsContainCast, this)) {
this.bits |= ASTNode.Unchecked;
}
if (this.binding.isAbstract()) {
if (this.receiver.isSuper()) {
scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, this.binding);
}
}
if (isMethodUseDeprecated(this.binding, scope, true, this))
scope.problemReporter().deprecatedMethod(this.binding, this);
TypeBinding returnType;
if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
returnType = this.binding.returnType;
if (returnType != null) {
returnType = scope.environment().convertToRawType(returnType.erasure(), true);
}
} else {
returnType = this.binding.returnType;
if (returnType != null) {
returnType = returnType.capture(scope, this.sourceStart, this.sourceEnd);
}
}
this.resolvedType = returnType;
if (this.receiver.isSuper() && compilerOptions.getSeverity(CompilerOptions.OverridingMethodWithoutSuperInvocation) != ProblemSeverities.Ignore) {
final ReferenceContext referenceContext = scope.methodScope().referenceContext;
if (referenceContext instanceof AbstractMethodDeclaration) {
final AbstractMethodDeclaration abstractMethodDeclaration = (AbstractMethodDeclaration) referenceContext;
MethodBinding enclosingMethodBinding = abstractMethodDeclaration.binding;
if (enclosingMethodBinding.isOverriding()
&& CharOperation.equals(this.binding.selector, enclosingMethodBinding.selector)
&& this.binding.areParametersEqual(enclosingMethodBinding)) {
abstractMethodDeclaration.bits |= ASTNode.OverridingMethodWithSupercall;
}
}
}
if (this.receiver.isSuper() && this.actualReceiverType.isInterface()) {
scope.checkAppropriateMethodAgainstSupers(this.selector, this.binding, this.argumentTypes, this);
}
if (this.typeArguments != null && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) {
scope.problemReporter().unnecessaryTypeArgumentsForMethodInvocation(this.binding, this.genericTypeArguments, this.typeArguments);
}
return (this.resolvedType.tagBits & TagBits.HasMissingType) == 0
? this.resolvedType
: null;
}
protected TypeBinding findMethodBinding(BlockScope scope) {
ReferenceContext referenceContext = scope.methodScope().referenceContext;
if (referenceContext instanceof LambdaExpression) {
this.outerInferenceContext = ((LambdaExpression) referenceContext).inferenceContext;
}
if (this.expectedType != null && this.binding instanceof PolyParameterizedGenericMethodBinding) {
this.binding = this.solutionsPerTargetType.get(this.expectedType);
}
if (this.binding == null) {
this.binding = this.receiver.isImplicitThis() ?
scope.getImplicitMethod(this.selector, this.argumentTypes, this)
: scope.getMethod(this.actualReceiverType, this.selector, this.argumentTypes, this);
if (this.binding instanceof PolyParameterizedGenericMethodBinding) {
this.solutionsPerTargetType = new HashMap<TypeBinding, MethodBinding>();
return new PolyTypeBinding(this);
}
}
this.binding = resolvePolyExpressionArguments(this, this.binding, this.argumentTypes, scope);
return this.binding.returnType;
}
@Override
public void setActualReceiverType(ReferenceBinding receiverType) {
if (receiverType == null) return;
this.actualReceiverType = receiverType;
}
@Override
public void setDepth(int depth) {
this.bits &= ~ASTNode.DepthMASK;
if (depth > 0) {
this.bits |= (depth & 0xFF) << ASTNode.DepthSHIFT;
}
}
@Override
public void setExpectedType(TypeBinding expectedType) {
this.expectedType = expectedType;
}
@Override
public void setExpressionContext(ExpressionContext context) {
this.expressionContext = context;
}
@Override
public boolean isPolyExpression() {
return isPolyExpression(this.binding);
}
@Override
public boolean isBoxingCompatibleWith(TypeBinding targetType, Scope scope) {
if (this.argumentsHaveErrors || this.binding == null || !this.binding.isValidBinding() || targetType == null || scope == null)
return false;
if (isPolyExpression() && !targetType.isPrimitiveOrBoxedPrimitiveType())
return false;
TypeBinding originalExpectedType = this.expectedType;
try {
MethodBinding method = this.solutionsPerTargetType != null ? this.solutionsPerTargetType.get(targetType) : null;
if (method == null) {
this.expectedType = targetType;
method = isPolyExpression() ? ParameterizedGenericMethodBinding.computeCompatibleMethod18(this.binding.shallowOriginal(), this.argumentTypes, scope, this) : this.binding;
registerResult(targetType, method);
}
if (method == null || !method.isValidBinding() || method.returnType == null || !method.returnType.isValidBinding())
return false;
return super.isBoxingCompatible(method.returnType.capture(scope, this.sourceStart, this.sourceEnd), targetType, this, scope);
} finally {
this.expectedType = originalExpectedType;
}
}
@Override
public boolean isCompatibleWith(TypeBinding targetType, final Scope scope) {
if (this.argumentsHaveErrors || this.binding == null || !this.binding.isValidBinding() || targetType == null || scope == null)
return false;
TypeBinding originalExpectedType = this.expectedType;
try {
MethodBinding method = this.solutionsPerTargetType != null ? this.solutionsPerTargetType.get(targetType) : null;
if (method == null) {
this.expectedType = targetType;
method = isPolyExpression() ? ParameterizedGenericMethodBinding.computeCompatibleMethod18(this.binding.shallowOriginal(), this.argumentTypes, scope, this) : this.binding;
registerResult(targetType, method);
}
TypeBinding returnType;
if (method == null || !method.isValidBinding() || (returnType = method.returnType) == null || !returnType.isValidBinding())
return false;
if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null)
returnType = scope.environment().convertToRawType(returnType.erasure(), true);
return returnType.capture(scope, this.sourceStart, this.sourceEnd).isCompatibleWith(targetType, scope);
} finally {
this.expectedType = originalExpectedType;
}
}
@Override
public boolean isPolyExpression(MethodBinding resolutionCandidate) {
if (this.expressionContext != ASSIGNMENT_CONTEXT && this.expressionContext != INVOCATION_CONTEXT)
return false;
if (this.typeArguments != null && this.typeArguments.length > 0)
return false;
if (this.constant != Constant.NotAConstant)
throw new UnsupportedOperationException("Unresolved MessageSend can't be queried if it is a polyexpression");
if (resolutionCandidate != null) {
if (resolutionCandidate instanceof ParameterizedGenericMethodBinding) {
ParameterizedGenericMethodBinding pgmb = (ParameterizedGenericMethodBinding) resolutionCandidate;
if (pgmb.inferredReturnType)
return true;
}
if (resolutionCandidate.returnType != null) {
MethodBinding candidateOriginal = resolutionCandidate.original();
return candidateOriginal.returnType.mentionsAny(candidateOriginal.typeVariables(), -1);
}
}
return false;
}
@Override
public boolean sIsMoreSpecific(TypeBinding s, TypeBinding t, Scope scope) {
if (super.sIsMoreSpecific(s, t, scope))
return true;
return isPolyExpression() ? !s.isBaseType() && t.isBaseType() : false;
}
@Override
public void setFieldIndex(int depth) {
}
@Override
public TypeBinding invocationTargetType() {
return this.expectedType;
}
@Override
public void traverse(ASTVisitor visitor, BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
this.receiver.traverse(visitor, blockScope);
if (this.typeArguments != null) {
for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) {
this.typeArguments[i].traverse(visitor, blockScope);
}
}
if (this.arguments != null) {
int argumentsLength = this.arguments.length;
for (int i = 0; i < argumentsLength; i++)
this.arguments[i].traverse(visitor, blockScope);
}
}
visitor.endVisit(this, blockScope);
}
@Override
public boolean statementExpression() {
return ((this.bits & ASTNode.ParenthesizedMASK) == 0);
}
@Override
public boolean receiverIsImplicitThis() {
return this.receiver.isImplicitThis();
}
@Override
public MethodBinding binding() {
return this.binding;
}
@Override
public void registerInferenceContext(ParameterizedGenericMethodBinding method, InferenceContext18 infCtx18) {
if (this.inferenceContexts == null)
this.inferenceContexts = new SimpleLookupTable();
this.inferenceContexts.put(method, infCtx18);
}
@Override
public void registerResult(TypeBinding targetType, MethodBinding method) {
if (this.solutionsPerTargetType == null)
this.solutionsPerTargetType = new HashMap<TypeBinding, MethodBinding>();
this.solutionsPerTargetType.put(targetType, method);
}
@Override
public InferenceContext18 getInferenceContext(ParameterizedMethodBinding method) {
if (this.inferenceContexts == null)
return null;
return (InferenceContext18) this.inferenceContexts.get(method);
}
@Override
public void cleanUpInferenceContexts() {
if (this.inferenceContexts == null)
return;
for (Object value : this.inferenceContexts.valueTable)
if (value != null)
((InferenceContext18) value).cleanUp();
this.inferenceContexts = null;
this.outerInferenceContext = null;
this.solutionsPerTargetType = null;
}
@Override
public Expression[] arguments() {
return this.arguments;
}
@Override
public ExpressionContext getExpressionContext() {
return this.expressionContext;
}
@Override
public InferenceContext18 freshInferenceContext(Scope scope) {
return new InferenceContext18(scope, this.arguments, this, this.outerInferenceContext);
}
@Override
public boolean isQualifiedSuper() {
return this.receiver.isQualifiedSuper();
}
@Override
public int nameSourceStart() {
return (int) (this.nameSourcePosition >>> 32);
}
@Override
public int nameSourceEnd() {
return (int) this.nameSourcePosition;
}
}