import eclipse 3.1 M4 compiler
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / MessageSend.java
index a9a2e9d..05b78da 100644 (file)
@@ -11,6 +11,7 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.compiler.ast;
 
+import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.ASTVisitor;
 import org.eclipse.jdt.internal.compiler.flow.*;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -18,20 +19,28 @@ import org.eclipse.jdt.internal.compiler.codegen.*;
 import org.eclipse.jdt.internal.compiler.lookup.*;
 
 public class MessageSend extends Expression implements InvocationSite {
+    
        public Expression receiver ;
        public char[] selector ;
        public Expression[] arguments ;
-       public MethodBinding binding, codegenBinding;
+       public MethodBinding binding;                                                   // exact binding resulting from lookup
+       protected MethodBinding codegenBinding;         // actual binding used for code generation (if no synthetic accessor)
+       MethodBinding syntheticAccessor;                                                // synthetic accessor for inner-emulation
+       public TypeBinding expectedType;                                        // for generic method invocation (return type inference)
 
        public long nameSourcePosition ; //(start<<32)+end
 
-       MethodBinding syntheticAccessor;
-
-       public TypeBinding receiverType, qualifyingType;
+       public TypeBinding actualReceiverType;
+       public TypeBinding valueCast; // extra reference type cast to perform on method returned value
+       public TypeReference[] typeArguments;
+       public TypeBinding[] genericTypeArguments;
        
 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
 
-       flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic()).unconditionalInits();
+       boolean nonStatic = !binding.isStatic();
+       flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits();
+       if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
+
        if (arguments != null) {
                int length = arguments.length;
                for (int i = 0; i < length; i++) {
@@ -46,7 +55,31 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
        manageSyntheticAccessIfNecessary(currentScope, flowInfo);       
        return flowInfo;
 }
-
+/**
+ * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
+ */
+public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
+       if (runtimeTimeType == null || compileTimeType == null)
+               return;
+       // set the generic cast after the fact, once the type expectation is fully known (no need for strict cast)
+       if (this.binding != null && this.binding.isValidBinding()) {
+               MethodBinding originalBinding = this.binding.original();
+               if (originalBinding != this.binding) {
+                   // extra cast needed if method return type has type variable
+                   if ((originalBinding.returnType.tagBits & TagBits.HasTypeVariable) != 0 && runtimeTimeType.id != T_JavaLangObject) {
+                       this.valueCast = originalBinding.returnType.genericCast(scope.boxing(runtimeTimeType)); // runtimeType could be base type in boxing case
+                   }
+               }       else if (this.actualReceiverType.isArrayType() 
+                                               && runtimeTimeType.id != T_JavaLangObject
+                                               && this.binding.parameters == NoParameters 
+                                               && scope.environment().options.complianceLevel >= JDK1_5 
+                                               && CharOperation.equals(this.binding.selector, CLONE)) {
+                                       // from 1.5 compliant mode on, array#clone() resolves to array type, but codegen to #clone()Object - thus require extra inserted cast
+                       this.valueCast = runtimeTimeType;                       
+               }
+       }
+       super.computeConversion(scope, runtimeTimeType, compileTimeType);
+}
 /**
  * MessageSend code generation
  *
@@ -59,7 +92,7 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean
        int pc = codeStream.position;
 
        // generate receiver/enclosing instance access
-       boolean isStatic = codegenBinding.isStatic();
+       boolean isStatic = this.codegenBinding.isStatic();
        // outer access ?
        if (!isStatic && ((bits & DepthMASK) != 0) && receiver.isImplicitThis()){
                // outer method can be reached through emulation if implicit access
@@ -70,23 +103,19 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean
                receiver.generateCode(currentScope, codeStream, !isStatic);
        }
        // generate arguments
-       if (arguments != null){
-               for (int i = 0, max = arguments.length; i < max; i++){
-                       arguments[i].generateCode(currentScope, codeStream, true);
-               }
-       }
+       generateArguments(binding, arguments, currentScope, codeStream);
        // actual message invocation
        if (syntheticAccessor == null){
                if (isStatic){
-                       codeStream.invokestatic(codegenBinding);
+                       codeStream.invokestatic(this.codegenBinding);
                } else {
-                       if( (receiver.isSuper()) || codegenBinding.isPrivate()){
-                               codeStream.invokespecial(codegenBinding);
+                       if( (receiver.isSuper()) || this.codegenBinding.isPrivate()){
+                               codeStream.invokespecial(this.codegenBinding);
                        } else {
-                               if (codegenBinding.declaringClass.isInterface()){
-                                       codeStream.invokeinterface(codegenBinding);
+                               if ((this.codegenBinding.declaringClass.modifiers & AccInterface) != 0) { // interface or annotation type
+                                       codeStream.invokeinterface(this.codegenBinding);
                                } else {
-                                       codeStream.invokevirtual(codegenBinding);
+                                       codeStream.invokevirtual(this.codegenBinding);
                                }
                        }
                }
@@ -96,6 +125,8 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean
        // operation on the returned value
        if (valueRequired){
                // implicit conversion if necessary
+               if (this.valueCast != null) 
+                       codeStream.checkcast(this.valueCast);
                codeStream.generateImplicitConversion(implicitConversion);
        } else {
                // pop return value if any
@@ -112,6 +143,12 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean
        }
        codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>> 32)); // highlight selector
 }
+/**
+ * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
+ */
+public TypeBinding[] genericTypeArguments() {
+       return this.genericTypeArguments;
+}
 public boolean isSuperAccess() {       
        return receiver.isSuper();
 }
