blob: 8b4a917f862440628853ad817b53d8cb5ebd62f6 [file] [log] [blame]
/* *******************************************************************
* Copyright (c) 2002,2005 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
* Andy Clement - upgrade to support fields targetting generic types
* ******************************************************************/
package org.aspectj.ajdt.internal.compiler.lookup;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.compiler.IProblem;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.IMemberFinder;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
/**
* The member finder looks after intertype declared members on a type, there is one member finder per type that was hit by an ITD.
*/
public class InterTypeMemberFinder implements IMemberFinder {
private List<FieldBinding> interTypeFields = new ArrayList<FieldBinding>();
private List<MethodBinding> interTypeMethods = new ArrayList<MethodBinding>();
public SourceTypeBinding sourceTypeBinding;
@Override
public FieldBinding getField(SourceTypeBinding sourceTypeBinding, char[] fieldName, InvocationSite site, Scope scope) {
FieldBinding retField = sourceTypeBinding.getFieldBase(fieldName, true); // XXX may need to get the correct value for second
// parameter in the future (see #55341)
if (interTypeFields.isEmpty()) {
return retField;
}
int fieldLength = fieldName.length;
for (int i = 0, len = interTypeFields.size(); i < len; i++) {
FieldBinding field = interTypeFields.get(i);
if (field.name.length == fieldLength && CharOperation.prefixEquals(field.name, fieldName)) {
retField = resolveConflicts(sourceTypeBinding, retField, field, site, scope);
}
}
return retField;
}
private FieldBinding resolveConflicts(SourceTypeBinding sourceTypeBinding, FieldBinding retField, FieldBinding field,
InvocationSite site, Scope scope) {
if (retField == null) {
return field;
}
if (site != null) {
if (!field.canBeSeenBy(sourceTypeBinding, site, scope)) {
return retField;
}
if (!retField.canBeSeenBy(sourceTypeBinding, site, scope)) {
return field;
}
}
// XXX need dominates check on aspects
return new ProblemFieldBinding(retField.declaringClass, retField.name, IProblem.AmbiguousField);// ProblemReporter.Ambiguous);
}
// private void reportConflicts(SourceTypeBinding sourceTypeBinding,
// MethodBinding m1, MethodBinding m2)
// {
// if (m1 == m2) {
// System.err.println("odd that we're ecomparing the same: " + m1);
// return;
// }
//
// if (!m1.areParametersEqual(m2)) return;
// if (m1 instanceof InterTypeMethodBinding) {
// if (m2 instanceof InterTypeMethodBinding) {
// reportConflictsBoth(sourceTypeBinding,
// (InterTypeMethodBinding)m1,
// (InterTypeMethodBinding)m2);
// } else {
// reportConflictsOne(sourceTypeBinding,
// (InterTypeMethodBinding)m1,
// m2);
// }
// } else if (m2 instanceof InterTypeMethodBinding) {
// reportConflictsOne(sourceTypeBinding,
// (InterTypeMethodBinding)m2,
// m1);
// } else {
// reportConflictsNone(sourceTypeBinding,
// m2,
// m1);
// }
// }
// private void reportConflicts(
// SourceTypeBinding sourceTypeBinding,
// MethodBinding m1,
// MethodBinding m2)
// {
// //System.err.println("compare: " + m1 + " with " + m2);
//
// if (m1 == m2) {
// System.err.println("odd that we're ecomparing the same: " + m1);
// return;
// }
//
// if (!m1.areParametersEqual(m2)) return;
//
// //System.err.println("t1: " + getTargetType(m1) + ", " + getTargetType(m2));
//
// if (getTargetType(m1) != getTargetType(m2)) return;
//
// if (m1.declaringClass == m2.declaringClass) {
// duplicateMethodBinding(m1, m2);
// return;
// }
//
//
// if (m1.isPublic() || m2.isPublic()) {
// duplicateMethodBinding(m1, m2);
// return;
// }
//
// // handle the wierd case where the aspect is a subtype of the target
// if (m2.isProtected()) {
// if (m2.declaringClass.isSuperclassOf(m1.declaringClass)) {
// duplicateMethodBinding(m1, m2);
// }
// // don't return because we also want to do the package test
// }
//
// if (!m1.isPrivate() || !m2.isPrivate()) {
// // at least package visible
// if (m1.declaringClass.getPackage() == m2.declaringClass.getPackage()) {
// duplicateMethodBinding(m1, m2);
// }
// return;
// }
//
// //XXX think about inner types some day
// }
// //
private boolean isVisible(MethodBinding m1, ReferenceBinding s) {
if (m1.declaringClass == s) {
return true;
}
if (m1.isPublic()) {
return true;
}
// don't need to handle protected
// if (m1.isProtected()) {
if (!m1.isPrivate()) {
// at least package visible
return (m1.declaringClass.getPackage() == s.getPackage());
}
return false;
}
//
// private void duplicateMethodBinding(MethodBinding m1, MethodBinding m2) {
// ReferenceBinding t1 = m1.declaringClass;
// ReferenceBinding t2 = m2.declaringClass;
//
//
//
//
//
//
// if (!(t1 instanceof SourceTypeBinding) || !(t2 instanceof SourceTypeBinding)) {
// throw new RuntimeException("unimplemented");
// }
//
// SourceTypeBinding s1 = (SourceTypeBinding)t1;
// SourceTypeBinding s2 = (SourceTypeBinding)t2;
//
// if (m1.sourceMethod() != null) {
// s1.scope.problemReporter().duplicateMethodInType(s2, m1.sourceMethod());
// }
// if (m2.sourceMethod() != null) {
// s2.scope.problemReporter().duplicateMethodInType(s1, m2.sourceMethod());
// }
// }
// private void reportConflictsNone(
// SourceTypeBinding sourceTypeBinding,
// MethodBinding m2,
// MethodBinding m1)
// {
// throw new RuntimeException("not possible");
// }
// ReferenceBinding t1 = getDeclaringClass(m1);
// //.declaringClass;
// ReferenceBinding t2 = getDeclaringClass(m2);
// //.declaringClass;
//
// if (t1 == t2) {
// AbstractMethodDeclaration methodDecl = m2.sourceMethod(); // cannot be retrieved after binding is lost
// System.err.println("duplicate: " + t1 + ", " + t2);
// sourceTypeBinding.scope.problemReporter().duplicateMethodInType(sourceTypeBinding, methodDecl);
// methodDecl.binding = null;
// //methods[m] = null; //XXX duplicate problem reports
// return;
// }
//
// if (!(t1 instanceof SourceTypeBinding) || !(t2 instanceof SourceTypeBinding)) {
// throw new RuntimeException("unimplemented");
// }
//
// SourceTypeBinding s1 = (SourceTypeBinding)t1;
// SourceTypeBinding s2 = (SourceTypeBinding)t2;
//
//
// if (m1.canBeSeenBy(s1, null, s2.scope) || m2.canBeSeenBy(s2, null, s1.scope)) {
// s1.scope.problemReporter().duplicateMethodInType(s2, m1.sourceMethod());
// s2.scope.problemReporter().duplicateMethodInType(s1, m2.sourceMethod());
// }
// }
// private ReferenceBinding getTargetType(MethodBinding m2) {
// if (m2 instanceof InterTypeMethodBinding) {
// return ((InterTypeMethodBinding)m2).getTargetType();
// }
//
// return m2.declaringClass;
// }
// find all of my methods, including ITDs
// PLUS: any public ITDs made on interfaces that I implement
@Override
public MethodBinding[] methods(SourceTypeBinding sourceTypeBinding) {
MethodBinding[] orig = sourceTypeBinding.methodsBase();
// if (interTypeMethods.isEmpty()) return orig;
List<MethodBinding> ret = new ArrayList<MethodBinding>(Arrays.asList(orig));
for (int i = 0, len = interTypeMethods.size(); i < len; i++) {
MethodBinding method = interTypeMethods.get(i);
ret.add(method);
}
ReferenceBinding[] interfaces = sourceTypeBinding.superInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i] instanceof SourceTypeBinding) {
SourceTypeBinding intSTB = (SourceTypeBinding) interfaces[i];
addPublicITDSFrom(intSTB, ret);
}
}
if (ret.isEmpty()) {
return Binding.NO_METHODS;
}
return ret.toArray(new MethodBinding[ret.size()]);
}
private void addPublicITDSFrom(SourceTypeBinding anInterface, List<MethodBinding> accumulator) {
if (anInterface.memberFinder != null) {
InterTypeMemberFinder finder = (InterTypeMemberFinder) anInterface.memberFinder;
for (MethodBinding aBinding: finder.interTypeMethods) {
if (Modifier.isPublic(aBinding.modifiers)) {
accumulator.add(aBinding);
}
}
}
ReferenceBinding superType = anInterface.superclass;
if (superType instanceof SourceTypeBinding && superType.isInterface()) {
addPublicITDSFrom((SourceTypeBinding) superType, accumulator);
}
}
// XXX conflicts
@Override
public MethodBinding[] getMethods(SourceTypeBinding sourceTypeBinding, char[] selector) {
// System.err.println("getMethods: " + new String(sourceTypeBinding.signature()) +
// ", " + new String(selector));
MethodBinding[] orig = sourceTypeBinding.getMethodsBase(selector);
if (interTypeMethods.isEmpty()) {
return orig;
}
Set<MethodBinding> ret = new HashSet<MethodBinding>(Arrays.asList(orig));
// System.err.println("declared method: " + ret + " inters = " + interTypeMethods);
for (int i = 0, len = interTypeMethods.size(); i < len; i++) {
MethodBinding method = interTypeMethods.get(i);
if (CharOperation.equals(selector, method.selector)) {
ret.add(method);
}
}
if (ret.isEmpty()) {
return Binding.NO_METHODS;
}
// System.err.println("method: " + ret);
// check for conflicts
// int len = ret.size();
// if (len > 1) {
// for (int i=0; i <len; i++) {
// MethodBinding m1 = (MethodBinding)ret.get(i);
// for (int j=i+1; j < len; j++) {
// MethodBinding m2 = (MethodBinding)ret.get(j);
// //reportConflicts(sourceTypeBinding, m1, m2);
// }
// }
// }
// System.err.println("got methods: " + ret + " on " + sourceTypeBinding);
return ret.toArray(new MethodBinding[ret.size()]);
}
@Override
public MethodBinding getExactMethod(SourceTypeBinding sourceTypeBinding, char[] selector, TypeBinding[] argumentTypes,
CompilationUnitScope refScope) {
MethodBinding ret = sourceTypeBinding.getExactMethodBase(selector, argumentTypes, refScope);
// An intertype declaration may override an inherited member (Bug#50776)
for (int i = 0, len = interTypeMethods.size(); i < len; i++) {
MethodBinding im = interTypeMethods.get(i);
if (matches(im, selector, argumentTypes)) {
return im;
}
}
return ret;
}
// if (isVisible(im, sourceTypeBinding)) {
// if (ret == null) {
// ret = im;
// } else {
// ret = resolveOverride(ret, im);
// }
// }
// }
// }
// return ret;
// }
// private MethodBinding resolveOverride(MethodBinding m1, MethodBinding m2) {
// ReferenceBinding t1 = getTargetType(m1);
// ReferenceBinding t2 = getTargetType(m2);
// if (t1 == t2) {
// //XXX also need a test for completely matching sigs
// if (m1.isAbstract()) return m2;
// else if (m2.isAbstract()) return m1;
//
//
// if (m1 instanceof InterTypeMethodBinding) {
// //XXX need to handle dominates here
// EclipseWorld world = EclipseWorld.fromScopeLookupEnvironment(sourceTypeBinding.scope);
// int cmp = compareAspectPrecedence(world.fromEclipse(m1.declaringClass),
// world.fromEclipse(m2.declaringClass));
// if (cmp < 0) return m2;
// else if (cmp > 0) return m1;
// }
//
// duplicateMethodBinding(m1, m2);
// return null;
// }
// if (t1.isSuperclassOf(t2)) {
// return m2;
// }
// if (t2.isSuperclassOf(t1)) {
// return m1;
// }
//
// duplicateMethodBinding(m1, m2);
// return null;
// }
// private int compareAspectPrecedence(ResolvedType a1, ResolvedType a2) {
// World world = a1.getWorld();
// int ret = world.compareByDominates(a1, a2);
// if (ret == 0) {
// if (a1.isAssignableFrom(a2)) return -1;
// if (a2.isAssignableFrom(a1)) return +1;
// }
// return ret;
// }
//
public static boolean matches(MethodBinding m1, MethodBinding m2) {
return matches(m1, m2.selector, m2.parameters);
// &&
// (isVisible(m1, m2.declaringClass) || isVisible(m2, m1.declaringClass));
}
public static boolean matches2(MethodBinding newBinding, MethodBinding existingBinding) {
if (!CharOperation.equals(existingBinding.selector, newBinding.selector)) {
return false;
}
int argCount = existingBinding.parameters.length;
if (newBinding.parameters.length != argCount) {
return false;
}
TypeBinding[] toMatch = newBinding.parameters;
boolean matches = true;
for (int p = 0; p < argCount; p++) {
if (toMatch[p] != existingBinding.parameters[p]) {
matches = false;
break;
}
}
if (!matches) {
// could be due to generics differences. For example:
// existingBinding is <T extends Foo> T bar(T t)
// newBinding is <T extends Foo> T bar(T t)
// the '==' check isn't going to pass, we need to do a deeper check
TypeVariableBinding[] existingTVBs = existingBinding.typeVariables;
TypeVariableBinding[] newTVBs = newBinding.typeVariables;
if (existingTVBs.length != 0 && existingTVBs.length == newTVBs.length) {
// Check them
boolean genericsMatch = true;
for (int t = 0, max = existingTVBs.length; t < max; t++) {
char[] existingSig = existingTVBs[t].genericSignature();
char[] newSig = newTVBs[t].genericSignature();
if (!CharOperation.equals(existingSig, newSig)) {
genericsMatch = false;
break;
}
}
if (genericsMatch) {
for (int a = 0; a < argCount; a++) {
char[] existingSig = existingBinding.parameters[a].genericTypeSignature();
char[] newSig = toMatch[a].genericTypeSignature();
if (!CharOperation.equals(existingSig, newSig)) {
genericsMatch = false;
break;
}
}
}
if (genericsMatch) {
matches = true;
}
}
}
return matches;
}
private static boolean matches(MethodBinding method, char[] selector, TypeBinding[] argumentTypes) {
if (!CharOperation.equals(selector, method.selector)) {
return false;
}
int argCount = argumentTypes.length;
if (method.parameters.length != argCount) {
return false;
}
TypeBinding[] toMatch = method.parameters;
for (int p = 0; p < argCount; p++) {
if (toMatch[p] != argumentTypes[p]) {
return false;
}
}
return true;
}
public void addInterTypeField(FieldBinding binding) {
// System.err.println("adding: " + binding + " to " + this);
interTypeFields.add(binding);
}
public void addInterTypeMethod(MethodBinding binding) {
// check for conflicts with existing methods, should really check type as well...
// System.err.println("adding: " + binding + " to " + sourceTypeBinding);
// pr284862 - this can leave a broken EclipseResolvedMember with an orphan method binding inside...ought to repair it
if (isVisible(binding, sourceTypeBinding)) {
MethodBinding[] baseMethods = sourceTypeBinding.methods;
for (int i = 0, len = baseMethods.length; i < len; i++) {
MethodBinding b = baseMethods[i];
sourceTypeBinding.resolveTypesFor(b); // this will return fast if its already been done.
if (matches2(binding, b)) {
// this always means we should remove the existing method
if (b.sourceMethod() != null) {
b.sourceMethod().binding = null;
}
sourceTypeBinding.removeMethod(i);
// System.err.println(" left: " + Arrays.asList(sourceTypeBinding.methods));
break;
}
}
}
interTypeMethods.add(binding);
}
/**
* @param name the name of the field
* @return true if already know about an intertype field declaration for that field
*/
public boolean definesField(String name) {
List<FieldBinding> l = interTypeFields;
if (l.size() > 0) {
char[] nameChars = name.toCharArray();
for (int i = 0; i < l.size(); i++) {
FieldBinding fieldBinding = interTypeFields.get(i);
if (CharOperation.equals(fieldBinding.name, nameChars)) {
return true;
}
}
}
return false;
}
}