improved PHP parser
[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.ASTVisitor;
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 net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
61  * @param codeStream net.sourceforge.phpdt.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 public StringBuffer printExpression(int indent, StringBuffer output){
176         
177         if (!receiver.isImplicitThis()) receiver.printExpression(0, output).append('.');
178         output.append(selector).append('(') ; //$NON-NLS-1$
179         if (arguments != null) {
180                 for (int i = 0; i < arguments.length ; i ++) {  
181                         if (i > 0) output.append(", "); //$NON-NLS-1$
182                         arguments[i].printExpression(0, output);
183                 }
184         }
185         return output.append(')');
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                         if(receiverType instanceof ReferenceBinding) {
207                                 // record any selector match, for clients who may still need hint about possible method match
208                                 this.codegenBinding = this.binding = scope.findMethod((ReferenceBinding)receiverType, selector, new TypeBinding[]{}, this);
209                         }                       
210                         return null;
211                 }
212         }
213         if (this.receiverType == null)
214                 return null;
215
216         // base type cannot receive any message
217         if (this.receiverType.isBaseType()) {
218                 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
219                 return null;
220         }
221
222         this.codegenBinding = this.binding = 
223                 receiver.isImplicitThis()
224                         ? scope.getImplicitMethod(selector, argumentTypes, this)
225                         : scope.getMethod(this.receiverType, selector, argumentTypes, this); 
226         if (!binding.isValidBinding()) {
227                 if (binding.declaringClass == null) {
228                         if (this.receiverType instanceof ReferenceBinding) {
229                                 binding.declaringClass = (ReferenceBinding) this.receiverType;
230                         } else { 
231                                 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
232                                 return null;
233                         }
234                 }
235                 scope.problemReporter().invalidMethod(this, binding);
236                 // record the closest match, for clients who may still need hint about possible method match
237                 if (binding instanceof ProblemMethodBinding){
238                         MethodBinding closestMatch = ((ProblemMethodBinding)binding).closestMatch;
239                         if (closestMatch != null) this.codegenBinding = this.binding = closestMatch;
240                 }
241                 return binding == null ? null : binding.returnType;
242         }
243         if (!binding.isStatic()) {
244                 // the "receiver" must not be a type, in other words, a NameReference that the TC has bound to a Type
245                 if (receiver instanceof NameReference 
246                                 && (((NameReference) receiver).bits & BindingIds.TYPE) != 0) {
247                         scope.problemReporter().mustUseAStaticMethod(this, binding);
248                 }
249         } else {
250                 // static message invoked through receiver? legal but unoptimal (optional warning).
251                 if (!(receiver.isImplicitThis()
252                                 || receiver.isSuper()
253                                 || (receiver instanceof NameReference 
254                                         && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
255                         scope.problemReporter().unnecessaryReceiverForStaticMethod(this, binding);
256                 }
257         }
258         if (arguments != null)
259                 for (int i = 0; i < arguments.length; i++)
260                         arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]);
261
262         //-------message send that are known to fail at compile time-----------
263         if (binding.isAbstract()) {
264                 if (receiver.isSuper()) {
265                         scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, binding);
266                 }
267                 // abstract private methods cannot occur nor abstract static............
268         }
269         if (isMethodUseDeprecated(binding, scope))
270                 scope.problemReporter().deprecatedMethod(binding, this);
271
272         return this.resolvedType = binding.returnType;
273 }
274 public void setActualReceiverType(ReferenceBinding receiverType) {
275         this.qualifyingType = receiverType;
276 }
277 public void setDepth(int depth) {
278         bits &= ~DepthMASK; // flush previous depth if any
279         if (depth > 0) {
280                 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
281         }
282 }
283 public void setFieldIndex(int depth) {
284         // ignore for here
285 }
286
287 public String toStringExpression(){
288         
289         String s = ""; //$NON-NLS-1$
290         if (!receiver.isImplicitThis())
291                 s = s + receiver.toStringExpression()+"."; //$NON-NLS-1$
292         s = s + new String(selector) + "(" ; //$NON-NLS-1$
293         if (arguments != null)
294                 for (int i = 0; i < arguments.length ; i ++)
295                 {       s = s + arguments[i].toStringExpression();
296                         if ( i != arguments.length -1 ) s = s + " , " ;};; //$NON-NLS-1$
297         s =s + ")" ; //$NON-NLS-1$
298         return s;
299 }
300
301 public void traverse(ASTVisitor visitor, BlockScope blockScope) {
302         if (visitor.visit(this, blockScope)) {
303                 receiver.traverse(visitor, blockScope);
304                 if (arguments != null) {
305                         int argumentsLength = arguments.length;
306                         for (int i = 0; i < argumentsLength; i++)
307                                 arguments[i].traverse(visitor, blockScope);
308                 }
309         }
310         visitor.endVisit(this, blockScope);
311 }
312 }