@@ -121,13 +158,16 @@ public boolean isTypeAccess() {
 public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo){
 
        if (!flowInfo.isReachable()) return;
-       if (binding.isPrivate()){
+
+       // if method from parameterized type got found, use the original method at codegen time
+       this.codegenBinding = this.binding.original();
+       if (this.binding.isPrivate()){
 
                // depth is set for both implicit and explicit access (see MethodBinding#canBeSeenBy)           
-               if (currentScope.enclosingSourceType() != binding.declaringClass){
+               if (currentScope.enclosingSourceType() != this.codegenBinding.declaringClass){
                
-                       syntheticAccessor = ((SourceTypeBinding)binding.declaringClass).addSyntheticMethod(binding, isSuperAccess());
-                       currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
+                       syntheticAccessor = ((SourceTypeBinding)this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isSuperAccess());
+                       currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
                        return;
                }
 
@@ -135,44 +175,60 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo f
 
                // qualified super need emulation always
                SourceTypeBinding destinationType = (SourceTypeBinding)(((QualifiedSuperReference)receiver).currentCompatibleType);
-               syntheticAccessor = destinationType.addSyntheticMethod(binding, isSuperAccess());
-               currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
+               syntheticAccessor = destinationType.addSyntheticMethod(this.codegenBinding, isSuperAccess());
+               currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
                return;
 
        } else if (binding.isProtected()){
 
                SourceTypeBinding enclosingSourceType;
                if (((bits & DepthMASK) != 0) 
-                               && binding.declaringClass.getPackage() 
+                               && this.codegenBinding.declaringClass.getPackage() 
                                        != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()){
 
                        SourceTypeBinding currentCompatibleType = (SourceTypeBinding)enclosingSourceType.enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
-                       syntheticAccessor = currentCompatibleType.addSyntheticMethod(binding, isSuperAccess());
-                       currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
+                       syntheticAccessor = currentCompatibleType.addSyntheticMethod(this.codegenBinding, isSuperAccess());
+                       currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
                        return;
                }
        }
+       
        // if the binding declaring class is not visible, need special action
        // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
        // NOTE: from target 1.2 on, method's declaring class is touched if any different from receiver type
        // and not from Object or implicit static method call.  
-       if (binding.declaringClass != this.qualifyingType
-               && !this.qualifyingType.isArrayType()
+       if (this.binding.declaringClass != this.actualReceiverType
+               && !this.actualReceiverType.isArrayType()
                && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2
-                               && (!receiver.isImplicitThis() || !binding.isStatic())
-                               && binding.declaringClass.id != T_Object) // no change for Object methods
-                       || !binding.declaringClass.canBeSeenBy(currentScope))) {
+                               && (!receiver.isImplicitThis() || !this.codegenBinding.isStatic())
+                               && this.binding.declaringClass.id != T_JavaLangObject) // no change for Object methods
+                       || !this.binding.declaringClass.canBeSeenBy(currentScope))) {
 
-               this.codegenBinding = currentScope.enclosingSourceType().getUpdatedMethodBinding(binding, (ReferenceBinding) this.qualifyingType);
+               this.codegenBinding = currentScope.enclosingSourceType().getUpdatedMethodBinding(
+                                                                                                       this.codegenBinding, (ReferenceBinding) this.actualReceiverType.erasure());
 
                // Post 1.4.0 target, array clone() invocations are qualified with array type 
                // This is handled in array type #clone method binding resolution (see Scope and UpdatedMethodBinding)
        }
 }
 
