All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.org.eclipse.jdt.internal.core.search.matching.MethodLocator Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 2019 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Samrat Dhillon [email protected] - Search for method references is
 *               returning methods as overriden even if the superclass's method is
 *               only package-visible - https://bugs.eclipse.org/357547
 *******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.core.search.matching;

import static org.aspectj.org.eclipse.jdt.internal.core.JavaModelManager.trace;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.*;
import org.aspectj.org.eclipse.jdt.core.*;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.search.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.aspectj.org.eclipse.jdt.internal.core.BinaryMethod;
import org.aspectj.org.eclipse.jdt.internal.core.search.BasicSearchEngine;

public class MethodLocator extends PatternLocator {

protected MethodPattern pattern;
protected boolean isDeclarationOfReferencedMethodsPattern;

//extra reference info
public char[][][] allSuperDeclaringTypeNames;

// This is set only if focus is null. In these cases
// it will be hard to determine if the super class is of the same package
// at a latter point. Hence, this array is created with all the super class
// names of the same package name as of the matching class name.
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=357547
private char[][][] samePkgSuperDeclaringTypeNames;

private MatchLocator matchLocator;
//method declarations which parameters verification fail
private Map methodDeclarationsWithInvalidParam = new HashMap<>();


public MethodLocator(MethodPattern pattern) {
	super(pattern);

	this.pattern = pattern;
	this.isDeclarationOfReferencedMethodsPattern = this.pattern instanceof DeclarationOfReferencedMethodsPattern;
}
/*
 * Clear caches
 */
@Override
protected void clear() {
	this.methodDeclarationsWithInvalidParam = new HashMap<>();
}
@Override
protected int fineGrain() {
	return this.pattern.fineGrain;
}

private ReferenceBinding getMatchingSuper(ReferenceBinding binding) {
	if (binding == null) return null;
	ReferenceBinding superBinding = binding.superclass();
	int level = resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, superBinding);
	if (level != IMPOSSIBLE_MATCH) return superBinding;
	// matches superclass
	if (!binding.isInterface() && !CharOperation.equals(binding.compoundName, TypeConstants.JAVA_LANG_OBJECT)) {
		superBinding = getMatchingSuper(superBinding);
		if (superBinding != null) return superBinding;
	}
	// matches interfaces
	ReferenceBinding[] interfaces = binding.superInterfaces();
	if (interfaces == null) return null;
	for (int i = 0; i < interfaces.length; i++) {
		level = resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, interfaces[i]);
		if (level != IMPOSSIBLE_MATCH) return interfaces[i];
		superBinding = getMatchingSuper(interfaces[i]);
		if (superBinding != null) return superBinding;
	}
	return null;
}

