Skip to content

Commit

Permalink
ArC: fix some screnarios with generic decorators
Browse files Browse the repository at this point in the history
- resolves quarkusio#33803
  • Loading branch information
mkouba committed Jun 6, 2023
1 parent 383b1ff commit ee7b56a
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
Expand Down Expand Up @@ -350,23 +351,41 @@ List<MethodInfo> getInterceptedOrDecoratedMethods() {
Set<MethodInfo> getDecoratedMethods(DecoratorInfo decorator) {
Set<MethodInfo> decorated = new HashSet<>();
for (Entry<MethodInfo, DecorationInfo> entry : decoratedMethods.entrySet()) {
if (entry.getValue().decorators.contains(decorator)) {
if (entry.getValue().contains(decorator)) {
decorated.add(entry.getKey());
}
}
return decorated;
}

MethodInfo getDecoratedMethod(MethodInfo decoratorMethod, DecoratorInfo decorator) {
for (Entry<MethodInfo, DecorationInfo> e : decoratedMethods.entrySet()) {
for (DecoratorMethod dm : e.getValue().decoratorMethods) {
if (dm.decorator.equals(decorator) && dm.method.equals(decoratorMethod)) {
return e.getKey();
}
}
}
return null;
}

// Returns a map of method descriptor -> next decorator in the chain
// e.g. foo() -> BravoDecorator
Map<MethodDescriptor, DecoratorInfo> getNextDecorators(DecoratorInfo decorator) {
Map<MethodDescriptor, DecoratorInfo> next = new HashMap<>();
Map<MethodDescriptor, DecoratorMethod> getNextDecorators(DecoratorInfo decorator) {
Map<MethodDescriptor, DecoratorMethod> next = new HashMap<>();
for (Entry<MethodInfo, DecorationInfo> entry : decoratedMethods.entrySet()) {
List<DecoratorInfo> decorators = entry.getValue().decorators;
int index = decorators.indexOf(decorator);
List<DecoratorMethod> decoratorMethods = entry.getValue().decoratorMethods;
int index = -1;
for (ListIterator<DecoratorMethod> it = decoratorMethods.listIterator(); it.hasNext();) {
DecoratorMethod dm = it.next();
if (dm.decorator.equals(decorator)) {
index = it.previousIndex();
break;
}
}
if (index != -1) {
if (index != (decorators.size() - 1)) {
next.put(MethodDescriptor.of(entry.getKey()), decorators.get(index + 1));
if (index != (decoratorMethods.size() - 1)) {
next.put(MethodDescriptor.of(entry.getKey()), decoratorMethods.get(index + 1));
}
}
}
Expand Down Expand Up @@ -456,9 +475,9 @@ public List<DecoratorInfo> getBoundDecorators() {
}
List<DecoratorInfo> bound = new ArrayList<>();
for (DecorationInfo decoration : decoratedMethods.values()) {
for (DecoratorInfo decorator : decoration.decorators) {
if (!bound.contains(decorator)) {
bound.add(decorator);
for (DecoratorMethod dm : decoration.decoratorMethods) {
if (!bound.contains(dm.decorator)) {
bound.add(dm.decorator);
}
}
}
Expand Down Expand Up @@ -692,7 +711,7 @@ private Map<MethodInfo, DecorationInfo> initDecoratedMethods() {
beanDeployment.getBeanArchiveIndex(), beanDeployment.getObserverAndProducerMethods(),
beanDeployment.getAnnotationStore()));

Map<MethodInfo, DecorationInfo> decoratedMethods = new HashMap<>(candidates.size());
Map<MethodInfo, DecorationInfo> decoratedMethods = new HashMap<>();
for (Entry<MethodKey, DecorationInfo> entry : candidates.entrySet()) {
decoratedMethods.put(entry.getKey().method, entry.getValue());
}
Expand All @@ -706,9 +725,10 @@ private void addDecoratedMethods(Map<MethodKey, DecorationInfo> decoratedMethods
if (skipPredicate.test(method)) {
continue;
}
List<DecoratorInfo> matching = findMatchingDecorators(method, boundDecorators);
if (!matching.isEmpty()) {
decoratedMethods.computeIfAbsent(new MethodKey(method), key -> new DecorationInfo(matching));
List<DecoratorMethod> matching = findMatchingDecorators(method, boundDecorators);
MethodKey key = new MethodKey(method);
if (!matching.isEmpty() && !decoratedMethods.containsKey(key)) {
decoratedMethods.put(key, new DecorationInfo(matching));
}
}
skipPredicate.methodsProcessed();
Expand All @@ -720,12 +740,11 @@ private void addDecoratedMethods(Map<MethodKey, DecorationInfo> decoratedMethods
}
}

private List<DecoratorInfo> findMatchingDecorators(MethodInfo method, List<DecoratorInfo> decorators) {
private List<DecoratorMethod> findMatchingDecorators(MethodInfo method, List<DecoratorInfo> decorators) {
List<Type> methodParams = method.parameterTypes();
List<DecoratorInfo> matching = new ArrayList<>(decorators.size());
List<DecoratorMethod> matching = new ArrayList<>(decorators.size());
for (DecoratorInfo decorator : decorators) {
for (Type decoratedType : decorator.getDecoratedTypes()) {
// Converter<String>
ClassInfo decoratedTypeClass = decorator.getDeployment().getBeanArchiveIndex()
.getClassByName(decoratedType.name());
if (decoratedTypeClass == null) {
Expand All @@ -750,13 +769,20 @@ private List<DecoratorInfo> findMatchingDecorators(MethodInfo method, List<Decor
decoratedMethod,
beanDeployment.getBeanArchiveIndex());
for (int i = 0; i < methodParams.size(); i++) {
if (!beanDeployment.getDelegateInjectionPointResolver().matches(decoratedMethodParams.get(i),
methodParams.get(i))) {
matches = false;
BeanResolver resolver = beanDeployment.getDelegateInjectionPointResolver();
Type decoratedParam = decoratedMethodParams.get(i);
if (decoratedParam.kind() == org.jboss.jandex.Type.Kind.TYPE_VARIABLE) {
if (!resolver.matchTypeArguments(decoratedMethodParams.get(i), methodParams.get(i))) {
matches = false;
}
} else {
if (!resolver.matches(decoratedMethodParams.get(i), methodParams.get(i))) {
matches = false;
}
}
}
if (matches) {
matching.add(decorator);
matching.add(new DecoratorMethod(decorator, decoratedMethod));
}
}
}
Expand Down Expand Up @@ -942,14 +968,39 @@ boolean isEmpty() {

static class DecorationInfo {

final List<DecoratorInfo> decorators;
final List<DecoratorMethod> decoratorMethods;

public DecorationInfo(List<DecoratorInfo> decorators) {
this.decorators = decorators;
public DecorationInfo(List<DecoratorMethod> decoratorMethods) {
this.decoratorMethods = decoratorMethods;
}

boolean isEmpty() {
return decorators.isEmpty();
return decoratorMethods.isEmpty();
}

boolean contains(DecoratorInfo decorator) {
for (DecoratorMethod dm : decoratorMethods) {
if (dm.decorator.equals(decorator)) {
return true;
}
}
return false;
}

DecoratorMethod firstDecoratorMethod() {
return decoratorMethods.get(0);
}

}

static class DecoratorMethod {

final DecoratorInfo decorator;
final MethodInfo method;

public DecoratorMethod(DecoratorInfo decorator, MethodInfo method) {
this.decorator = Objects.requireNonNull(decorator);
this.method = Objects.requireNonNull(method);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,11 @@ default Set<BeanInfo> resolveBeans(Type requiredType, AnnotationInstance... requ
*/
boolean hasQualifier(Collection<AnnotationInstance> qualifiers, AnnotationInstance requiredQualifier);

/**
* @param requiredTypeArgument
* @param typeArgument
* @return {@code true} if the required type argument matches the given type argument, {@code false} otherwise
*/
boolean matchTypeArguments(Type requiredTypeArgument, Type typeArgument);

}
Original file line number Diff line number Diff line change
Expand Up @@ -185,19 +185,19 @@ boolean matchesNoBoxing(Type requiredType, Type beanType) {
throw new IllegalArgumentException("Invalid argument combination " + requiredType + "; " + beanType);
}
for (int i = 0; i < requiredTypeArguments.size(); i++) {
if (!parametersMatch(requiredTypeArguments.get(i), beanTypeArguments.get(i))) {
if (!matchTypeArguments(requiredTypeArguments.get(i), beanTypeArguments.get(i))) {
return false;
}
}
return true;
}
} else if (WILDCARD_TYPE.equals(requiredType.kind())) {
return parametersMatch(requiredType, beanType);
return matchTypeArguments(requiredType, beanType);
}
return false;
}

boolean parametersMatch(Type requiredParameter, Type beanParameter) {
public boolean matchTypeArguments(Type requiredParameter, Type beanParameter) {
if (isActualType(requiredParameter) && isActualType(beanParameter)) {
/*
* the required type parameter and the bean type parameter are actual types with identical raw type, and, if the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DelegateInjectionPointResolverImpl extends BeanResolverImpl {
}

@Override
boolean parametersMatch(Type delegateType, Type beanParameter) {
public boolean matchTypeArguments(Type delegateType, Type beanParameter) {
// this is the same as for bean types
if (isActualType(delegateType) && isActualType(beanParameter)) {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private static boolean skipForClientProxy(MethodInfo method, boolean transformUn
}

static boolean skipForDelegateSubclass(MethodInfo method) {
if (Modifier.isStatic(method.flags())) {
if (Modifier.isStatic(method.flags()) || method.isSynthetic() || isDefault(method)) {
return true;
}
if (IGNORED_METHODS.contains(method.name())) {
Expand All @@ -153,6 +153,12 @@ static boolean skipForDelegateSubclass(MethodInfo method) {
return false;
}

static boolean isDefault(MethodInfo method) {
// Default methods are public non-abstract instance methods declared in an interface
return ((method.flags() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
&& method.declaringClass().isInterface();
}

static boolean isObjectToString(MethodInfo method) {
return method.declaringClass().name().equals(DotNames.OBJECT) && method.name().equals(TO_STRING);
}
Expand Down
Loading

0 comments on commit ee7b56a

Please sign in to comment.