Skip to content

Commit 8fcbcdd

Browse files
Ladicekgsmet
authored andcommitted
ArC: guard against multiple occurrences of the same decorator
Gizmo 1 didn't bother checking that multiple fields of the same name are added to a class, so `SubclassGenerator` didn't fail in case the same decorator occurred multiple times in the `BeanInfo.getNextDecorators()` map. However, Gizmo 2 does check, so we have to guard against it. The guard is simple: instead of adding decorators to a list directly, we first add them to a set and when that's done, we create a list from that set. (cherry picked from commit ac46b31)
1 parent 8e094e7 commit 8fcbcdd

File tree

2 files changed

+99
-8
lines changed

2 files changed

+99
-8
lines changed

independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -610,10 +610,11 @@ private void processDecorator(Gizmo gizmo, ClassCreator subclass,
610610
}
611611

612612
Map<MethodDesc, DecoratorMethod> nextDecorators = bean.getNextDecorators(decorator);
613-
List<DecoratorInfo> decoratorParameters = new ArrayList<>();
613+
Set<DecoratorInfo> decoratorParametersSet = new HashSet<>();
614614
for (DecoratorMethod decoratorMethod : nextDecorators.values()) {
615-
decoratorParameters.add(decoratorMethod.decorator);
615+
decoratorParametersSet.add(decoratorMethod.decorator);
616616
}
617+
List<DecoratorInfo> decoratorParameters = new ArrayList<>(decoratorParametersSet);
617618
Collections.sort(decoratorParameters);
618619

619620
List<ClassDesc> delegateSubclassCtorParams = new ArrayList<>();
@@ -807,19 +808,19 @@ && isDecorated(decoratedMethodDescriptors, methodDescriptor, resolvedMethodDesc,
807808
LocalVar cc = subclassCtor.localVar("cc", subclassCtor.invokeStatic(MethodDescs.CREATIONAL_CTX_CHILD, ccParam));
808809

809810
// Create new delegate subclass instance
810-
Expr[] params = new Expr[1 + decoratorParameters.size()];
811-
params[0] = subclass.this_();
812-
int paramIdx = 1;
811+
Expr[] args = new Expr[1 + decoratorParameters.size()];
812+
args[0] = subclass.this_();
813+
int argIndex = 1;
813814
for (DecoratorInfo decoratorParameter : decoratorParameters) {
814815
LocalVar decoratorVar = decoratorToLocalVar.get(decoratorParameter.getIdentifier());
815816
if (decoratorVar == null) {
816817
throw new IllegalStateException("Decorator var must not be null");
817818
}
818-
params[paramIdx] = decoratorVar;
819-
paramIdx++;
819+
args[argIndex] = decoratorVar;
820+
argIndex++;
820821
}
821822
Expr delegateSubclassInstance = subclassCtor.new_(ConstructorDesc.of(
822-
delegateSubclass, delegateSubclassCtorParams), params);
823+
delegateSubclass, delegateSubclassCtorParams), args);
823824

824825
// Set the DecoratorDelegateProvider to satisfy the delegate IP
825826
LocalVar prev = subclassCtor.localVar("prev", subclassCtor.invokeStatic(
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package io.quarkus.arc.test.decorators;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import jakarta.annotation.Priority;
6+
import jakarta.decorator.Decorator;
7+
import jakarta.decorator.Delegate;
8+
import jakarta.enterprise.context.ApplicationScoped;
9+
import jakarta.inject.Inject;
10+
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.extension.RegisterExtension;
13+
14+
import io.quarkus.arc.Arc;
15+
import io.quarkus.arc.test.ArcTestContainer;
16+
17+
/**
18+
* Verifies correct behavior in case the same decorator occurs multiple times
19+
* in the {@code BeanInfo.getNextDecorators()} map. The {@code SubclassGenerator}
20+
* code generator didn't bother checking, which was fine in Gizmo 1 which didn't
21+
* check duplicate fields. Gizmo 2 does check, so the {@code SubclassGenerator}
22+
* has to guard against that.
23+
*/
24+
public class SameDecoratorInMultipleNextChainsTest {
25+
@RegisterExtension
26+
public ArcTestContainer container = new ArcTestContainer(MyInterface.class, MyClass.class, MyDecorator1.class,
27+
MyDecorator2.class);
28+
29+
@Test
30+
public void test() {
31+
MyInterface instance = Arc.container().instance(MyInterface.class).get();
32+
assertEquals("d1m1 d2m1 A", instance.method1());
33+
assertEquals("d1m2 d2m2 B", instance.method2());
34+
}
35+
36+
interface MyInterface {
37+
String method1();
38+
39+
String method2();
40+
}
41+
42+
@ApplicationScoped
43+
static class MyClass implements MyInterface {
44+
@Override
45+
public String method1() {
46+
return "A";
47+
}
48+
49+
@Override
50+
public String method2() {
51+
return "B";
52+
}
53+
}
54+
55+
@Priority(10)
56+
@Decorator
57+
static class MyDecorator1 implements MyInterface {
58+
@Inject
59+
@Delegate
60+
MyInterface resource;
61+
62+
@Override
63+
public String method1() {
64+
return "d1m1 " + resource.method1();
65+
}
66+
67+
@Override
68+
public String method2() {
69+
return "d1m2 " + resource.method2();
70+
}
71+
}
72+
73+
@Priority(20)
74+
@Decorator
75+
static class MyDecorator2 implements MyInterface {
76+
@Inject
77+
@Delegate
78+
MyInterface resource;
79+
80+
@Override
81+
public String method1() {
82+
return "d2m1 " + resource.method1();
83+
}
84+
85+
@Override
86+
public String method2() {
87+
return "d2m2 " + resource.method2();
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)