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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
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;
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;
31 public long nameSourcePosition ; //(start<<32)+end
33 MethodBinding syntheticAccessor;
35 public TypeBinding receiverType, qualifyingType;
37 public MessageSend() {
40 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
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();
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);
54 manageSyntheticAccessIfNecessary(currentScope);
58 * MessageSend code generation
60 * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
61 * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
62 * @param valueRequired boolean
64 //public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
66 // int pc = codeStream.position;
68 // // generate receiver/enclosing instance access
69 // boolean isStatic = codegenBinding.isStatic();
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);
77 // receiver.generateCode(currentScope, codeStream, !isStatic);
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);
85 // // actual message invocation
86 // if (syntheticAccessor == null){
88 // codeStream.invokestatic(codegenBinding);
90 // if( (receiver.isSuper()) || codegenBinding.isPrivate()){
91 // codeStream.invokespecial(codegenBinding);
93 // if (codegenBinding.declaringClass.isInterface()){
94 // codeStream.invokeinterface(codegenBinding);
96 // codeStream.invokevirtual(codegenBinding);
101 // codeStream.invokestatic(syntheticAccessor);
103 // // operation on the returned value
104 // if (valueRequired){
105 // // implicit conversion if necessary
106 // codeStream.generateImplicitConversion(implicitConversion);
108 // // pop return value if any
109 // switch(binding.returnType.id){
112 // codeStream.pop2();
120 // codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>> 32)); // highlight selector
122 public boolean isSuperAccess() {
123 return receiver.isSuper();
125 public boolean isTypeAccess() {
126 return receiver != null && receiver.isTypeReference();
128 public void manageSyntheticAccessIfNecessary(BlockScope currentScope){
130 if (binding.isPrivate()){
132 // depth is set for both implicit and explicit access (see MethodBinding#canBeSeenBy)
133 if (currentScope.enclosingSourceType() != binding.declaringClass){
135 syntheticAccessor = ((SourceTypeBinding)binding.declaringClass).addSyntheticMethod(binding, isSuperAccess());
136 currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
140 } else if (receiver instanceof QualifiedSuperReference){ // qualified super
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);
148 } else if (binding.isProtected()){
150 SourceTypeBinding enclosingSourceType;
151 if (((bits & DepthMASK) != 0)
152 && binding.declaringClass.getPackage()
153 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()){
155 SourceTypeBinding currentCompatibleType = (SourceTypeBinding)enclosingSourceType.enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
156 syntheticAccessor = currentCompatibleType.addSyntheticMethod(binding, isSuperAccess());
157 currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
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))) {
172 // this.codegenBinding = currentScope.enclosingSourceType().getUpdatedMethodBinding(binding, (ReferenceBinding) this.qualifyingType);
175 public StringBuffer printExpression(int indent, StringBuffer output){
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);
185 return output.append(')');
187 public TypeBinding resolveType(BlockScope scope) {
188 // Answer the signature return type
189 // Base type promotion
191 constant = NotAConstant;
192 this.qualifyingType = this.receiverType = receiver.resolveType(scope);
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){
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);
213 if (this.receiverType == null)
216 // base type cannot receive any message
217 if (this.receiverType.isBaseType()) {
218 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
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;
231 scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes);
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;
241 return binding == null ? null : binding.returnType;
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);
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);
258 if (arguments != null)
259 for (int i = 0; i < arguments.length; i++)
260 arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]);
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);
267 // abstract private methods cannot occur nor abstract static............
269 if (isMethodUseDeprecated(binding, scope))
270 scope.problemReporter().deprecatedMethod(binding, this);
272 return this.resolvedType = binding.returnType;
274 public void setActualReceiverType(ReferenceBinding receiverType) {
275 this.qualifyingType = receiverType;
277 public void setDepth(int depth) {
278 bits &= ~DepthMASK; // flush previous depth if any
280 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
283 public void setFieldIndex(int depth) {
287 public String toStringExpression(){
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$
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);
310 visitor.endVisit(this, blockScope);