fixed bug #1037094 (foreach)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / internal / compiler / ast / MessageSend.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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  *******************************************************************************/
11 package net.sourceforge.phpeclipse.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
16 import net.sourceforge.phpdt.internal.compiler.lookup.BindingIds;
17 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
18 import net.sourceforge.phpdt.internal.compiler.lookup.InvocationSite;
19 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
20 import net.sourceforge.phpdt.internal.compiler.lookup.ProblemMethodBinding;
21 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
22 import net.sourceforge.phpdt.internal.compiler.lookup.SourceTypeBinding;
23 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
24
25 public class MessageSend extends Expression implements InvocationSite {
26         public Expression receiver ;
27         public char[] selector ;
28         public Expression[] arguments ;
29         public MethodBinding binding, codegenBinding;
30
31         public long nameSourcePosition ; //(start<<32)+end
32
33         MethodBinding syntheticAccessor;
34
35         public TypeBinding receiverType, qualifyingType;
36         
37 public MessageSend() {
38         
39 }
40 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
41
42         flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic()).unconditionalInits();
43         if (arguments != null) {
44                 int length = arguments.length;
45                 for (int i = 0; i < length; i++) {
46                         flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
47                 }
48         }
49         ReferenceBinding[] thrownExceptions;
50         if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
51                 // must verify that exceptions potentially thrown by this expression are caught in the method
52                 flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope);
53         }
54         manageSyntheticAccessIfNecessary(currentScope); 
55         return flowInfo;
56 }
57 /**
58  * MessageSend code generation
59  *
60  * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
61  * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
62  * @param valueRequired boolean
63  */ 
64 //public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
65 //
66 //      int pc = codeStream.position;
67 //
68 //      // generate receiver/enclosing instance access
69 //      boolean isStatic = codegenBinding.isStatic();
70 //      // outer access ?
71 //      if (!isStatic && ((bits & DepthMASK) != 0) && receiver.isImplicitThis()){
72 //              // outer method can be reached through emulation if implicit access
73 //              ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);             
74 //              Object[] path = currentScope.getEmulationPath(targetType, true /*only exact match*/, false/*consider enclosing arg*/);
75 //              codeStream.generateOuterAccess(path, this, targetType, currentScope);
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 manageSyntheticAccessIfNecessary(BlockScope currentScope){
129
130         if (binding.isPrivate()){
131
132                 // depth is set for both implicit and explicit access (see MethodBinding#canBeSeenBy)           
133                 if (currentScope.enclosingSourceType() != binding.declaringClass){
134                 
135                         syntheticAccessor = ((SourceTypeBinding)binding.declaringClass).addSyntheticMethod(binding, isSuperAccess());
136                         currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
137                         return;
138                 }
139
140         } else if (receiver instanceof QualifiedSuperReference){ // qualified super
141
142                 // qualified super need emulation always
143                 SourceTypeBinding destinationType = (SourceTypeBinding)(((QualifiedSuperReference)receiver).currentCompatibleType);
144                 syntheticAccessor = destinationType.addSyntheticMethod(binding, isSuperAccess());
145                 currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
146                 return;
147
148         } else if (binding.isProtected()){
149
150                 SourceTypeBinding enclosingSourceType;
151                 if (((bits & DepthMASK) != 0) 
152                                 && binding.declaringClass.getPackage() 
153                                         != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()){
154
155                         SourceTypeBinding currentCompatibleType = (SourceTypeBinding)enclosingSourceType.enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
156                         syntheticAccessor = currentCompatibleType.addSyntheticMethod(binding, isSuperAccess());
157                         currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
158                         return;
159                 }
160         }
161         // if the binding declaring class is not visible, need special action
162         // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
163         // NOTE: from target 1.2 on, method's declaring class is touched if any different from receiver type
164         // and not from Object or implicit static method call.  
165 //      if (binding.declaringClass != this.qualifyingType
166 //              && !this.qualifyingType.isArrayType()
167 //              && ((currentScope.environment().options.targetJDK >= CompilerOptions.JDK1_2
168 //                              && (!receiver.isImplicitThis() || !binding.isStatic())
169 //                              && binding.declaringClass.id != T_Object) // no change for Object methods
170 //                      || !binding.declaringClass.canBeSeenBy(currentScope))) {
171 //
172 //              this.codegenBinding = currentScope.enclosingSourceType().getUpdatedMethodBinding(binding, (ReferenceBinding) this.qualifyingType);
173 //      }
174 }
175
176 public TypeBinding resolveType(BlockScope scope) {
177         // Answer the signature return type
178         // Base type promotion
179
180         constant = NotAConstant;
181         this.qualifyingType = this.receiverType = receiver.resolveType(scope); 
182         
183         // will check for null after args are resolved
184         TypeBinding[] argumentTypes = NoParameters;
185         if (arguments != null) {
186                 boolean argHasError = false; // typeChecks all arguments 
187                 int length = arguments.length;
188                 argumentTypes = new TypeBinding[length];
189                 for (int i = 0; i < length; i++){
190                         if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null){
191                                 argHasError = true;
192                         }
193                 }
194                 if (argHasError){
195                         if(receiverType instanceof ReferenceBinding) {
196                                 // record any selector match, for clients who may still need hint about possible method match
197                                 this.codegenBinding = this.binding = scope.findMethod((ReferenceBinding)receiverType, selector, new TypeBinding[]{}, this);
198                         }                       
199                         return null;
200                 }
201         }
202         if (this.receiverType == null)
203                 return null;
204
205         // base type cannot receive any message
206         if (this.receiverType.isBaseType()) {
207                 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
208                 return null;
209         }
210
211         this.codegenBinding = this.binding = 
212                 receiver.isImplicitThis()
213                         ? scope.getImplicitMethod(selector, argumentTypes, this)
214                         : scope.getMethod(this.receiverType, selector, argumentTypes, this); 
215         if (!binding.isValidBinding()) {
216                 if (binding.declaringClass == null) {
217                         if (this.receiverType instanceof ReferenceBinding) {
218                                 binding.declaringClass = (ReferenceBinding) this.receiverType;
219                         } else { 
220                                 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
221                                 return null;
222                         }
223                 }
224                 scope.problemReporter().invalidMethod(this, binding);
225                 // record the closest match, for clients who may still need hint about possible method match
226                 if (binding instanceof ProblemMethodBinding){
227                         MethodBinding closestMatch = ((ProblemMethodBinding)binding).closestMatch;
228                         if (closestMatch != null) this.codegenBinding = this.binding = closestMatch;
229                 }
230                 return binding == null ? null : binding.returnType;
231         }
232         if (!binding.isStatic()) {
233                 // the "receiver" must not be a type, in other words, a NameReference that the TC has bound to a Type
234                 if (receiver instanceof NameReference 
235                                 && (((NameReference) receiver).bits & BindingIds.TYPE) != 0) {
236                         scope.problemReporter().mustUseAStaticMethod(this, binding);
237                 }
238         } else {
239                 // static message invoked through receiver? legal but unoptimal (optional warning).
240                 if (!(receiver.isImplicitThis()
241                                 || receiver.isSuper()
242                                 || (receiver instanceof NameReference 
243                                         && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
244                         scope.problemReporter().unnecessaryReceiverForStaticMethod(this, binding);
245                 }
246         }
247         if (arguments != null)
248                 for (int i = 0; i < arguments.length; i++)
249                         arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]);
250
251         //-------message send that are known to fail at compile time-----------
252         if (binding.isAbstract()) {
253                 if (receiver.isSuper()) {
254                         scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, binding);
255                 }
256                 // abstract private methods cannot occur nor abstract static............
257         }
258         if (isMethodUseDeprecated(binding, scope))
259                 scope.problemReporter().deprecatedMethod(binding, this);
260
261         return this.resolvedType = binding.returnType;
262 }
263 public void setActualReceiverType(ReferenceBinding receiverType) {
264         this.qualifyingType = receiverType;
265 }
266 public void setDepth(int depth) {
267         bits &= ~DepthMASK; // flush previous depth if any
268         if (depth > 0) {
269                 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
270         }
271 }
272 public void setFieldIndex(int depth) {
273         // ignore for here
274 }
275
276 public String toStringExpression(){
277         
278         String s = ""; //$NON-NLS-1$
279         if (!receiver.isImplicitThis())
280                 s = s + receiver.toStringExpression()+"."; //$NON-NLS-1$
281         s = s + new String(selector) + "(" ; //$NON-NLS-1$
282         if (arguments != null)
283                 for (int i = 0; i < arguments.length ; i ++)
284                 {       s = s + arguments[i].toStringExpression();
285                         if ( i != arguments.length -1 ) s = s + " , " ;};; //$NON-NLS-1$
286         s =s + ")" ; //$NON-NLS-1$
287         return s;
288 }
289
290 public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope blockScope) {
291         if (visitor.visit(this, blockScope)) {
292                 receiver.traverse(visitor, blockScope);
293                 if (arguments != null) {
294                         int argumentsLength = arguments.length;
295                         for (int i = 0; i < argumentsLength; i++)
296                                 arguments[i].traverse(visitor, blockScope);
297                 }
298         }
299         visitor.endVisit(this, blockScope);
300 }
301 }