+public int nullStatus(FlowInfo flowInfo) {
+       return FlowInfo.UNKNOWN;
+}
+       
 public StringBuffer printExpression(int indent, StringBuffer output){
        
        if (!receiver.isImplicitThis()) receiver.printExpression(0, output).append('.');
+       if (this.typeArguments != null) {
+               output.append('<');//$NON-NLS-1$
+               int max = typeArguments.length - 1;
+               for (int j = 0; j < max; j++) {
+                       typeArguments[j].print(0, output);
+                       output.append(", ");//$NON-NLS-1$
+               }
+               typeArguments[max].print(0, output);
+               output.append('>');
+       }
        output.append(selector).append('(') ; //$NON-NLS-1$
        if (arguments != null) {
                for (int i = 0; i < arguments.length ; i ++) {  
@@ -188,18 +244,32 @@ public TypeBinding resolveType(BlockScope scope) {
        // Base type promotion
 
        constant = NotAConstant;
-       boolean receiverCast = false, argumentsCast = false; 
+       boolean receiverCast = false, argsContainCast = false; 
        if (this.receiver instanceof CastExpression) {
                this.receiver.bits |= IgnoreNeedForCastCheckMASK; // will check later on
                receiverCast = true;
        }
-       this.qualifyingType = this.receiverType = receiver.resolveType(scope); 
-       if (receiverCast && this.receiverType != null) {
+       this.actualReceiverType = receiver.resolveType(scope); 
+       if (receiverCast && this.actualReceiverType != null) {
                 // due to change of declaring class with receiver type, only identity cast should be notified
-               if (((CastExpression)this.receiver).expression.resolvedType == this.receiverType) { 
-                                       scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);         
+               if (((CastExpression)this.receiver).expression.resolvedType == this.actualReceiverType) { 
+                       scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);         
                }
        }
+       // resolve type arguments (for generic constructor call)
+       if (this.typeArguments != null) {
+               int length = this.typeArguments.length;
+               boolean argHasError = false; // typeChecks all arguments
+               this.genericTypeArguments = new TypeBinding[length];
+               for (int i = 0; i < length; i++) {
+                       if ((this.genericTypeArguments[i] = this.typeArguments[i].resolveType(scope, true /* check bounds*/)) == null) {
+                               argHasError = true;
+                       }
+               }
+               if (argHasError) {
+                       return null;
+               }
+       }       
        // will check for null after args are resolved
        TypeBinding[] argumentTypes = NoParameters;
        if (arguments != null) {
@@ -210,75 +280,92 @@ public TypeBinding resolveType(BlockScope scope) {
                        Expression argument = arguments[i];
                        if (argument instanceof CastExpression) {
                                argument.bits |= IgnoreNeedForCastCheckMASK; // will check later on
-                               argumentsCast = true;
+                               argsContainCast = true;
                        }
                        if ((argumentTypes[i] = argument.resolveType(scope)) == null){
                                argHasError = true;
                        }
                }
                if (argHasError) {
-                       if(receiverType instanceof ReferenceBinding) {
+                       if(actualReceiverType instanceof ReferenceBinding) {
                                // record any selector match, for clients who may still need hint about possible method match
-                               this.codegenBinding = this.binding = scope.findMethod((ReferenceBinding)receiverType, selector, new TypeBinding[]{}, this);
+                               this.binding = scope.findMethod((ReferenceBinding)actualReceiverType, selector, new TypeBinding[]{}, this);
                        }                       
                        return null;
                }
        }
-       if (this.receiverType == null)
+       if (this.actualReceiverType == null) {
                return null;
-
+       }
        // base type cannot receive any message
-       if (this.receiverType.isBaseType()) {
-               scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
+       if (this.actualReceiverType.isBaseType()) {
+               scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
                return null;
        }
-       this.codegenBinding = this.binding = 
+       this.binding = 
                receiver.isImplicitThis()
                        ? scope.getImplicitMethod(selector, argumentTypes, this)
-                       : scope.getMethod(this.receiverType, selector, argumentTypes, this); 
+                       : scope.getMethod(this.actualReceiverType, selector, argumentTypes, this); 
        if (!binding.isValidBinding()) {
                if (binding.declaringClass == null) {
-                       if (this.receiverType instanceof ReferenceBinding) {
-                               binding.declaringClass = (ReferenceBinding) this.receiverType;
+                       if (this.actualReceiverType instanceof ReferenceBinding) {
+                               binding.declaringClass = (ReferenceBinding) this.actualReceiverType;
                        } else { 
-                               scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
+                               scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
                                return null;
                        }
                }
                scope.problemReporter().invalidMethod(this, binding);
+               MethodBinding closestMatch = ((ProblemMethodBinding)binding).closestMatch;
+               switch (this.binding.problemId()) {
+                       case ProblemReasons.Ambiguous :
+                       case ProblemReasons.NotVisible :
+                       case ProblemReasons.NonStaticReferenceInConstructorInvocation :
+                       case ProblemReasons.NonStaticReferenceInStaticContext :
+                       case ProblemReasons.ReceiverTypeNotVisible :
+                       case ProblemReasons.ParameterBoundMismatch :
+                               // only steal returnType in cases listed above
+                               if (closestMatch != null) this.resolvedType = closestMatch.returnType;
+                       default :
+               }
                // record the closest match, for clients who may still need hint about possible method match
-               if (binding instanceof ProblemMethodBinding){
-                       MethodBinding closestMatch = ((ProblemMethodBinding)binding).closestMatch;
-                       if (closestMatch != null) this.codegenBinding = this.binding = closestMatch;
+               if (closestMatch != null) {
+                       this.binding = closestMatch;
+                       if (closestMatch.isPrivate() && !scope.isDefinedInMethod(closestMatch)) {
+                               // ignore cases where method is used from within inside itself (e.g. direct recursions)
+                               closestMatch.original().modifiers |= AccPrivateUsed;
+                       }
                }
-               return this.resolvedType = this.binding == null ? null : this.binding.returnType;
+               return this.resolvedType;
        }
        if (!binding.isStatic()) {
                // the "receiver" must not be a type, in other words, a NameReference that the TC has bound to a Type
                if (receiver instanceof NameReference 
-                               && (((NameReference) receiver).bits & BindingIds.TYPE) != 0) {
+                               && (((NameReference) receiver).bits & Binding.TYPE) != 0) {
                        scope.problemReporter().mustUseAStaticMethod(this, binding);
+               } else {
+                       // compute generic cast if necessary
+                       TypeBinding expectedReceiverType = this.actualReceiverType.erasure().isCompatibleWith(this.binding.declaringClass.erasure())
+                               ? this.actualReceiverType
+                               : this.binding.declaringClass;
+                       receiver.computeConversion(scope, expectedReceiverType, actualReceiverType);
+                       if (expectedReceiverType != this.actualReceiverType) this.actualReceiverType = expectedReceiverType;
                }
        } else {
                // static message invoked through receiver? legal but unoptimal (optional warning).
                if (!(receiver.isImplicitThis()
                                || receiver.isSuper()
                                || (receiver instanceof NameReference 
-                                       && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
+                                       && (((NameReference) receiver).bits & Binding.TYPE) != 0))) {
                        scope.problemReporter().nonStaticAccessToStaticMethod(this, binding);
                }
-               if (!receiver.isImplicitThis() && binding.declaringClass != receiverType) {
+               if (!receiver.isImplicitThis() && binding.declaringClass != actualReceiverType) {
                        scope.problemReporter().indirectAccessToStaticMethod(this, binding);
                }               
        }
-       if (arguments != null) {
-               for (int i = 0; i < arguments.length; i++) {
-                       arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]);
-               }
-               if (argumentsCast) {
-                       CastExpression.checkNeedForArgumentCasts(scope, this.receiver, receiverType, binding, this.arguments, argumentTypes, this);
-               }
-       }
+       if (this.arguments != null) 
+               checkInvocationArguments(scope, this.receiver, actualReceiverType, binding, this.arguments, argumentTypes, argsContainCast, this);
+
        //-------message send that are known to fail at compile time-----------
        if (binding.isAbstract()) {
                if (receiver.isSuper()) {
@@ -289,11 +376,28 @@ public TypeBinding resolveType(BlockScope scope) {
        if (isMethodUseDeprecated(binding, scope))
                scope.problemReporter().deprecatedMethod(binding, this);
 
-       return this.resolvedType = binding.returnType;
+       // from 1.5 compliance on, array#clone() returns the array type (but binding still shows Object)
+       if (actualReceiverType.isArrayType() 
+                       && this.binding.parameters == NoParameters 
+                       && scope.environment().options.complianceLevel >= JDK1_5 
+                       && CharOperation.equals(this.binding.selector, CLONE)) {
+               this.resolvedType = actualReceiverType;
+       } else {
+               this.resolvedType = this.binding.returnType;
+       }
+       return this.resolvedType;
 }
+
 public void setActualReceiverType(ReferenceBinding receiverType) {
-       this.qualifyingType = receiverType;
+       this.actualReceiverType = receiverType;
+}
+/**
+ * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
+ */
+public void setExpectedType(TypeBinding expectedType) {
+    this.expectedType = expectedType;
 }
+
 public void setDepth(int depth) {
        bits &= ~DepthMASK; // flush previous depth if any
        if (depth > 0) {
@@ -307,6 +411,11 @@ public void setFieldIndex(int depth) {
 public void traverse(ASTVisitor visitor, BlockScope blockScope) {
        if (visitor.visit(this, blockScope)) {
                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 (arguments != null) {
                        int argumentsLength = arguments.length;
                        for (int i = 0; i < argumentsLength; i++)