/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package net.sourceforge.phpdt.internal.compiler.ast; import net.sourceforge.phpdt.internal.compiler.ASTVisitor; import net.sourceforge.phpdt.internal.compiler.flow.FlowContext; import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo; import net.sourceforge.phpdt.internal.compiler.lookup.BindingIds; import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; import net.sourceforge.phpdt.internal.compiler.lookup.InvocationSite; import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding; import net.sourceforge.phpdt.internal.compiler.lookup.ProblemMethodBinding; import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding; import net.sourceforge.phpdt.internal.compiler.lookup.SourceTypeBinding; import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding; public class MessageSend extends Expression implements InvocationSite { public Expression receiver; public char[] selector; public Expression[] arguments; public MethodBinding binding, codegenBinding; public long nameSourcePosition; // (start<<32)+end MethodBinding syntheticAccessor; public TypeBinding receiverType, qualifyingType; public MessageSend() { } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic()).unconditionalInits(); if (arguments != null) { int length = arguments.length; for (int i = 0; i < length; i++) { flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); } } ReferenceBinding[] thrownExceptions; if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) { // must verify that exceptions potentially thrown by this expression // are caught in the method flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope); } manageSyntheticAccessIfNecessary(currentScope); return flowInfo; } /** * MessageSend code generation * * @param currentScope * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope * @param codeStream * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream * @param valueRequired * boolean */ // public void generateCode(BlockScope currentScope, CodeStream codeStream, // boolean valueRequired) { // // int pc = codeStream.position; // // // generate receiver/enclosing instance access // boolean isStatic = codegenBinding.isStatic(); // // outer access ? // if (!isStatic && ((bits & DepthMASK) != 0) && receiver.isImplicitThis()){ // // outer method can be reached through emulation if implicit access // ReferenceBinding targetType = // currentScope.enclosingSourceType().enclosingTypeAt((bits & DepthMASK) >> // DepthSHIFT); // Object[] path = currentScope.getEmulationPath(targetType, true /*only // exact match*/, false/*consider enclosing arg*/); // codeStream.generateOuterAccess(path, this, targetType, currentScope); // } else { // receiver.generateCode(currentScope, codeStream, !isStatic); // } // // generate arguments // if (arguments != null){ // for (int i = 0, max = arguments.length; i < max; i++){ // arguments[i].generateCode(currentScope, codeStream, true); // } // } // // actual message invocation // if (syntheticAccessor == null){ // if (isStatic){ // codeStream.invokestatic(codegenBinding); // } else { // if( (receiver.isSuper()) || codegenBinding.isPrivate()){ // codeStream.invokespecial(codegenBinding); // } else { // if (codegenBinding.declaringClass.isInterface()){ // codeStream.invokeinterface(codegenBinding); // } else { // codeStream.invokevirtual(codegenBinding); // } // } // } // } else { // codeStream.invokestatic(syntheticAccessor); // } // // operation on the returned value // if (valueRequired){ // // implicit conversion if necessary // codeStream.generateImplicitConversion(implicitConversion); // } else { // // pop return value if any // switch(binding.returnType.id){ // case T_long : // case T_double : // codeStream.pop2(); // break; // case T_void : // break; // default: // codeStream.pop(); // } // } // codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>> // 32)); // highlight selector // } public boolean isSuperAccess() { return receiver.isSuper(); } public boolean isTypeAccess() { return receiver != null && receiver.isTypeReference(); } public void manageSyntheticAccessIfNecessary(BlockScope currentScope) { if (binding.isPrivate()) { // depth is set for both implicit and explicit access (see // MethodBinding#canBeSeenBy) if (currentScope.enclosingSourceType() != binding.declaringClass) { syntheticAccessor = ((SourceTypeBinding) binding.declaringClass) .addSyntheticMethod(binding, isSuperAccess()); currentScope.problemReporter().needToEmulateMethodAccess( binding, this); return; } } else if (receiver instanceof QualifiedSuperReference) { // qualified // super // qualified super need emulation always SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType); syntheticAccessor = destinationType.addSyntheticMethod(binding, isSuperAccess()); currentScope.problemReporter().needToEmulateMethodAccess(binding, this); return; } else if (binding.isProtected()) { SourceTypeBinding enclosingSourceType; if (((bits & DepthMASK) != 0) && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope .enclosingSourceType()).getPackage()) { SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT); syntheticAccessor = currentCompatibleType.addSyntheticMethod( binding, isSuperAccess()); currentScope.problemReporter().needToEmulateMethodAccess( binding, this); return; } } // if the binding declaring class is not visible, need special action // for runtime compatibility on 1.2 VMs : change the declaring class of // the binding // NOTE: from target 1.2 on, method's declaring class is touched if any // different from receiver type // and not from Object or implicit static method call. // if (binding.declaringClass != this.qualifyingType // && !this.qualifyingType.isArrayType() // && ((currentScope.environment().options.targetJDK >= // CompilerOptions.JDK1_2 // && (!receiver.isImplicitThis() || !binding.isStatic()) // && binding.declaringClass.id != T_Object) // no change for Object // methods // || !binding.declaringClass.canBeSeenBy(currentScope))) { // // this.codegenBinding = // currentScope.enclosingSourceType().getUpdatedMethodBinding(binding, // (ReferenceBinding) this.qualifyingType); // } } public StringBuffer printExpression(int indent, StringBuffer output) { if (!receiver.isImplicitThis()) receiver.printExpression(0, output).append('.'); output.append(selector).append('('); //$NON-NLS-1$ if (arguments != null) { for (int i = 0; i < arguments.length; i++) { if (i > 0) output.append(", "); //$NON-NLS-1$ arguments[i].printExpression(0, output); } } return output.append(')'); } public TypeBinding resolveType(BlockScope scope) { // Answer the signature return type // Base type promotion constant = NotAConstant; this.qualifyingType = this.receiverType = receiver.resolveType(scope); // will check for null after args are resolved TypeBinding[] argumentTypes = NoParameters; if (arguments != null) { boolean argHasError = false; // typeChecks all arguments int length = arguments.length; argumentTypes = new TypeBinding[length]; for (int i = 0; i < length; i++) { if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null) { argHasError = true; } } if (argHasError) { if (receiverType instanceof ReferenceBinding) { // record any selector match, for clients who may still need // hint about possible method match this.codegenBinding = this.binding = scope.findMethod( (ReferenceBinding) receiverType, selector, new TypeBinding[] {}, this); } return null; } } if (this.receiverType == null) return null; // base type cannot receive any message if (this.receiverType.isBaseType()) { scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes); return null; } this.codegenBinding = this.binding = receiver.isImplicitThis() ? scope .getImplicitMethod(selector, argumentTypes, this) : scope .getMethod(this.receiverType, selector, argumentTypes, this); if (!binding.isValidBinding()) { if (binding.declaringClass == null) { if (this.receiverType instanceof ReferenceBinding) { binding.declaringClass = (ReferenceBinding) this.receiverType; } else { scope.problemReporter().errorNoMethodFor(this, this.receiverType, argumentTypes); return null; } } scope.problemReporter().invalidMethod(this, binding); // record the closest match, for clients who may still need hint // about possible method match if (binding instanceof ProblemMethodBinding) { MethodBinding closestMatch = ((ProblemMethodBinding) binding).closestMatch; if (closestMatch != null) this.codegenBinding = this.binding = closestMatch; } return binding == null ? null : binding.returnType; } if (!binding.isStatic()) { // the "receiver" must not be a type, in other words, a // NameReference that the TC has bound to a Type if (receiver instanceof NameReference && (((NameReference) receiver).bits & BindingIds.TYPE) != 0) { scope.problemReporter().mustUseAStaticMethod(this, binding); } } else { // static message invoked through receiver? legal but unoptimal // (optional warning). if (!(receiver.isImplicitThis() || receiver.isSuper() || (receiver instanceof NameReference && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) { scope.problemReporter().unnecessaryReceiverForStaticMethod( this, binding); } } if (arguments != null) for (int i = 0; i < arguments.length; i++) arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]); // -------message send that are known to fail at compile time----------- if (binding.isAbstract()) { if (receiver.isSuper()) { scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, binding); } // abstract private methods cannot occur nor abstract // static............ } if (isMethodUseDeprecated(binding, scope)) scope.problemReporter().deprecatedMethod(binding, this); return this.resolvedType = binding.returnType; } public void setActualReceiverType(ReferenceBinding receiverType) { this.qualifyingType = receiverType; } public void setDepth(int depth) { bits &= ~DepthMASK; // flush previous depth if any if (depth > 0) { bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits } } public void setFieldIndex(int depth) { // ignore for here } public String toStringExpression() { String s = ""; //$NON-NLS-1$ if (!receiver.isImplicitThis()) s = s + receiver.toStringExpression() + "."; //$NON-NLS-1$ s = s + new String(selector) + "("; //$NON-NLS-1$ if (arguments != null) for (int i = 0; i < arguments.length; i++) { s = s + arguments[i].toStringExpression(); if (i != arguments.length - 1) s = s + " , ";};; //$NON-NLS-1$ s = s + ")"; //$NON-NLS-1$ return s; } public void traverse(ASTVisitor visitor, BlockScope blockScope) { if (visitor.visit(this, blockScope)) { receiver.traverse(visitor, blockScope); if (arguments != null) { int argumentsLength = arguments.length; for (int i = 0; i < argumentsLength; i++) arguments[i].traverse(visitor, blockScope); } } visitor.endVisit(this, blockScope); } }