*** empty log message ***
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / MessageSend.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
16 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
17 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
18 import net.sourceforge.phpdt.internal.compiler.lookup.BindingIds;
19 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
20 import net.sourceforge.phpdt.internal.compiler.lookup.InvocationSite;
21 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
22 import net.sourceforge.phpdt.internal.compiler.lookup.ProblemMethodBinding;
23 import net.sourceforge.phpdt.internal.compiler.lookup.ProblemReasons;
24 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
25 import net.sourceforge.phpdt.internal.compiler.lookup.SourceTypeBinding;
26 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
27
28 public class MessageSend extends Expression implements InvocationSite {
29         public Expression receiver ;
30         public char[] selector ;
31         public Expression[] arguments ;
32         public MethodBinding binding, codegenBinding;
33
34         public long nameSourcePosition ; //(start<<32)+end
35
36         MethodBinding syntheticAccessor;
37
38         public TypeBinding receiverType, qualifyingType;
39         
40 public MessageSend() {
41         
42 }
43 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
44
45         flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic()).unconditionalInits();
46         if (arguments != null) {
47                 int length = arguments.length;
48                 for (int i = 0; i < length; i++) {
49                         flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
50                 }
51         }
52         ReferenceBinding[] thrownExceptions;
53         if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
54                 // must verify that exceptions potentially thrown by this expression are caught in the method
55                 flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope);
56         }
57         // if invoking through an enclosing instance, then must perform the field generation -- only if reachable
58         manageEnclosingInstanceAccessIfNecessary(currentScope);
59         manageSyntheticAccessIfNecessary(currentScope); 
60         return flowInfo;
61 }
62 /**
63  * MessageSend code generation
64  *
65  * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
66  * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
67  * @param valueRequired boolean
68  */ 
69 public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
70
71         int pc = codeStream.position;
72
73         // generate receiver/enclosing instance access
74         boolean isStatic = codegenBinding.isStatic();
75         // outer access ?
76         if (!isStatic && ((bits & DepthMASK) != 0) && (receiver == ThisReference.ThisImplicit)){
77                 // outer method can be reached through emulation if implicit access
78                 Object[] path = currentScope.getExactEmulationPath(currentScope.enclosingSourceType().enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT));
79                 if (path == null) {
80                         // emulation was not possible (should not happen per construction)
81                         currentScope.problemReporter().needImplementation();
82                 } else {
83                         codeStream.generateOuterAccess(path, this, currentScope);
84                 }
85         } else {
86                 receiver.generateCode(currentScope, codeStream, !isStatic);
87         }
88         // generate arguments
89         if (arguments != null){
90                 for (int i = 0, max = arguments.length; i < max; i++){
91                         arguments[i].generateCode(currentScope, codeStream, true);
92                 }
93         }
94         // actual message invocation
95         if (syntheticAccessor == null){
96                 if (isStatic){
97                         codeStream.invokestatic(codegenBinding);
98                 } else {
99                         if( (receiver.isSuper()) || codegenBinding.isPrivate()){
100                                 codeStream.invokespecial(codegenBinding);
101                         } else {
102                                 if (codegenBinding.declaringClass.isInterface()){
103                                         codeStream.invokeinterface(codegenBinding);
104                                 } else {
105                                         codeStream.invokevirtual(codegenBinding);
106                                 }
107                         }
108                 }
109         } else {
110                 codeStream.invokestatic(syntheticAccessor);
111         }
112         // operation on the returned value
113         if (valueRequired){
114                 // implicit conversion if necessary
115                 codeStream.generateImplicitConversion(implicitConversion);
116         } else {
117                 // pop return value if any
118                 switch(binding.returnType.id){
119                         case T_long :
120                         case T_double :
121                                 codeStream.pop2();
122                                 break;
123                         case T_void :
124                                 break;
125                         default:
126                                 codeStream.pop();
127                 }
128         }
129         codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>> 32)); // highlight selector
130 }
131 public boolean isSuperAccess() {        
132         return receiver.isSuper();
133 }
134 public boolean isTypeAccess() { 
135         return receiver != null && receiver.isTypeReference();
136 }
137 public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope) {
138         if (((bits & DepthMASK) != 0) && !binding.isStatic() && (receiver == ThisReference.ThisImplicit)) {
139                 ReferenceBinding compatibleType = currentScope.enclosingSourceType();
140                 // the declaringClass of the target binding must be compatible with the enclosing
141                 // type at <depth> levels outside
142                 for (int i = 0, depth = (bits & DepthMASK) >> DepthSHIFT; i < depth; i++) {
143                         compatibleType = compatibleType.enclosingType();
144                 }
145                 currentScope.emulateOuterAccess((SourceTypeBinding) compatibleType, false); // request cascade of accesses
146         }
147 }
148 public void manageSyntheticAccessIfNecessary(BlockScope currentScope){
149
150         if (binding.isPrivate()){
151
152                 // depth is set for both implicit and explicit access (see MethodBinding#canBeSeenBy)           
153                 if (currentScope.enclosingSourceType() != binding.declaringClass){
154                 
155                         syntheticAccessor = ((SourceTypeBinding)binding.declaringClass).addSyntheticMethod(binding);
156                         currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
157                         return;
158                 }
159
160         } else if (receiver instanceof QualifiedSuperReference){ // qualified super
161
162                 // qualified super need emulation always
163                 SourceTypeBinding destinationType = (SourceTypeBinding)(((QualifiedSuperReference)receiver).currentCompatibleType);
164                 syntheticAccessor = destinationType.addSyntheticMethod(binding);
165                 currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
166                 return;
167
168         } else if (binding.isProtected()){
169
170                 SourceTypeBinding enclosingSourceType;
171                 if (((bits & DepthMASK) != 0) 
172                                 && binding.declaringClass.getPackage() 
173                                         != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()){
174
175                         SourceTypeBinding currentCompatibleType = (SourceTypeBinding)enclosingSourceType.enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
176                         syntheticAccessor = currentCompatibleType.addSyntheticMethod(binding);
177                         currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
178                         return;
179                 }
180         }
181         // if the binding declaring class is not visible, need special action
182         // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
183         // NOTE: from 1.4 on, method's declaring class is touched if any different from receiver type
184         // and not from Object or implicit static method call.  
185         if (binding.declaringClass != this.qualifyingType
186                 && !this.qualifyingType.isArrayType()
187                 && ((currentScope.environment().options.complianceLevel >= CompilerOptions.JDK1_4
188                                 && (receiver != ThisReference.ThisImplicit || !binding.isStatic())
189                                 && binding.declaringClass.id != T_Object) // no change for Object methods
190                         || !binding.declaringClass.canBeSeenBy(currentScope))) {
191
192                 this.codegenBinding = currentScope.enclosingSourceType().getUpdatedMethodBinding(binding, (ReferenceBinding) this.qualifyingType);
193         }
194 }
195
196 public TypeBinding resolveType(BlockScope scope) {
197         // Answer the signature return type
198         // Base type promotion
199
200         constant = NotAConstant;
201         this.qualifyingType = this.receiverType = receiver.resolveType(scope); 
202         
203         // will check for null after args are resolved
204         TypeBinding[] argumentTypes = NoParameters;
205         if (arguments != null) {
206                 boolean argHasError = false; // typeChecks all arguments 
207                 int length = arguments.length;
208                 argumentTypes = new TypeBinding[length];
209                 for (int i = 0; i < length; i++){
210                         if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null){
211                                 argHasError = true;
212                         }
213                 }
214                 if (argHasError){
215                         MethodBinding closestMethod = null;
216                         if(receiverType instanceof ReferenceBinding) {
217                                 // record any selector match, for clients who may still need hint about possible method match
218                                 this.codegenBinding = this.binding = scope.findMethod((ReferenceBinding)receiverType, selector, new TypeBinding[]{}, this);
219                         }                       
220                         return null;
221                 }
222         }
223         if (this.receiverType == null)
224                 return null;
225
226         // base type cannot receive any message
227         if (this.receiverType.isBaseType()) {
228                 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
229                 return null;
230         }
231
232         this.codegenBinding = this.binding = 
233                 receiver == ThisReference.ThisImplicit
234                         ? scope.getImplicitMethod(selector, argumentTypes, this)
235                         : scope.getMethod(this.receiverType, selector, argumentTypes, this); 
236         if (!binding.isValidBinding()) {
237                 if (binding.declaringClass == null) {
238                         if (this.receiverType instanceof ReferenceBinding) {
239                                 binding.declaringClass = (ReferenceBinding) this.receiverType;
240                         } else { // really bad error ....
241                                 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
242                                 return null;
243                         }
244                 }
245                 scope.problemReporter().invalidMethod(this, binding);
246                 // record the closest match, for clients who may still need hint about possible method match
247                 if (binding.problemId() == ProblemReasons.NotFound){
248                         this.codegenBinding = this.binding = ((ProblemMethodBinding)binding).closestMatch;
249                 }
250                 return null;
251         }
252         if (!binding.isStatic()) {
253                 // the "receiver" must not be a type, i.e. a NameReference that the TC has bound to a Type
254                 if (receiver instanceof NameReference) {
255                         if ((((NameReference) receiver).bits & BindingIds.TYPE) != 0) {
256                                 scope.problemReporter().mustUseAStaticMethod(this, binding);
257                                 return null;
258                         }
259                 }
260         }
261         if (arguments != null)
262                 for (int i = 0; i < arguments.length; i++)
263                         arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]);
264
265         //-------message send that are known to fail at compile time-----------
266         if (binding.isAbstract()) {
267                 if (receiver.isSuper()) {
268                         scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, binding);
269                         return null;
270                 }
271                 // abstract private methods cannot occur nor abstract static............
272         }
273         if (isMethodUseDeprecated(binding, scope))
274                 scope.problemReporter().deprecatedMethod(binding, this);
275
276         return binding.returnType;
277 }
278 public void setActualReceiverType(ReferenceBinding receiverType) {
279         this.qualifyingType = receiverType;
280 }
281 public void setDepth(int depth) {
282         if (depth > 0) {
283                 bits &= ~DepthMASK; // flush previous depth if any
284                 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
285         }
286 }
287 public void setFieldIndex(int depth) {
288         // ignore for here
289 }
290
291 public String toStringExpression(){
292         
293         String s = ""; //$NON-NLS-1$
294         if (receiver != ThisReference.ThisImplicit)
295                 s = s + receiver.toStringExpression()+"."; //$NON-NLS-1$
296         s = s + new String(selector) + "(" ; //$NON-NLS-1$
297         if (arguments != null)
298                 for (int i = 0; i < arguments.length ; i ++)
299                 {       s = s + arguments[i].toStringExpression();
300                         if ( i != arguments.length -1 ) s = s + " , " ;};; //$NON-NLS-1$
301         s =s + ")" ; //$NON-NLS-1$
302         return s;
303 }
304
305 public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope blockScope) {
306         if (visitor.visit(this, blockScope)) {
307                 receiver.traverse(visitor, blockScope);
308                 if (arguments != null) {
309                         int argumentsLength = arguments.length;
310                         for (int i = 0; i < argumentsLength; i++)
311                                 arguments[i].traverse(visitor, blockScope);
312                 }
313         }
314         visitor.endVisit(this, blockScope);
315 }
316 }