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;
28 public char[] selector;
30 public Expression[] arguments;
32 public MethodBinding binding, codegenBinding;
34 public long nameSourcePosition; // (start<<32)+end
36 MethodBinding syntheticAccessor;
38 public TypeBinding receiverType, qualifyingType;
40 public MessageSend() {
44 public FlowInfo analyseCode(BlockScope currentScope,
45 FlowContext flowContext, FlowInfo flowInfo) {
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();
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);
63 manageSyntheticAccessIfNecessary(currentScope);
68 * MessageSend code generation
71 * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
73 * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
74 * @param valueRequired
77 // public void generateCode(BlockScope currentScope, CodeStream codeStream,
78 // boolean valueRequired) {
80 // int pc = codeStream.position;
82 // // generate receiver/enclosing instance access
83 // boolean isStatic = codegenBinding.isStatic();
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) >>
90 // Object[] path = currentScope.getEmulationPath(targetType, true /*only
91 // exact match*/, false/*consider enclosing arg*/);
92 // codeStream.generateOuterAccess(path, this, targetType, currentScope);
94 // receiver.generateCode(currentScope, codeStream, !isStatic);
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);
102 // // actual message invocation
103 // if (syntheticAccessor == null){
105 // codeStream.invokestatic(codegenBinding);
107 // if( (receiver.isSuper()) || codegenBinding.isPrivate()){
108 // codeStream.invokespecial(codegenBinding);
110 // if (codegenBinding.declaringClass.isInterface()){
111 // codeStream.invokeinterface(codegenBinding);
113 // codeStream.invokevirtual(codegenBinding);
118 // codeStream.invokestatic(syntheticAccessor);
120 // // operation on the returned value
121 // if (valueRequired){
122 // // implicit conversion if necessary
123 // codeStream.generateImplicitConversion(implicitConversion);
125 // // pop return value if any
126 // switch(binding.returnType.id){
129 // codeStream.pop2();
137 // codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>>
138 // 32)); // highlight selector
140 public boolean isSuperAccess() {
141 return receiver.isSuper();
144 public boolean isTypeAccess() {
145 return receiver != null && receiver.isTypeReference();
148 public void manageSyntheticAccessIfNecessary(BlockScope currentScope) {
150 if (binding.isPrivate()) {
152 // depth is set for both implicit and explicit access (see
153 // MethodBinding#canBeSeenBy)
154 if (currentScope.enclosingSourceType() != binding.declaringClass) {
156 syntheticAccessor = ((SourceTypeBinding) binding.declaringClass)
157 .addSyntheticMethod(binding, isSuperAccess());
158 currentScope.problemReporter().needToEmulateMethodAccess(
163 } else if (receiver instanceof QualifiedSuperReference) { // qualified
166 // qualified super need emulation always
167 SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType);
168 syntheticAccessor = destinationType.addSyntheticMethod(binding,
170 currentScope.problemReporter().needToEmulateMethodAccess(binding,
174 } else if (binding.isProtected()) {
176 SourceTypeBinding enclosingSourceType;
177 if (((bits & DepthMASK) != 0)
178 && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope
179 .enclosingSourceType()).getPackage()) {
181 SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType
182 .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
183 syntheticAccessor = currentCompatibleType.addSyntheticMethod(
184 binding, isSuperAccess());
185 currentScope.problemReporter().needToEmulateMethodAccess(
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
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
203 // || !binding.declaringClass.canBeSeenBy(currentScope))) {
205 // this.codegenBinding =
206 // currentScope.enclosingSourceType().getUpdatedMethodBinding(binding,
207 // (ReferenceBinding) this.qualifyingType);
211 public StringBuffer printExpression(int indent, StringBuffer output) {
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++) {
219 output.append(", "); //$NON-NLS-1$
220 arguments[i].printExpression(0, output);
223 return output.append(')');
226 public TypeBinding resolveType(BlockScope scope) {
227 // Answer the signature return type
228 // Base type promotion
230 constant = NotAConstant;
231 this.qualifyingType = this.receiverType = receiver.resolveType(scope);
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) {
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);
255 if (this.receiverType == null)
258 // base type cannot receive any message
259 if (this.receiverType.isBaseType()) {
260 scope.problemReporter().errorNoMethodFor(this, this.receiverType,
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;
273 scope.problemReporter().errorNoMethodFor(this,
274 this.receiverType, argumentTypes);
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;
286 return binding == null ? null : binding.returnType;
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);
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(
303 if (arguments != null)
304 for (int i = 0; i < arguments.length; i++)
305 arguments[i].implicitWidening(binding.parameters[i],
308 // -------message send that are known to fail at compile time-----------
309 if (binding.isAbstract()) {
310 if (receiver.isSuper()) {
311 scope.problemReporter().cannotDireclyInvokeAbstractMethod(this,
314 // abstract private methods cannot occur nor abstract
315 // static............
317 if (isMethodUseDeprecated(binding, scope))
318 scope.problemReporter().deprecatedMethod(binding, this);
320 return this.resolvedType = binding.returnType;
323 public void setActualReceiverType(ReferenceBinding receiverType) {
324 this.qualifyingType = receiverType;
327 public void setDepth(int depth) {
328 bits &= ~DepthMASK; // flush previous depth if any
330 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
334 public void setFieldIndex(int depth) {
338 public String toStringExpression() {
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$
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);
362 visitor.endVisit(this, blockScope);