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