1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     Nick Teryaev - fix for bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=40752)
11  *******************************************************************************/
12 package org.eclipse.jdt.internal.compiler.ast;
13 
14 import org.eclipse.jdt.internal.compiler.ASTVisitor;
15 import org.eclipse.jdt.internal.compiler.flow.*;
16 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
17 import org.eclipse.jdt.internal.compiler.codegen.*;
18 import org.eclipse.jdt.internal.compiler.lookup.*;
19 
20 public class MessageSend extends Expression implements InvocationSite {
21 
22 	public Expression receiver ;
23 	public char[] selector ;
24 	public Expression[] arguments ;
25 	public MethodBinding binding;							// exact binding resulting from lookup
26 	protected MethodBinding codegenBinding;		// actual binding used for code generation (if no synthetic accessor)
27 	MethodBinding syntheticAccessor;						// synthetic accessor for inner-emulation
28 	public TypeBinding expectedType;					// for generic method invocation (return type inference)
29 
30 	public long nameSourcePosition ; //(start<<32)+end
31 
32 	public TypeBinding receiverType, qualifyingType;
33 	public TypeBinding genericCast;
34 	public TypeReference[] typeArguments;
35 	public TypeBinding[] genericTypeArguments;
36 
analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)37 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
38 
39 	flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic()).unconditionalInits();
40 	if (arguments != null) {
41 		int length = arguments.length;
42 		for (int i = 0; i < length; i++) {
43 			flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
44 		}
45 	}
46 	ReferenceBinding[] thrownExceptions;
47 	if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
48 		// must verify that exceptions potentially thrown by this expression are caught in the method
49 		flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope);
50 	}
51 	manageSyntheticAccessIfNecessary(currentScope, flowInfo);
52 	return flowInfo;
53 }
54 /**
55  * @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)
56  */
computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType)57 public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
58 	if (runtimeTimeType == null || compileTimeType == null)
59 		return;
60 	// set the generic cast after the fact, once the type expectation is fully known (no need for strict cast)
61 	if (this.binding != null && this.binding.isValidBinding()) {
62 		MethodBinding originalBinding = this.binding.original();
63 		if (originalBinding != this.binding) {
64 		    // extra cast needed if method return type has type variable
65 		    if ((originalBinding.returnType.tagBits & TagBits.HasTypeVariable) != 0 && runtimeTimeType.id != T_Object) {
66 		        this.genericCast = originalBinding.returnType.genericCast(runtimeTimeType);
67 		    }
68 		}
69 	}
70 	super.computeConversion(scope, runtimeTimeType, compileTimeType);
71 }
72 /**
73  * MessageSend code generation
74  *
75  * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
76  * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
77  * @param valueRequired boolean
78  */
generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired)79 public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
80 
81 	int pc = codeStream.position;
82 
83 	// generate receiver/enclosing instance access
84 	boolean isStatic = this.codegenBinding.isStatic();
85 	// outer access ?
86 	if (!isStatic && ((bits & DepthMASK) != 0) && receiver.isImplicitThis()){
87 		// outer method can be reached through emulation if implicit access
88 		ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
89 		Object[] path = currentScope.getEmulationPath(targetType, true /*only exact match*/, false/*consider enclosing arg*/);
90 		codeStream.generateOuterAccess(path, this, targetType, currentScope);
91 	} else {
92 		receiver.generateCode(currentScope, codeStream, !isStatic);
93 	}
94 	// generate arguments
95 	if (arguments != null){
96 		for (int i = 0, max = arguments.length; i < max; i++){
97 			arguments[i].generateCode(currentScope, codeStream, true);
98 		}
99 	}
100 	// actual message invocation
101 	if (syntheticAccessor == null){
102 		if (isStatic){
103 			codeStream.invokestatic(this.codegenBinding);
104 		} else {
105 			if( (receiver.isSuper()) || this.codegenBinding.isPrivate()){
106 				codeStream.invokespecial(this.codegenBinding);
107 			} else {
108 				if (this.codegenBinding.declaringClass.isInterface()){
109 					codeStream.invokeinterface(this.codegenBinding);
110 				} else {
111 					codeStream.invokevirtual(this.codegenBinding);
112 				}
113 			}
114 		}
115 	} else {
116 		codeStream.invokestatic(syntheticAccessor);
117 	}
118 	// operation on the returned value
119 	if (valueRequired){
120 		// implicit conversion if necessary
121 		codeStream.generateImplicitConversion(implicitConversion);
122 		if (this.genericCast != null) codeStream.checkcast(this.genericCast);
123 	} else {
124 		// pop return value if any
125 		switch(binding.returnType.id){
126 			case T_long :
127 			case T_double :
128 				codeStream.pop2();
129 				break;
130 			case T_void :
131 				break;
132 			default:
133 				codeStream.pop();
134 		}
135 	}
136 	codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>> 32)); // highlight selector
137 }
138 /**
139  * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
140  */
genericTypeArguments()141 public TypeBinding[] genericTypeArguments() {
142 	return this.genericTypeArguments;
143 }
isSuperAccess()144 public boolean isSuperAccess() {
145 	return receiver.isSuper();
146 }
isTypeAccess()147 public boolean isTypeAccess() {
148 	return receiver != null && receiver.isTypeReference();
149 }
manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo)150 public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo){
151 
152 	if (!flowInfo.isReachable()) return;
153 
154 	// if method from parameterized type got found, use the original method at codegen time
155 	this.codegenBinding = this.binding.original();
156 	if (this.binding.isPrivate()){
157 
158 		// depth is set for both implicit and explicit access (see MethodBinding#canBeSeenBy)
159 		if (currentScope.enclosingSourceType() != this.codegenBinding.declaringClass){
160 
161 			syntheticAccessor = ((SourceTypeBinding)this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isSuperAccess());
162 			currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
163 			return;
164 		}
165 
166 	} else if (receiver instanceof QualifiedSuperReference){ // qualified super
167 
168 		// qualified super need emulation always
169 		SourceTypeBinding destinationType = (SourceTypeBinding)(((QualifiedSuperReference)receiver).currentCompatibleType);
170 		syntheticAccessor = destinationType.addSyntheticMethod(this.codegenBinding, isSuperAccess());
171 		currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
172 		return;
173 
174 	} else if (binding.isProtected()){
175 
176 		SourceTypeBinding enclosingSourceType;
177 		if (((bits & DepthMASK) != 0)
178 				&& this.codegenBinding.declaringClass.getPackage()
179 					!= (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()){
180 
181 			SourceTypeBinding currentCompatibleType = (SourceTypeBinding)enclosingSourceType.enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
182 			syntheticAccessor = currentCompatibleType.addSyntheticMethod(this.codegenBinding, isSuperAccess());
183 			currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
184 			return;
185 		}
186 	}
187 
188 	// if the binding declaring class is not visible, need special action
189 	// for runtime compatibility on 1.2 VMs : change the declaring class of the binding
190 	// NOTE: from target 1.2 on, method's declaring class is touched if any different from receiver type
191 	// and not from Object or implicit static method call.
192 	if (this.binding.declaringClass != this.qualifyingType
193 		&& !this.qualifyingType.isArrayType()
194 		&& ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2
195 				&& (!receiver.isImplicitThis() || !this.codegenBinding.isStatic())
196 				&& this.binding.declaringClass.id != T_Object) // no change for Object methods
197 			|| !this.binding.declaringClass.canBeSeenBy(currentScope))) {
198 
199 		this.codegenBinding = currentScope.enclosingSourceType().getUpdatedMethodBinding(
200 		        										this.codegenBinding, (ReferenceBinding) this.qualifyingType.erasure());
201 
202 		// Post 1.4.0 target, array clone() invocations are qualified with array type
203 		// This is handled in array type #clone method binding resolution (see Scope and UpdatedMethodBinding)
204 	}
205 }
206 
printExpression(int indent, StringBuffer output)207 public StringBuffer printExpression(int indent, StringBuffer output){
208 
209 	if (!receiver.isImplicitThis()) receiver.printExpression(0, output).append('.');
210 	if (this.typeArguments != null) {
211 		output.append('<');//$NON-NLS-1$
212 		int max = typeArguments.length - 1;
213 		for (int j = 0; j < max; j++) {
214 			typeArguments[j].print(0, output);
215 			output.append(", ");//$NON-NLS-1$
216 		}
217 		typeArguments[max].print(0, output);
218 		output.append('>');
219 	}
220 	output.append(selector).append('(') ; //$NON-NLS-1$
221 	if (arguments != null) {
222 		for (int i = 0; i < arguments.length ; i ++) {
223 			if (i > 0) output.append(", "); //$NON-NLS-1$
224 			arguments[i].printExpression(0, output);
225 		}
226 	}
227 	return output.append(')');
228 }
229 
resolveType(BlockScope scope)230 public TypeBinding resolveType(BlockScope scope) {
231 	// Answer the signature return type
232 	// Base type promotion
233 
234 	constant = NotAConstant;
235 	boolean receiverCast = false, argsContainCast = false;
236 	if (this.receiver instanceof CastExpression) {
237 		this.receiver.bits |= IgnoreNeedForCastCheckMASK; // will check later on
238 		receiverCast = true;
239 	}
240 	this.qualifyingType = this.receiverType = receiver.resolveType(scope);
241 	if (receiverCast && this.receiverType != null) {
242 		 // due to change of declaring class with receiver type, only identity cast should be notified
243 		if (((CastExpression)this.receiver).expression.resolvedType == this.receiverType) {
244 			scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);
245 		}
246 	}
247 	// resolve type arguments (for generic constructor call)
248 	if (this.typeArguments != null) {
249 		int length = this.typeArguments.length;
250 		boolean argHasError = false; // typeChecks all arguments
251 		this.genericTypeArguments = new TypeBinding[length];
252 		for (int i = 0; i < length; i++) {
253 			if ((this.genericTypeArguments[i] = this.typeArguments[i].resolveType(scope)) == null) {
254 				argHasError = true;
255 			}
256 		}
257 		if (argHasError) {
258 			return null;
259 		}
260 	}
261 	// will check for null after args are resolved
262 	TypeBinding[] argumentTypes = NoParameters;
263 	if (arguments != null) {
264 		boolean argHasError = false; // typeChecks all arguments
265 		int length = arguments.length;
266 		argumentTypes = new TypeBinding[length];
267 		for (int i = 0; i < length; i++){
268 			Expression argument = arguments[i];
269 			if (argument instanceof CastExpression) {
270 				argument.bits |= IgnoreNeedForCastCheckMASK; // will check later on
271 				argsContainCast = true;
272 			}
273 			if ((argumentTypes[i] = argument.resolveType(scope)) == null){
274 				argHasError = true;
275 			}
276 		}
277 		if (argHasError) {
278 			if(receiverType instanceof ReferenceBinding) {
279 				// record any selector match, for clients who may still need hint about possible method match
280 				this.binding = scope.findMethod((ReferenceBinding)receiverType, selector, new TypeBinding[]{}, this);
281 			}
282 			return null;
283 		}
284 	}
285 	if (this.receiverType == null) {
286 		return null;
287 	}
288 	// base type cannot receive any message
289 	if (this.receiverType.isBaseType()) {
290 		scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
291 		return null;
292 	}
293 	this.binding =
294 		receiver.isImplicitThis()
295 			? scope.getImplicitMethod(selector, argumentTypes, this)
296 			: scope.getMethod(this.receiverType, selector, argumentTypes, this);
297 	if (!binding.isValidBinding()) {
298 		if (binding.declaringClass == null) {
299 			if (this.receiverType instanceof ReferenceBinding) {
300 				binding.declaringClass = (ReferenceBinding) this.receiverType;
301 			} else {
302 				scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
303 				return null;
304 			}
305 		}
306 		scope.problemReporter().invalidMethod(this, binding);
307 		MethodBinding closestMatch = ((ProblemMethodBinding)binding).closestMatch;
308 		switch (this.binding.problemId()) {
309 			case ProblemReasons.Ambiguous :
310 			case ProblemReasons.NotVisible :
311 			case ProblemReasons.NonStaticReferenceInConstructorInvocation :
312 			case ProblemReasons.NonStaticReferenceInStaticContext :
313 			case ProblemReasons.ReceiverTypeNotVisible :
314 			case ProblemReasons.ParameterBoundMismatch :
315 				// only steal returnType in cases listed above
316 				if (closestMatch != null) this.resolvedType = closestMatch.returnType;
317 			default :
318 		}
319 		// record the closest match, for clients who may still need hint about possible method match
320 		if (closestMatch != null) {
321 			this.binding = closestMatch;
322 			if (closestMatch.isPrivate() && !scope.isDefinedInMethod(closestMatch)) {
323 				// ignore cases where method is used from within inside itself (e.g. direct recursions)
324 				closestMatch.original().modifiers |= AccPrivateUsed;
325 			}
326 		}
327 		return this.resolvedType;
328 	}
329 	if (!binding.isStatic()) {
330 		// the "receiver" must not be a type, in other words, a NameReference that the TC has bound to a Type
331 		if (receiver instanceof NameReference
332 				&& (((NameReference) receiver).bits & BindingIds.TYPE) != 0) {
333 			scope.problemReporter().mustUseAStaticMethod(this, binding);
334 		}
335 		receiver.computeConversion(scope, receiverType, receiverType); // compute generic cast if necessary
336 	} else {
337 		// static message invoked through receiver? legal but unoptimal (optional warning).
338 		if (!(receiver.isImplicitThis()
339 				|| receiver.isSuper()
340 				|| (receiver instanceof NameReference
341 					&& (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
342 			scope.problemReporter().nonStaticAccessToStaticMethod(this, binding);
343 		}
344 		if (!receiver.isImplicitThis() && binding.declaringClass != receiverType) {
345 			scope.problemReporter().indirectAccessToStaticMethod(this, binding);
346 		}
347 	}
348 	if (this.arguments != null)
349 		checkInvocationArguments(scope, this.receiver, receiverType, binding, this.arguments, argumentTypes, argsContainCast, this);
350 
351 	//-------message send that are known to fail at compile time-----------
352 	if (binding.isAbstract()) {
353 		if (receiver.isSuper()) {
354 			scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, binding);
355 		}
356 		// abstract private methods cannot occur nor abstract static............
357 	}
358 	if (isMethodUseDeprecated(binding, scope))
359 		scope.problemReporter().deprecatedMethod(binding, this);
360 
361 	return this.resolvedType = this.binding.returnType;
362 }
setActualReceiverType(ReferenceBinding receiverType)363 public void setActualReceiverType(ReferenceBinding receiverType) {
364 	this.qualifyingType = receiverType;
365 }
366 /**
367  * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
368  */
setExpectedType(TypeBinding expectedType)369 public void setExpectedType(TypeBinding expectedType) {
370     this.expectedType = expectedType;
371 }
372 
setDepth(int depth)373 public void setDepth(int depth) {
374 	bits &= ~DepthMASK; // flush previous depth if any
375 	if (depth > 0) {
376 		bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
377 	}
378 }
setFieldIndex(int depth)379 public void setFieldIndex(int depth) {
380 	// ignore for here
381 }
382 
traverse(ASTVisitor visitor, BlockScope blockScope)383 public void traverse(ASTVisitor visitor, BlockScope blockScope) {
384 	if (visitor.visit(this, blockScope)) {
385 		receiver.traverse(visitor, blockScope);
386 		if (this.typeArguments != null) {
387 			for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) {
388 				this.typeArguments[i].traverse(visitor, blockScope);
389 			}
390 		}
391 		if (arguments != null) {
392 			int argumentsLength = arguments.length;
393 			for (int i = 0; i < argumentsLength; i++)
394 				arguments[i].traverse(visitor, blockScope);
395 		}
396 	}
397 	visitor.endVisit(this, blockScope);
398 }
399 }
400