private MethodBinding getMethodBinding(ReferenceBinding type, char[] methodName, TypeBinding[] argumentTypes) {
	MethodBinding[] methods = type.getMethods(methodName);
	MethodBinding method = null;
	methodsLoop: for (int i=0, length=methods.length; i newLevel) {
			if (newLevel == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH;
			level = newLevel; // can only be downgraded
		}
	}

	// parameter types
	int parameterCount = this.pattern.parameterSimpleNames == null ? -1 : this.pattern.parameterSimpleNames.length;
	if (parameterCount > -1) {
		// global verification
		if (method.parameters == null) return INACCURATE_MATCH;
		if (parameterCount != method.parameters.length) return IMPOSSIBLE_MATCH;
		if (!method.isValidBinding() && ((ProblemMethodBinding)method).problemId() == ProblemReasons.Ambiguous) {
			// return inaccurate match for ambiguous call (bug 80890)
			return INACCURATE_MATCH;
		}
		boolean foundTypeVariable = false;
		MethodBinding focusMethodBinding = null;
		boolean checkedFocus = false;
		boolean isBinary = this.pattern!= null && this.pattern.focus instanceof BinaryMethod;
		// verify each parameter
		for (int i = 0; i < parameterCount; i++) {
			TypeBinding argType = method.parameters[i];
			int newLevel = IMPOSSIBLE_MATCH;
			boolean foundLevel = false;
			if (argType.isMemberType() || this.pattern.parameterQualifications[i] != null) {
				if (!checkedFocus) {
					focusMethodBinding = this.matchLocator.getMethodBinding(this.pattern);
					checkedFocus = true;
				}
				if (focusMethodBinding != null) {// textual comparison insufficient
					TypeBinding[] parameters = focusMethodBinding.parameters;
					if (parameters.length >= parameterCount) {
						newLevel = (isBinary ? argType.erasure().isEquivalentTo((parameters[i].erasure())) :argType.isEquivalentTo((parameters[i]))) ?
								ACCURATE_MATCH : IMPOSSIBLE_MATCH;
						foundLevel = true;
					}
				}
			} else {
				// TODO (frederic) use this call to refine accuracy on parameter types
//				 newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, argType);
				newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], argType);
			}
			if (level > newLevel) {
				if (newLevel == IMPOSSIBLE_MATCH) {
					if (skipImpossibleArg) {
						// Do not consider match as impossible while finding declarations and source level >= 1.5
					 	// (see  bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763)
						if (!foundLevel) {
							newLevel = level;
						}
					} else if (argType.isTypeVariable()) {
						newLevel = level;
						foundTypeVariable = true;
					} else {
						return IMPOSSIBLE_MATCH;
					}
				}
				level = newLevel; // can only be downgraded
			}
		}
		if (foundTypeVariable) {
			if (!method.isStatic() && !method.isPrivate()) {
				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=123836, No point in textually comparing type variables, captures etc with concrete types.
				if (!checkedFocus)
					focusMethodBinding = this.matchLocator.getMethodBinding(this.pattern);
				if (focusMethodBinding != null) {
					if (matchOverriddenMethod(focusMethodBinding.declaringClass, focusMethodBinding, method)) {
						return ACCURATE_MATCH;
					}
				}
			}
			return IMPOSSIBLE_MATCH;
		}
	}

	return level;
}
// This works for only methods of parameterized types.
private boolean matchOverriddenMethod(ReferenceBinding type, MethodBinding method, MethodBinding matchMethod) {
	if (type == null || this.pattern.selector == null) return false;

	// matches superclass
	if (!type.isInterface() && !CharOperation.equals(type.compoundName, TypeConstants.JAVA_LANG_OBJECT)) {
		ReferenceBinding superClass = type.superclass();
		if (superClass.isParameterizedType()) {
			MethodBinding[] methods = superClass.getMethods(this.pattern.selector);
			int length = methods.length;
			for (int i = 0; i>> 32);
	this.match.setOffset(offset);
	this.match.setLength(messageSend.sourceEnd - offset + 1);
	 if (isParameterized && this.pattern.hasMethodArguments())  {
		locator.reportAccurateParameterizedMethodReference(this.match, messageSend, messageSend.typeArguments);
	} else {
		locator.report(this.match);
	}
}
/*
 * Return whether method parameters are equals to pattern ones.
 */
private boolean methodParametersEqualsPattern(MethodBinding method) {
	TypeBinding[] methodParameters = method.parameters;

	int length = methodParameters.length;
	if (length != this.pattern.parameterSimpleNames.length) return false;

	for (int i = 0; i < length; i++) {
		char[] paramQualifiedName = qualifiedPattern(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i]);
		if (!CharOperation.match(paramQualifiedName, methodParameters[i].readableName(), this.isCaseSensitive)) {
			return false;
		}
	}
	return true;
}
@Override
public SearchMatch newDeclarationMatch(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, int length, MatchLocator locator) {
	if (elementBinding != null) {
		MethodBinding methodBinding = (MethodBinding) elementBinding;
		// If method parameters verification was not valid, then try to see if method arguments can match a method in hierarchy
		if (this.methodDeclarationsWithInvalidParam.containsKey(reference)) {
			// First see if this reference has already been resolved => report match if validated
			Boolean report = this.methodDeclarationsWithInvalidParam.get(reference);
			if (report != null) {
				if (report.booleanValue()) {
					return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator);
				}
				return null;
			}
			if (matchOverriddenMethod(methodBinding.declaringClass, methodBinding, null)) {
				this.methodDeclarationsWithInvalidParam.put(reference, Boolean.TRUE);
				return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator);
			}
			if (isTypeInSuperDeclaringTypeNames(methodBinding.declaringClass.compoundName)) {
				MethodBinding patternBinding = locator.getMethodBinding(this.pattern);
				if (patternBinding != null) {
					if (!matchOverriddenMethod(patternBinding.declaringClass, patternBinding, methodBinding)) {
						this.methodDeclarationsWithInvalidParam.put(reference, Boolean.FALSE);
						return null;
					}
				}
				this.methodDeclarationsWithInvalidParam.put(reference, Boolean.TRUE);
				return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator);
			}
			this.methodDeclarationsWithInvalidParam.put(reference, Boolean.FALSE);
			return null;
		}
	}
	return super.newDeclarationMatch(reference, element, elementBinding, accuracy, length, locator);
}
@Override
protected int referenceType() {
	return IJavaElement.METHOD;
}
protected void reportDeclaration(MethodBinding methodBinding, MatchLocator locator, SimpleSet knownMethods) throws CoreException {
	ReferenceBinding declaringClass = methodBinding.declaringClass;
	IType type = locator.lookupType(declaringClass);
	if (type == null) return; // case of a secondary type

	// Report match for binary
	if (type.isBinary()) {
		IMethod method = null;
		TypeBinding[] parameters = methodBinding.original().parameters;
		int parameterLength = parameters.length;
		char[][] parameterTypes = new char[parameterLength][];
		for (int i = 0; i (declaringLevel & MATCH_LEVEL_MASK) ? declaringLevel : methodLevel; // return the weaker match
}
protected int resolveLevel(MessageSend messageSend) {
	MethodBinding method = messageSend.binding;
	if (method == null) {
		return INACCURATE_MATCH;
	}
	if (messageSend.resolvedType == null) {
		// Closest match may have different argument numbers when ProblemReason is NotFound
		// see MessageSend#resolveType(BlockScope)
		// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=97322
		int argLength = messageSend.arguments == null ? 0 : messageSend.arguments.length;
		if (this.pattern.parameterSimpleNames == null || argLength == this.pattern.parameterSimpleNames.length) {
			return INACCURATE_MATCH;
		}
		return IMPOSSIBLE_MATCH;
	}

	int methodLevel = matchMethod(method, false);
	if (methodLevel == IMPOSSIBLE_MATCH) {
		if (method != method.original()) methodLevel = matchMethod(method.original(), false);
		if (methodLevel == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH;
		method = method.original();
	}

	// receiver type
	if (this.pattern.declaringSimpleName == null && this.pattern.declaringQualification == null) return methodLevel; // since any declaring class will do

	int declaringLevel;
	if (isVirtualInvoke(method, messageSend) && (messageSend.actualReceiverType instanceof ReferenceBinding)) {
		ReferenceBinding methodReceiverType = (ReferenceBinding) messageSend.actualReceiverType;
		declaringLevel = resolveLevelAsSubtype(this.pattern.declaringSimpleName, this.pattern.declaringQualification, methodReceiverType, method.selector, method.parameters, methodReceiverType.qualifiedPackageName(), method.isDefault());
		if (declaringLevel == IMPOSSIBLE_MATCH) {
			if (method.declaringClass == null || this.allSuperDeclaringTypeNames == null) {
				declaringLevel = INACCURATE_MATCH;
			} else {
				char[][][] superTypeNames = (method.isDefault() && this.pattern.focus == null) ? this.samePkgSuperDeclaringTypeNames: this.allSuperDeclaringTypeNames;
				if (superTypeNames != null && resolveLevelAsSuperInvocation(methodReceiverType, method.parameters, superTypeNames, true)) {
						declaringLevel = methodLevel // since this is an ACCURATE_MATCH so return the possibly weaker match
							| SUPER_INVOCATION_FLAVOR; // this is an overridden method => add flavor to returned level
				}
			}
		}
		if ((declaringLevel & FLAVORS_MASK) != 0) {
			// level got some flavors => return it
			return declaringLevel;
		}
	} else {
		declaringLevel = resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, method.declaringClass);
	}
	return (methodLevel & MATCH_LEVEL_MASK) > (declaringLevel & MATCH_LEVEL_MASK) ? declaringLevel : methodLevel; // return the weaker match
}

protected int resolveLevel(ReferenceExpression referenceExpression) {
	MethodBinding method = referenceExpression.getMethodBinding();
	if (method == null || !method.isValidBinding())
		return INACCURATE_MATCH;

	int methodLevel = matchMethod(method, false);
	if (methodLevel == IMPOSSIBLE_MATCH) {
		if (method != method.original()) methodLevel = matchMethod(method.original(), false);
		if (methodLevel == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH;
		method = method.original();
	}

	// receiver type
	if (this.pattern.declaringSimpleName == null && this.pattern.declaringQualification == null) return methodLevel; // since any declaring class will do
	int declaringLevel;
	ReferenceBinding ref = checkMethodRef(method, referenceExpression);
	if (ref != null) {
		declaringLevel = resolveLevelAsSubtype(this.pattern.declaringSimpleName, this.pattern.declaringQualification, ref, method.selector, method.parameters, ref.qualifiedPackageName(), method.isDefault());
		if (declaringLevel == IMPOSSIBLE_MATCH) {
			if (method.declaringClass == null || this.allSuperDeclaringTypeNames == null) {
				declaringLevel = INACCURATE_MATCH;
			} else {
				char[][][] superTypeNames = (method.isDefault() && this.pattern.focus == null) ? this.samePkgSuperDeclaringTypeNames: this.allSuperDeclaringTypeNames;
				if (superTypeNames != null && resolveLevelAsSuperInvocation(ref, method.parameters, superTypeNames, true)) {
						declaringLevel = methodLevel // since this is an ACCURATE_MATCH so return the possibly weaker match
							| SUPER_INVOCATION_FLAVOR; // TODO: not an invocation really but ref -> add flavor to returned level
				}
			}
		}
		if ((declaringLevel & FLAVORS_MASK) != 0) {
			// level got some flavors => return it
			return declaringLevel;
		}
	} else {
		declaringLevel = resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, method.declaringClass);
	}
	return (methodLevel & MATCH_LEVEL_MASK) > (declaringLevel & MATCH_LEVEL_MASK) ? declaringLevel : methodLevel; // return the weaker match
}

/**
 * Returns whether the given reference type binding matches or is a subtype of a type
 * that matches the given qualified pattern.
 * Returns ACCURATE_MATCH if it does.
 * Returns INACCURATE_MATCH if resolve fails
 * Returns IMPOSSIBLE_MATCH if it doesn't.
 */
protected int resolveLevelAsSubtype(char[] simplePattern, char[] qualifiedPattern, ReferenceBinding type, char[] methodName, TypeBinding[] argumentTypes, char[] packageName, boolean isDefault) {
	if (type == null) return INACCURATE_MATCH;

	int level = resolveLevelForType(simplePattern, qualifiedPattern, type);
	if (level != IMPOSSIBLE_MATCH) {
		if (isDefault && !CharOperation.equals(packageName, type.qualifiedPackageName())) {
			return IMPOSSIBLE_MATCH;
		}
		MethodBinding method = argumentTypes == null ? null : getMethodBinding(type, methodName, argumentTypes);
		if (((method != null && !method.isAbstract()) || !type.isAbstract()) && !type.isInterface()) { // if concrete, then method is overridden
			level |= OVERRIDDEN_METHOD_FLAVOR;
		}
		return level;
	}

	// matches superclass
	if (!type.isInterface() && !CharOperation.equals(type.compoundName, TypeConstants.JAVA_LANG_OBJECT)) {
		level = resolveLevelAsSubtype(simplePattern, qualifiedPattern, type.superclass(), methodName, argumentTypes, packageName, isDefault);
		if (level != IMPOSSIBLE_MATCH) {
			if (argumentTypes != null) {
				// need to verify if method may be overridden
				MethodBinding method = getMethodBinding(type, methodName, argumentTypes);
				if (method != null) { // one method match in hierarchy
					if ((level & OVERRIDDEN_METHOD_FLAVOR) != 0) {
						// this method is already overridden on a super class, current match is impossible
						return IMPOSSIBLE_MATCH;
					}
					if (!method.isAbstract() && !type.isInterface()) {
						// store the fact that the method is overridden
						level |= OVERRIDDEN_METHOD_FLAVOR;
					}
				}
			}
			return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level
		}
	}

	// matches interfaces
	ReferenceBinding[] interfaces = type.superInterfaces();
	if (interfaces == null) return INACCURATE_MATCH;
	for (int i = 0; i < interfaces.length; i++) {
		level = resolveLevelAsSubtype(simplePattern, qualifiedPattern, interfaces[i], methodName, null, packageName, isDefault);
		if (level != IMPOSSIBLE_MATCH) {
			if (!type.isAbstract() && !type.isInterface()) { // if concrete class, then method is overridden
				level |= OVERRIDDEN_METHOD_FLAVOR;
			}
			return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level
		}
	}
	return IMPOSSIBLE_MATCH;
}

/*
 * Return whether the given type binding or one of its possible super interfaces
 * matches a type in the declaring type names hierarchy.
 */
private boolean resolveLevelAsSuperInvocation(ReferenceBinding type, TypeBinding[] argumentTypes, char[][][] superTypeNames, boolean methodAlreadyVerified) {
	char[][] compoundName = type.compoundName;
	for (int i = 0, max = superTypeNames.length; i < max; i++) {
		if (CharOperation.equals(superTypeNames[i], compoundName)) {
			// need to verify if the type implements the pattern method
			if (methodAlreadyVerified) return true; // already verified before enter into this method (see resolveLevel(MessageSend))
			MethodBinding[] methods = type.getMethods(this.pattern.selector);
			for (int j=0, length=methods.length; j




© 2015 - 2024 Weber Informatics LLC | Privacy Policy