Applying pteague's patch (re #685)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / 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.phpdt.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
28         public char[] selector;
29
30         public Expression[] arguments;
31
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
44         public FlowInfo analyseCode(BlockScope currentScope,
45                         FlowContext flowContext, FlowInfo flowInfo) {
46
47                 flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo,
48                                 !binding.isStatic()).unconditionalInits();
49                 if (arguments != null) {
50                         int length = arguments.length;
51                         for (int i = 0; i < length; i++) {
52                                 flowInfo = arguments[i].analyseCode(currentScope, flowContext,
53                                                 flowInfo).unconditionalInits();
54                         }
55                 }
56                 ReferenceBinding[] thrownExceptions;
57                 if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
58                         // must verify that exceptions potentially thrown by this expression
59                         // are caught in the method
60                         flowContext.checkExceptionHandlers(thrownExceptions, this,
61                                         flowInfo, currentScope);
62                 }
63                 manageSyntheticAccessIfNecessary(currentScope);
64                 return flowInfo;
65         }
66
67         /**
68          * MessageSend code generation
69          * 
70          * @param currentScope
71          *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
72          * @param codeStream
73          *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
74          * @param valueRequired
75          *            boolean
76          */
77         // public void generateCode(BlockScope currentScope, CodeStream codeStream,
78         // boolean valueRequired) {
79         //
80         // int pc = codeStream.position;
81         //
82         // // generate receiver/enclosing instance access
83         // boolean isStatic = codegenBinding.isStatic();
84         // // outer access ?
85         // if (!isStatic && ((bits & DepthMASK) != 0) && receiver.isImplicitThis()){
86         // // outer method can be reached through emulation if implicit access
87         // ReferenceBinding targetType =
88         // currentScope.enclosingSourceType().enclosingTypeAt((bits & DepthMASK) >>
89         // DepthSHIFT);
90         // Object[] path = currentScope.getEmulationPath(targetType, true /*only
91         // exact match*/, false/*consider enclosing arg*/);
92         // codeStream.generateOuterAccess(path, this, targetType, currentScope);
93         // } else {
94         // receiver.generateCode(currentScope, codeStream, !isStatic);
95         // }
96         // // generate arguments
97         // if (arguments != null){
98         // for (int i = 0, max = arguments.length; i < max; i++){
99         // arguments[i].generateCode(currentScope, codeStream, true);
100         // }
101         // }
102         // // actual message invocation
103         // if (syntheticAccessor == null){
104         // if (isStatic){
105         // codeStream.invokestatic(codegenBinding);
106         // } else {
107         // if( (receiver.isSuper()) || codegenBinding.isPrivate()){
108         // codeStream.invokespecial(codegenBinding);
109         // } else {
110         // if (codegenBinding.declaringClass.isInterface()){
111         // codeStream.invokeinterface(codegenBinding);
112         // } else {
113         // codeStream.invokevirtual(codegenBinding);
114         // }
115         // }
116         // }
117         // } else {
118         // codeStream.invokestatic(syntheticAccessor);
119         // }
120         // // operation on the returned value
121         // if (valueRequired){
122         // // implicit conversion if necessary
123         // codeStream.generateImplicitConversion(implicitConversion);
124         // } else {
125         // // pop return value if any
126         // switch(binding.returnType.id){
127         // case T_long :
128         // case T_double :
129         // codeStream.pop2();
130         // break;
131         // case T_void :
132         // break;
133         // default:
134         // codeStream.pop();
135         // }
136         // }
137         // codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>>
138         // 32)); // highlight selector
139         // }
140         public boolean isSuperAccess() {
141                 return receiver.isSuper();
142         }
143
144         public boolean isTypeAccess() {
145                 return receiver != null && receiver.isTypeReference();
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
153                         // MethodBinding#canBeSeenBy)
154                         if (currentScope.enclosingSourceType() != binding.declaringClass) {
155
156                                 syntheticAccessor = ((SourceTypeBinding) binding.declaringClass)
157                                                 .addSyntheticMethod(binding, isSuperAccess());
158                                 currentScope.problemReporter().needToEmulateMethodAccess(
159                                                 binding, this);
160                                 return;
161                         }
162
163                 } else if (receiver instanceof QualifiedSuperReference) { // qualified
164                                                                                                                                         // super
165
166                         // qualified super need emulation always
167                         SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType);
168                         syntheticAccessor = destinationType.addSyntheticMethod(binding,
169                                         isSuperAccess());
170                         currentScope.problemReporter().needToEmulateMethodAccess(binding,
171                                         this);
172                         return;
173
174                 } else if (binding.isProtected()) {
175
176                         SourceTypeBinding enclosingSourceType;
177                         if (((bits & DepthMASK) != 0)
178                                         && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope
179                                                         .enclosingSourceType()).getPackage()) {
180
181                                 SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType
182                                                 .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
183                                 syntheticAccessor = currentCompatibleType.addSyntheticMethod(
184                                                 binding, isSuperAccess());
185                                 currentScope.problemReporter().needToEmulateMethodAccess(
186                                                 binding, this);
187                                 return;
188                         }
189                 }
190                 // if the binding declaring class is not visible, need special action
191                 // for runtime compatibility on 1.2 VMs : change the declaring class of
192                 // the binding
193                 // NOTE: from target 1.2 on, method's declaring class is touched if any
194                 // different from receiver type
195                 // and not from Object or implicit static method call.
196                 // if (binding.declaringClass != this.qualifyingType
197                 // && !this.qualifyingType.isArrayType()
198                 // && ((currentScope.environment().options.targetJDK >=
199                 // CompilerOptions.JDK1_2
200                 // && (!receiver.isImplicitThis() || !binding.isStatic())
201                 // && binding.declaringClass.id != T_Object) // no change for Object
202                 // methods
203                 // || !binding.declaringClass.canBeSeenBy(currentScope))) {
204                 //
205                 // this.codegenBinding =
206                 // currentScope.enclosingSourceType().getUpdatedMethodBinding(binding,
207                 // (ReferenceBinding) this.qualifyingType);
208                 // }
209         }
210
211         public StringBuffer printExpression(int indent, StringBuffer output) {
212
213                 if (!receiver.isImplicitThis())
214                         receiver.printExpression(0, output).append('.');
215                 output.append(selector).append('('); //$NON-NLS-1$
216                 if (arguments != null) {
217                         for (int i = 0; i < arguments.length; i++) {
218                                 if (i > 0)
219                                         output.append(", "); //$NON-NLS-1$
220                                 arguments[i].printExpression(0, output);
221                         }
222                 }
223                 return output.append(')');
224         }
225
226         public TypeBinding resolveType(BlockScope scope) {
227                 // Answer the signature return type
228                 // Base type promotion
229
230                 constant = NotAConstant;
231                 this.qualifyingType = this.receiverType = receiver.resolveType(scope);
232
233                 // will check for null after args are resolved
234                 TypeBinding[] argumentTypes = NoParameters;
235                 if (arguments != null) {
236                         boolean argHasError = false; // typeChecks all arguments
237                         int length = arguments.length;
238                         argumentTypes = new TypeBinding[length];
239                         for (int i = 0; i < length; i++) {
240                                 if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null) {
241                                         argHasError = true;
242                                 }
243                         }
244                         if (argHasError) {
245                                 if (receiverType instanceof ReferenceBinding) {
246                                         // record any selector match, for clients who may still need
247                                         // hint about possible method match
248                                         this.codegenBinding = this.binding = scope.findMethod(
249                                                         (ReferenceBinding) receiverType, selector,
250                                                         new TypeBinding[] {}, this);
251                                 }
252                                 return null;
253                         }
254                 }
255                 if (this.receiverType == null)
256                         return null;
257
258                 // base type cannot receive any message
259                 if (this.receiverType.isBaseType()) {
260                         scope.problemReporter().errorNoMethodFor(this, this.receiverType,
261                                         argumentTypes);
262                         return null;
263                 }
264
265                 this.codegenBinding = this.binding = receiver.isImplicitThis() ? scope
266                                 .getImplicitMethod(selector, argumentTypes, this) : scope
267                                 .getMethod(this.receiverType, selector, argumentTypes, this);
268                 if (!binding.isValidBinding()) {
269                         if (binding.declaringClass == null) {
270                                 if (this.receiverType instanceof ReferenceBinding) {
271                                         binding.declaringClass = (ReferenceBinding) this.receiverType;
272                                 } else {
273                                         scope.problemReporter().errorNoMethodFor(this,
274                                                         this.receiverType, argumentTypes);
275                                         return null;
276                                 }
277                         }
278                         scope.problemReporter().invalidMethod(this, binding);
279                         // record the closest match, for clients who may still need hint
280                         // about possible method match
281                         if (binding instanceof ProblemMethodBinding) {
282                                 MethodBinding closestMatch = ((ProblemMethodBinding) binding).closestMatch;
283                                 if (closestMatch != null)
284                                         this.codegenBinding = this.binding = closestMatch;
285                         }
286                         return binding == null ? null : binding.returnType;
287                 }
288                 if (!binding.isStatic()) {
289                         // the "receiver" must not be a type, in other words, a
290                         // NameReference that the TC has bound to a Type
291                         if (receiver instanceof NameReference
292                                         && (((NameReference) receiver).bits & BindingIds.TYPE) != 0) {
293                                 scope.problemReporter().mustUseAStaticMethod(this, binding);
294                         }
295                 } else {
296                         // static message invoked through receiver? legal but unoptimal
297                         // (optional warning).
298                         if (!(receiver.isImplicitThis() || receiver.isSuper() || (receiver instanceof NameReference && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
299                                 scope.problemReporter().unnecessaryReceiverForStaticMethod(
300                                                 this, binding);
301                         }
302                 }
303                 if (arguments != null)
304                         for (int i = 0; i < arguments.length; i++)
305                                 arguments[i].implicitWidening(binding.parameters[i],
306                                                 argumentTypes[i]);
307
308                 // -------message send that are known to fail at compile time-----------
309                 if (binding.isAbstract()) {
310                         if (receiver.isSuper()) {
311                                 scope.problemReporter().cannotDireclyInvokeAbstractMethod(this,
312                                                 binding);
313                         }
314                         // abstract private methods cannot occur nor abstract
315                         // static............
316                 }
317                 if (isMethodUseDeprecated(binding, scope))
318                         scope.problemReporter().deprecatedMethod(binding, this);
319
320                 return this.resolvedType = binding.returnType;
321         }
322
323         public void setActualReceiverType(ReferenceBinding receiverType) {
324                 this.qualifyingType = receiverType;
325         }
326
327         public void setDepth(int depth) {
328                 bits &= ~DepthMASK; // flush previous depth if any
329                 if (depth > 0) {
330                         bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
331                 }
332         }
333
334         public void setFieldIndex(int depth) {
335                 // ignore for here
336         }
337
338         public String toStringExpression() {
339
340                 String s = ""; //$NON-NLS-1$
341                 if (!receiver.isImplicitThis())
342                         s = s + receiver.toStringExpression() + "."; //$NON-NLS-1$
343                 s = s + new String(selector) + "("; //$NON-NLS-1$
344                 if (arguments != null)
345                         for (int i = 0; i < arguments.length; i++) {
346                                 s = s + arguments[i].toStringExpression();
347                                 if (i != arguments.length - 1)
348                                         s = s + " , ";};; //$NON-NLS-1$
349                 s = s + ")"; //$NON-NLS-1$
350                 return s;
351         }
352
353         public void traverse(ASTVisitor visitor, BlockScope blockScope) {
354                 if (visitor.visit(this, blockScope)) {
355                         receiver.traverse(visitor, blockScope);
356                         if (arguments != null) {
357                                 int argumentsLength = arguments.length;
358                                 for (int i = 0; i < argumentsLength; i++)
359                                         arguments[i].traverse(visitor, blockScope);
360                         }
361                 }
362                 visitor.endVisit(this, blockScope);
363         }
364 }