/* ******************************************************************* * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). * 2004 contributors * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v1.0 * which accompanies this distribution and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * PARC initial implementation * IBM ongoing maintenance * ******************************************************************/ package org.aspectj.ajdt.internal.compiler.ast; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CastExpression; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant; import org.aspectj.org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Scope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.aspectj.weaver.AdviceKind; /** * Used to represent any method call to a method named <code>proceed</code>. During * <code>resolvedType</code> it will be determined if this is actually in the body * of an <code>around</code> advice and has no receiver (must be a bare proceed call, * see pr 53981), and if not this will be treated like any other * MessageSend. * * @author Jim Hugunin */ 00039 public class Proceed extends MessageSend { public boolean inInner = false; public Proceed(MessageSend parent) { super(); this.receiver = parent.receiver; this.selector = parent.selector; this.arguments = parent.arguments; this.binding = parent.binding; this.codegenBinding = parent.codegenBinding; this.syntheticAccessor = parent.syntheticAccessor; this.expectedType = parent.expectedType; this.nameSourcePosition = parent.nameSourcePosition; this.actualReceiverType = parent.actualReceiverType; //this.qualifyingType = parent.qualifyingType; this.valueCast = parent.valueCast; this.typeArguments = parent.typeArguments; this.genericTypeArguments = parent.genericTypeArguments; this.sourceStart = parent.sourceStart; this.sourceEnd = parent.sourceEnd; } public TypeBinding resolveType(BlockScope scope) { // find out if I'm really in an around body or not //??? this could in theory be done by the parser, but that appears to be hard AdviceDeclaration aroundDecl = findEnclosingAround(scope); if (aroundDecl == null) { return super.resolveType(scope); } constant = Constant.NotAConstant; binding = codegenBinding = aroundDecl.proceedMethodBinding; this.actualReceiverType = binding.declaringClass; int baseArgCount = 0; if (arguments != null) { baseArgCount = arguments.length; Expression[] newArguments = new Expression[baseArgCount + 1]; System.arraycopy(arguments, 0, newArguments, 0, baseArgCount); arguments = newArguments; } else { arguments = new Expression[1]; } arguments[baseArgCount] = AstUtil.makeLocalVariableReference(aroundDecl.extraArgument.binding); int declaredParameterCount = aroundDecl.getDeclaredParameterCount(); if (baseArgCount < declaredParameterCount) { scope.problemReporter().signalError(this.sourceStart, this.sourceEnd, "too few arguments to proceed, expected " + declaredParameterCount); aroundDecl.ignoreFurtherInvestigation = true; return null; //binding.returnType; } if (baseArgCount > declaredParameterCount) { scope.problemReporter().signalError(this.sourceStart, this.sourceEnd, "too many arguments to proceed, expected " + declaredParameterCount); aroundDecl.ignoreFurtherInvestigation = true; return null; //binding.returnType; } boolean argsContainCast = false; for (int i=0; i<arguments.length;i++) { if (arguments[i] instanceof CastExpression) argsContainCast = true; // if (arguments[i].constant==null) arguments[i].constant=Constant.NotAConstant; } // TypeBinding[] argumentTypes = Binding.NO_PARAMETERS; // if (this.arguments != null) { // boolean argHasError = false; // typeChecks all arguments // int length = this.arguments.length; // argumentTypes = new TypeBinding[length]; // for (int i = 0; i < length; i++){ // Expression argument = this.arguments[i]; // if (argument instanceof CastExpression) { // argument.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on // argsContainCast = true; // } // if ((argumentTypes[i] = argument.resolveType(scope)) == null){ // argHasError = true; // } // } // if (argHasError) { // if (this.actualReceiverType instanceof ReferenceBinding) { // // record a best guess, for clients who need hint about possible method match // TypeBinding[] pseudoArgs = new TypeBinding[length]; // for (int i = length; --i >= 0;) // pseudoArgs[i] = argumentTypes[i] == null ? TypeBinding.NULL : argumentTypes[i]; // replace args with errors with null type // this.binding = // this.receiver.isImplicitThis() // ? scope.getImplicitMethod(this.selector, pseudoArgs, this) // : scope.findMethod((ReferenceBinding) this.actualReceiverType, this.selector, pseudoArgs, this); // if (this.binding != null && !this.binding.isValidBinding()) { // MethodBinding closestMatch = ((ProblemMethodBinding)this.binding).closestMatch; // // record the closest match, for clients who may still need hint about possible method match // if (closestMatch != null) { // if (closestMatch.original().typeVariables != Binding.NO_TYPE_VARIABLES) { // generic method // // shouldn't return generic method outside its context, rather convert it to raw method (175409) // closestMatch = scope.environment().createParameterizedGenericMethod(closestMatch.original(), (RawTypeBinding)null); // } // this.binding = closestMatch; // MethodBinding closestMatchOriginal = closestMatch.original(); // if ((closestMatchOriginal.isPrivate() || closestMatchOriginal.declaringClass.isLocalType()) && !scope.isDefinedInMethod(closestMatchOriginal)) { // // ignore cases where method is used from within inside itself (e.g. direct recursions) // closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // } // } // } // } // return null; // } // } // // checkInvocationArguments(scope, this.receiver, this.actualReceiverType, this.binding, this.arguments, argumentTypes, argsContainCast, this); for (int i=0, len=arguments.length; i < len; i++) { Expression arg = arguments[i]; TypeBinding argType = arg.resolveType(scope); if (argType != null) { TypeBinding paramType = binding.parameters[i]; if (!argType.isCompatibleWith(paramType)) { scope.problemReporter().typeMismatchError(argType, paramType, arg); } } } checkInvocationArguments(scope,null,this.actualReceiverType,binding, this.arguments,binding.parameters,argsContainCast,this); return binding.returnType; } private AdviceDeclaration findEnclosingAround(Scope scope) { if (scope == null) return null; if (scope instanceof MethodScope) { MethodScope methodScope = (MethodScope)scope; ReferenceContext context = methodScope.referenceContext; if (context instanceof AdviceDeclaration) { AdviceDeclaration adviceDecl = (AdviceDeclaration)context; if (adviceDecl.kind == AdviceKind.Around) { // pr 53981 only match "bare" calls to proceed if((receiver != null) && (!receiver.isThis())) { return null; } adviceDecl.proceedCalls.add(this); return adviceDecl; } else { return null; } } } else if (scope instanceof ClassScope) { inInner = true; } return findEnclosingAround(scope.parent); } }