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