Skip to content

Commit dc12d3f

Browse files
authored
Merge pull request quarkusio#48061 from Ladicek/arc-fix-interception-proxy
ArC: fix interception proxy to forward non-intercepted methods to the delegate
2 parents 9ba60eb + f48156a commit dc12d3f

13 files changed

+231
-70
lines changed

extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedClassTest.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public class ProducerWithFinalInterceptedClassTest {
3232
@Test
3333
public void test() {
3434
MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get();
35-
assertEquals("intercepted: hello1", nonbean.hello1());
36-
assertEquals("hello2", nonbean.hello2());
35+
assertEquals("intercepted: hello1_foobar", nonbean.hello1());
36+
assertEquals("hello2_foobar", nonbean.hello2());
3737
}
3838

3939
@Retention(RetentionPolicy.RUNTIME)
@@ -53,13 +53,23 @@ Object intercept(InvocationContext ctx) throws Exception {
5353
}
5454

5555
static final class MyNonbean {
56+
private final String value;
57+
58+
MyNonbean() {
59+
this(null);
60+
}
61+
62+
MyNonbean(String value) {
63+
this.value = value;
64+
}
65+
5666
@MyBinding
5767
String hello1() {
58-
return "hello1";
68+
return "hello1_" + value;
5969
}
6070

6171
String hello2() {
62-
return "hello2";
72+
return "hello2_" + value;
6373
}
6474
}
6575

@@ -68,7 +78,7 @@ static class MyProducer {
6878
@Produces
6979
@Unremovable
7080
MyNonbean produce(InterceptionProxy<MyNonbean> proxy) {
71-
return proxy.create(new MyNonbean());
81+
return proxy.create(new MyNonbean("foobar"));
7282
}
7383
}
7484
}

extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedMethodTest.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public class ProducerWithFinalInterceptedMethodTest {
3232
@Test
3333
public void test() {
3434
MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get();
35-
assertEquals("intercepted: hello1", nonbean.hello1());
36-
assertEquals("hello2", nonbean.hello2());
35+
assertEquals("intercepted: hello1_foobar", nonbean.hello1());
36+
assertEquals("hello2_foobar", nonbean.hello2());
3737
}
3838

3939
@Retention(RetentionPolicy.RUNTIME)
@@ -53,13 +53,23 @@ Object intercept(InvocationContext ctx) throws Exception {
5353
}
5454

5555
static class MyNonbean {
56+
private final String value;
57+
58+
MyNonbean() {
59+
this(null);
60+
}
61+
62+
MyNonbean(String value) {
63+
this.value = value;
64+
}
65+
5666
@MyBinding
5767
final String hello1() {
58-
return "hello1";
68+
return "hello1_" + value;
5969
}
6070

6171
final String hello2() {
62-
return "hello2";
72+
return "hello2_" + value;
6373
}
6474
}
6575

@@ -68,7 +78,7 @@ static class MyProducer {
6878
@Produces
6979
@Unremovable
7080
MyNonbean produce(InterceptionProxy<MyNonbean> proxy) {
71-
return proxy.create(new MyNonbean());
81+
return proxy.create(new MyNonbean("foobar"));
7282
}
7383
}
7484
}

extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ public class ProducerWithoutZeroParamCtorAndInterceptionTest {
3232
@Test
3333
public void test() {
3434
MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get();
35-
assertEquals("intercepted: hello", nonbean.hello());
35+
assertEquals("intercepted: hello1_foobar", nonbean.hello1());
36+
assertEquals("hello2_foobar", nonbean.hello2());
3637
}
3738

3839
@Retention(RetentionPolicy.RUNTIME)
@@ -52,12 +53,19 @@ Object intercept(InvocationContext ctx) throws Exception {
5253
}
5354

5455
static class MyNonbean {
55-
MyNonbean(int ignored) {
56+
private final String value;
57+
58+
private MyNonbean(String value) {
59+
this.value = value;
5660
}
5761

5862
@MyBinding
59-
String hello() {
60-
return "hello";
63+
String hello1() {
64+
return "hello1_" + value;
65+
}
66+
67+
String hello2() {
68+
return "hello2_" + value;
6169
}
6270
}
6371

@@ -66,7 +74,7 @@ static class MyProducer {
6674
@Produces
6775
@Unremovable
6876
MyNonbean produce(InterceptionProxy<MyNonbean> proxy) {
69-
return proxy.create(new MyNonbean(0));
77+
return proxy.create(new MyNonbean("foobar"));
7078
}
7179
}
7280
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ public List<Resource> generateResources(ReflectionRegistration reflectionRegistr
274274
// for interception of producer methods and synthetic beans and only supports
275275
// limited form of interception (and no decoration)
276276
InterceptionProxyGenerator interceptionGenerator = new InterceptionProxyGenerator(generateSources,
277-
applicationClassPredicate, annotationLiterals, reflectionRegistration);
277+
applicationClassPredicate, beanDeployment, annotationLiterals, reflectionRegistration);
278278

279279
List<Resource> resources = new ArrayList<>();
280280

@@ -373,7 +373,8 @@ public Collection<Resource> call() throws Exception {
373373
secondaryTasks.add(executor.submit(new Callable<Collection<Resource>>() {
374374
@Override
375375
public Collection<Resource> call() throws Exception {
376-
Collection<Resource> interceptionResources = interceptionGenerator.generate(bean);
376+
Collection<Resource> interceptionResources = interceptionGenerator.generate(bean,
377+
bytecodeTransformerConsumer, transformUnproxyableClasses);
377378
for (Resource r : interceptionResources) {
378379
if (r.getSpecialType() == SpecialType.SUBCLASS) {
379380
refReg.registerSubclass(bean.getInterceptionProxy().getTargetClass(),
@@ -487,7 +488,8 @@ public Collection<Resource> call() throws Exception {
487488
resources.addAll(subclassResources);
488489
}
489490
if (bean.getInterceptionProxy() != null) {
490-
Collection<Resource> interceptionResources = interceptionGenerator.generate(bean);
491+
Collection<Resource> interceptionResources = interceptionGenerator.generate(bean,
492+
bytecodeTransformerConsumer, transformUnproxyableClasses);
491493
for (Resource r : interceptionResources) {
492494
if (r.getSpecialType() == SpecialType.SUBCLASS) {
493495
refReg.registerSubclass(bean.getInterceptionProxy().getTargetClass(),

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

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.Map;
99
import java.util.Set;
1010
import java.util.function.BiFunction;
11+
import java.util.function.Consumer;
1112
import java.util.function.Function;
1213
import java.util.function.Predicate;
1314
import java.util.function.Supplier;
@@ -18,6 +19,7 @@
1819
import org.jboss.jandex.AnnotationInstanceEquivalenceProxy;
1920
import org.jboss.jandex.ClassInfo;
2021
import org.jboss.jandex.DotName;
22+
import org.jboss.jandex.IndexView;
2123
import org.jboss.jandex.MethodInfo;
2224
import org.jboss.jandex.Type;
2325
import org.objectweb.asm.Opcodes;
@@ -42,18 +44,22 @@ public class InterceptionProxyGenerator extends AbstractGenerator {
4244
private static final String INTERCEPTION_SUBCLASS = "_InterceptionSubclass";
4345

4446
private final Predicate<DotName> applicationClassPredicate;
47+
private final IndexView beanArchiveIndex;
4548
private final AnnotationLiteralProcessor annotationLiterals;
4649
private final ReflectionRegistration reflectionRegistration;
4750

4851
InterceptionProxyGenerator(boolean generateSources, Predicate<DotName> applicationClassPredicate,
49-
AnnotationLiteralProcessor annotationLiterals, ReflectionRegistration reflectionRegistration) {
52+
BeanDeployment deployment, AnnotationLiteralProcessor annotationLiterals,
53+
ReflectionRegistration reflectionRegistration) {
5054
super(generateSources);
5155
this.applicationClassPredicate = applicationClassPredicate;
56+
this.beanArchiveIndex = deployment.getBeanArchiveIndex();
5257
this.annotationLiterals = annotationLiterals;
5358
this.reflectionRegistration = reflectionRegistration;
5459
}
5560

56-
Collection<Resource> generate(BeanInfo bean) {
61+
Collection<Resource> generate(BeanInfo bean, Consumer<BytecodeTransformer> bytecodeTransformerConsumer,
62+
boolean transformUnproxyableClasses) {
5763
if (bean.getInterceptionProxy() == null) {
5864
return Collections.emptyList();
5965
}
@@ -69,7 +75,8 @@ Collection<Resource> generate(BeanInfo bean) {
6975

7076
createInterceptionProxyProvider(classOutput, bean);
7177
createInterceptionProxy(classOutput, bean);
72-
createInterceptionSubclass(classOutput, bean.getInterceptionProxy());
78+
createInterceptionSubclass(classOutput, bean.getInterceptionProxy(),
79+
bytecodeTransformerConsumer, transformUnproxyableClasses);
7380

7481
return classOutput.getResources();
7582
}
@@ -148,7 +155,8 @@ private void createInterceptionProxy(ClassOutput classOutput, BeanInfo bean) {
148155
}
149156
}
150157

151-
private void createInterceptionSubclass(ClassOutput classOutput, InterceptionProxyInfo interceptionProxy) {
158+
private void createInterceptionSubclass(ClassOutput classOutput, InterceptionProxyInfo interceptionProxy,
159+
Consumer<BytecodeTransformer> bytecodeTransformerConsumer, boolean transformUnproxyableClasses) {
152160
BeanInfo pseudoBean = interceptionProxy.getPseudoBean();
153161
ClassInfo pseudoBeanClass = pseudoBean.getImplClazz();
154162
String pseudoBeanClassName = pseudoBeanClass.name().toString();
@@ -355,6 +363,49 @@ private void createInterceptionSubclass(ClassOutput classOutput, InterceptionPro
355363

356364
MethodCreator getDelegate = clazz.getMethodCreator("arc_delegate", Object.class);
357365
getDelegate.returnValue(getDelegate.readInstanceField(delegate.getFieldDescriptor(), getDelegate.getThis()));
366+
367+
// forward non-intercepted methods to the delegate unconditionally
368+
Collection<MethodInfo> methodsToForward = collectMethodsToForward(pseudoBean,
369+
bytecodeTransformerConsumer, transformUnproxyableClasses);
370+
for (MethodInfo method : methodsToForward) {
371+
MethodCreator mc = clazz.getMethodCreator(MethodDescriptor.of(method));
372+
ResultHandle dlgt = mc.readInstanceField(delegate.getFieldDescriptor(), mc.getThis());
373+
ResultHandle[] args = new ResultHandle[method.parametersCount()];
374+
for (int i = 0; i < method.parametersCount(); i++) {
375+
args[i] = mc.getMethodParam(i);
376+
}
377+
ResultHandle result = method.declaringClass().isInterface()
378+
? mc.invokeInterfaceMethod(method, dlgt, args)
379+
: mc.invokeVirtualMethod(method, dlgt, args);
380+
mc.returnValue(result);
381+
}
382+
}
383+
}
384+
385+
// uses the same algorithm as `ClientProxyGenerator`
386+
private Collection<MethodInfo> collectMethodsToForward(BeanInfo pseudoBean,
387+
Consumer<BytecodeTransformer> bytecodeTransformerConsumer, boolean transformUnproxyableClasses) {
388+
ClassInfo pseudoBeanClass = pseudoBean.getImplClazz();
389+
390+
Map<Methods.MethodKey, MethodInfo> methods = new HashMap<>();
391+
Map<String, Set<Methods.MethodKey>> methodsFromWhichToRemoveFinal = new HashMap<>();
392+
393+
Methods.addDelegatingMethods(beanArchiveIndex, pseudoBeanClass, methods, methodsFromWhichToRemoveFinal,
394+
transformUnproxyableClasses);
395+
396+
if (!methodsFromWhichToRemoveFinal.isEmpty()) {
397+
for (Map.Entry<String, Set<Methods.MethodKey>> entry : methodsFromWhichToRemoveFinal.entrySet()) {
398+
String className = entry.getKey();
399+
bytecodeTransformerConsumer.accept(new BytecodeTransformer(className,
400+
new Methods.RemoveFinalFromMethod(entry.getValue())));
401+
}
358402
}
403+
404+
for (MethodInfo interceptedMethod : pseudoBean.getInterceptedMethods().keySet()) {
405+
// these methods are intercepted, so they don't need to (and in fact _must not_) forward directly
406+
methods.remove(new Methods.MethodKey(interceptedMethod));
407+
}
408+
409+
return methods.values();
359410
}
360411
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private static boolean skipForClientProxy(MethodInfo method, boolean transformUn
116116
if (Modifier.isFinal(method.flags())) {
117117
String className = method.declaringClass().name().toString();
118118
if (!className.startsWith("java.")) {
119-
if (transformUnproxyableClasses && (methodsFromWhichToRemoveFinal != null)) {
119+
if (transformUnproxyableClasses && methodsFromWhichToRemoveFinal != null) {
120120
methodsFromWhichToRemoveFinal.computeIfAbsent(className, (k) -> new HashSet<>())
121121
.add(new MethodKey(method));
122122
return false;

independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsTest.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ public class ProducerWithClassAndMethodLevelInterceptorsTest {
3131
@Test
3232
public void test() {
3333
MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get();
34-
assertEquals("intercepted1: intercepted2: hello1", nonbean.hello1());
35-
assertEquals("intercepted1: hello2", nonbean.hello2());
36-
assertEquals("hello3", nonbean.hello3());
37-
assertEquals("intercepted2: hello4", nonbean.hello4());
34+
assertEquals("intercepted1: intercepted2: hello1_foobar", nonbean.hello1());
35+
assertEquals("intercepted1: hello2_foobar", nonbean.hello2());
36+
assertEquals("intercepted2: hello3_foobar", nonbean.hello3());
37+
assertEquals("hello4_foobar", nonbean.hello4());
3838
}
3939

4040
@Retention(RetentionPolicy.RUNTIME)
@@ -71,32 +71,42 @@ Object intercept(InvocationContext ctx) throws Exception {
7171

7272
@MyBinding1
7373
static class MyNonbean {
74+
private final String value;
75+
76+
MyNonbean() {
77+
this(null);
78+
}
79+
80+
MyNonbean(String value) {
81+
this.value = value;
82+
}
83+
7484
@MyBinding2
7585
String hello1() {
76-
return "hello1";
86+
return "hello1_" + value;
7787
}
7888

7989
String hello2() {
80-
return "hello2";
90+
return "hello2_" + value;
8191
}
8292

8393
@NoClassInterceptors
94+
@MyBinding2
8495
String hello3() {
85-
return "hello3";
96+
return "hello3_" + value;
8697
}
8798

8899
@NoClassInterceptors
89-
@MyBinding2
90100
String hello4() {
91-
return "hello4";
101+
return "hello4_" + value;
92102
}
93103
}
94104

95105
@Dependent
96106
static class MyProducer {
97107
@Produces
98108
MyNonbean produce(InterceptionProxy<MyNonbean> proxy) {
99-
return proxy.create(new MyNonbean());
109+
return proxy.create(new MyNonbean("foobar"));
100110
}
101111
}
102112
}

independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsTest.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public class ProducerWithClassLevelInterceptorsTest {
3131
@Test
3232
public void test() {
3333
MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get();
34-
assertEquals("intercepted1: intercepted2: hello1", nonbean.hello1());
35-
assertEquals("hello2", nonbean.hello2());
34+
assertEquals("intercepted1: intercepted2: hello1_foobar", nonbean.hello1());
35+
assertEquals("hello2_foobar", nonbean.hello2());
3636
}
3737

3838
@Retention(RetentionPolicy.RUNTIME)
@@ -70,21 +70,31 @@ Object intercept(InvocationContext ctx) throws Exception {
7070
@MyBinding1
7171
@MyBinding2
7272
static class MyNonbean {
73+
private final String value;
74+
75+
MyNonbean() {
76+
this(null);
77+
}
78+
79+
MyNonbean(String value) {
80+
this.value = value;
81+
}
82+
7383
String hello1() {
74-
return "hello1";
84+
return "hello1_" + value;
7585
}
7686

7787
@NoClassInterceptors
7888
String hello2() {
79-
return "hello2";
89+
return "hello2_" + value;
8090
}
8191
}
8292

8393
@Dependent
8494
static class MyProducer {
8595
@Produces
8696
MyNonbean produce(InterceptionProxy<MyNonbean> proxy) {
87-
return proxy.create(new MyNonbean());
97+
return proxy.create(new MyNonbean("foobar"));
8898
}
8999
}
90100
}

0 commit comments

Comments
 (0)