Skip to content

Commit 94658de

Browse files
authored
Use TypeDef.erasure to prevent recursion types errors (#11657)
1 parent 51d1dfc commit 94658de

File tree

6 files changed

+153
-45
lines changed

6 files changed

+153
-45
lines changed

core-processor/src/main/java/io/micronaut/aop/writer/AopProxyWriter.java

+14-12
Original file line numberDiff line numberDiff line change
@@ -668,24 +668,26 @@ public void visitAroundMethod(TypedElement beanType,
668668

669669
if (!proxiedMethodsRefSet.contains(methodKey)) {
670670

671-
String interceptedProxyClassName = null;
672-
String interceptedProxyBridgeMethodName = null;
671+
ClassTypeDef interceptedProxyDef = null;
672+
MethodDef interceptedProxyBridgeMethod = null;
673673

674674
if (!isProxyTarget) {
675675
// if the target is not being proxied then we need to generate a bridge method and executable method that knows about it
676676

677677
if (!methodElement.isAbstract() || methodElement.isDefault()) {
678-
interceptedProxyClassName = proxyFullName;
679-
interceptedProxyBridgeMethodName = "$$access$$" + methodName;
678+
interceptedProxyDef = ClassTypeDef.of(proxyFullName);
679+
interceptedProxyBridgeMethod = MethodDef.builder("$$access$$" + methodName)
680+
.addModifiers(Modifier.PUBLIC)
681+
.addParameters(argumentTypeList.stream().map(p -> ParameterDef.of(p.getName(), TypeDef.erasure(p.getType()))).toList())
682+
.returns(TypeDef.erasure(returnType))
683+
.build((aThis, methodParameters) -> aThis.superRef((ClassTypeDef) TypeDef.erasure(methodElement.getOwningType()))
684+
.invoke(methodElement, methodParameters)
685+
.returning()
686+
);
680687

681688
// now build a bridge to invoke the original method
682689
proxyBuilder.addMethod(
683-
MethodDef.builder(interceptedProxyBridgeMethodName)
684-
.addModifiers(Modifier.PUBLIC)
685-
.addParameters(argumentTypeList.stream().map(p -> ParameterDef.of(p.getName(), TypeDef.erasure(p.getType()))).toList())
686-
.returns(TypeDef.erasure(returnType))
687-
.build((aThis, methodParameters) -> aThis.superRef((ClassTypeDef) TypeDef.erasure(methodElement.getOwningType()))
688-
.invoke(methodElement, methodParameters).returning())
690+
interceptedProxyBridgeMethod
689691
);
690692
}
691693
}
@@ -694,8 +696,8 @@ public void visitAroundMethod(TypedElement beanType,
694696
int methodIndex = beanDefinitionWriter.visitExecutableMethod(
695697
beanType,
696698
methodElement,
697-
interceptedProxyClassName,
698-
interceptedProxyBridgeMethodName
699+
interceptedProxyDef,
700+
interceptedProxyBridgeMethod
699701
);
700702
int index = proxyMethodCount++;
701703

core-processor/src/main/java/io/micronaut/inject/writer/BeanDefinitionWriter.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -2620,14 +2620,14 @@ public int visitExecutableMethod(TypedElement declaringBean,
26202620
* @param declaringType The declaring type of the method. Either a Class or a string representing the
26212621
* name of the type
26222622
* @param methodElement The method element
2623-
* @param interceptedProxyClassName The intercepted proxy class name
2624-
* @param interceptedProxyBridgeMethodName The intercepted proxy bridge method name
2623+
* @param interceptedProxyType The intercepted proxy type
2624+
* @param interceptedProxyBridgeMethod The intercepted proxy bridge method name
26252625
* @return The index of a new method.
26262626
*/
26272627
public int visitExecutableMethod(TypedElement declaringType,
26282628
MethodElement methodElement,
2629-
String interceptedProxyClassName,
2630-
String interceptedProxyBridgeMethodName) {
2629+
ClassTypeDef interceptedProxyType,
2630+
MethodDef interceptedProxyBridgeMethod) {
26312631

26322632
if (executableMethodsDefinitionWriter == null) {
26332633
executableMethodsDefinitionWriter = new ExecutableMethodsDefinitionWriter(
@@ -2639,7 +2639,7 @@ public int visitExecutableMethod(TypedElement declaringType,
26392639
visitorContext
26402640
);
26412641
}
2642-
return executableMethodsDefinitionWriter.visitExecutableMethod(declaringType, methodElement, interceptedProxyClassName, interceptedProxyBridgeMethodName);
2642+
return executableMethodsDefinitionWriter.visitExecutableMethod(declaringType, methodElement, interceptedProxyType, interceptedProxyBridgeMethod);
26432643
}
26442644

26452645
@Override

core-processor/src/main/java/io/micronaut/inject/writer/DispatchWriter.java

+19-21
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,21 @@ private DispatchTarget findDispatchTarget(TypedElement declaringType, MethodElem
172172
*
173173
* @param declaringType The declaring type
174174
* @param methodElement The method element
175-
* @param interceptedProxyClassName The interceptedProxyClassName
176-
* @param interceptedProxyBridgeMethodName The interceptedProxyBridgeMethodName
175+
* @param interceptedProxyType The interceptedProxyType
176+
* @param interceptedProxyBridgeMethod The interceptedProxyBridgeMethod
177177
* @return the target index
178178
*/
179179
public int addInterceptedMethod(TypedElement declaringType,
180180
MethodElement methodElement,
181-
String interceptedProxyClassName,
182-
String interceptedProxyBridgeMethodName) {
181+
ClassTypeDef interceptedProxyType,
182+
MethodDef interceptedProxyBridgeMethod) {
183183
hasInterceptedMethod = true;
184184
return addDispatchTarget(new InterceptableMethodDispatchTarget(
185185
findDispatchTarget(declaringType, methodElement, false),
186186
declaringType,
187187
methodElement,
188-
interceptedProxyClassName,
189-
interceptedProxyBridgeMethodName)
188+
interceptedProxyType,
189+
interceptedProxyBridgeMethod)
190190
);
191191
}
192192

@@ -857,20 +857,20 @@ public ExpressionDef dispatchOneExpression(ExpressionDef target, ExpressionDef v
857857
public static final class InterceptableMethodDispatchTarget extends AbstractDispatchTarget {
858858
private final TypedElement declaringType;
859859
private final DispatchTarget dispatchTarget;
860-
private final String interceptedProxyClassName;
861-
private final String interceptedProxyBridgeMethodName;
860+
private final ClassTypeDef interceptedProxyType;
861+
private final MethodDef interceptedProxyBridgeMethod;
862862
private final MethodElement methodElement;
863863

864864
private InterceptableMethodDispatchTarget(DispatchTarget dispatchTarget,
865865
TypedElement declaringType,
866866
MethodElement methodElement,
867-
String interceptedProxyClassName,
868-
String interceptedProxyBridgeMethodName) {
867+
ClassTypeDef interceptedProxyType,
868+
MethodDef interceptedProxyBridgeMethod) {
869869
this.declaringType = declaringType;
870870
this.methodElement = methodElement;
871871
this.dispatchTarget = dispatchTarget;
872-
this.interceptedProxyClassName = interceptedProxyClassName;
873-
this.interceptedProxyBridgeMethodName = interceptedProxyBridgeMethodName;
872+
this.interceptedProxyType = interceptedProxyType;
873+
this.interceptedProxyBridgeMethod = interceptedProxyBridgeMethod;
874874
}
875875

876876
@Override
@@ -898,23 +898,21 @@ public StatementDef dispatch(ExpressionDef target, ExpressionDef valuesArray) {
898898
VariableDef.Field interceptableField = new VariableDef.This()
899899
.field(FIELD_INTERCEPTABLE, TypeDef.of(boolean.class));
900900

901-
ClassTypeDef proxyType = ClassTypeDef.of(interceptedProxyClassName);
902901

903-
return interceptableField.isTrue().and(target.instanceOf(proxyType))
902+
return interceptableField.isTrue().and(target.instanceOf(interceptedProxyType))
904903
.doIfElse(
905-
invokeProxyBridge(proxyType, target, valuesArray),
904+
invokeProxyBridge(interceptedProxyType, target, valuesArray),
906905
dispatchTarget.dispatch(target, valuesArray)
907906
);
908907
}
909908

910909
private StatementDef invokeProxyBridge(ClassTypeDef proxyType, ExpressionDef target, ExpressionDef valuesArray) {
911910
boolean suspend = methodElement.isSuspend();
912-
ExpressionDef.InvokeInstanceMethod invoke = target.cast(proxyType).invoke(
913-
interceptedProxyBridgeMethodName,
914-
Arrays.stream(methodElement.getSuspendParameters()).map(p -> TypeDef.of(p.getType())).toList(),
915-
suspend ? TypeDef.OBJECT : TypeDef.of(methodElement.getReturnType()),
916-
IntStream.range(0, methodElement.getSuspendParameters().length).mapToObj(valuesArray::arrayElement).toList()
917-
);
911+
ExpressionDef.InvokeInstanceMethod invoke = target.cast(proxyType)
912+
.invoke(
913+
interceptedProxyBridgeMethod,
914+
IntStream.range(0, methodElement.getSuspendParameters().length).mapToObj(valuesArray::arrayElement).toList()
915+
);
918916
if (dispatchTarget.getMethodElement().getReturnType().isVoid() && !suspend) {
919917
return StatementDef.multi(
920918
invoke,

core-processor/src/main/java/io/micronaut/inject/writer/ExecutableMethodsDefinitionWriter.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,14 @@ public boolean isSuspend(int index) {
197197
* @param declaringType The declaring type of the method. Either a Class or a string representing the
198198
* name of the type
199199
* @param methodElement The method element
200-
* @param interceptedProxyClassName The intercepted proxy class name
201-
* @param interceptedProxyBridgeMethodName The intercepted proxy bridge method name
200+
* @param interceptedProxyType The intercepted proxy type
201+
* @param interceptedProxyBridgeMethod The intercepted proxy bridge method
202202
* @return The method index
203203
*/
204204
public int visitExecutableMethod(TypedElement declaringType,
205205
MethodElement methodElement,
206-
String interceptedProxyClassName,
207-
String interceptedProxyBridgeMethodName) {
206+
ClassTypeDef interceptedProxyType,
207+
MethodDef interceptedProxyBridgeMethod) {
208208
evaluatedExpressionProcessor.processEvaluatedExpressions(methodElement);
209209

210210
String methodKey = methodElement.getName() +
@@ -219,10 +219,10 @@ public int visitExecutableMethod(TypedElement declaringType,
219219
return index;
220220
}
221221
addedMethods.add(methodKey);
222-
if (interceptedProxyClassName == null) {
222+
if (interceptedProxyType == null) {
223223
return methodDispatchWriter.addMethod(declaringType, methodElement);
224224
} else {
225-
return methodDispatchWriter.addInterceptedMethod(declaringType, methodElement, interceptedProxyClassName, interceptedProxyBridgeMethodName);
225+
return methodDispatchWriter.addInterceptedMethod(declaringType, methodElement, interceptedProxyType, interceptedProxyBridgeMethod);
226226
}
227227
}
228228

core-processor/src/main/java/io/micronaut/inject/writer/MethodGenUtils.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private static ExpressionDef invokeKotlinDefaultMethod(ClassElement declaringTyp
167167
newValues.addAll(List.of(masks)); // Bit mask of defaults
168168
newValues.add(ExpressionDef.nullValue()); // Last parameter is just a marker and is always null
169169

170-
MethodDef defaultKotlinMethod = MethodGenUtils.asDefaultKotlinMethod(TypeDef.of(declaringType), methodElement, numberOfMasks);
170+
MethodDef defaultKotlinMethod = MethodGenUtils.asDefaultKotlinMethod(TypeDef.erasure(declaringType), methodElement, numberOfMasks);
171171

172172
return ClassTypeDef.of(declaringType).invokeStatic(defaultKotlinMethod, newValues);
173173
}

inject-java/src/test/groovy/io/micronaut/aop/compile/AroundCompileSpec.groovy

+108
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,114 @@ class TestInterceptor implements Interceptor {
142142
interceptor.invoked
143143
}
144144

145+
void 'test apply interceptor using interface return ENUM'() {
146+
given:
147+
ApplicationContext context = buildContext('''
148+
package test;
149+
150+
import java.lang.annotation.*;
151+
import io.micronaut.aop.*;
152+
import jakarta.inject.*;
153+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
154+
155+
interface IMyBean {
156+
@TestAnn
157+
<E extends Enum<E>> E test();
158+
}
159+
160+
@Singleton
161+
class MyBean implements IMyBean {
162+
163+
@Override
164+
public <E extends Enum<E>> E test() {
165+
return null;
166+
}
167+
168+
}
169+
170+
@Inherited
171+
@Retention(RUNTIME)
172+
@Target({ElementType.METHOD, ElementType.TYPE})
173+
@InterceptorBinding
174+
@interface TestAnn {
175+
}
176+
177+
@InterceptorBean(TestAnn.class)
178+
class TestInterceptor implements Interceptor {
179+
boolean invoked = false;
180+
@Override
181+
public Object intercept(InvocationContext context) {
182+
invoked = true;
183+
return context.proceed();
184+
}
185+
}
186+
187+
''')
188+
def instance = getBean(context, 'test.MyBean')
189+
def interceptor = getBean(context, 'test.TestInterceptor')
190+
191+
when:
192+
instance.test() == null
193+
194+
then:"the interceptor was invoked"
195+
instance instanceof Intercepted
196+
interceptor.invoked
197+
}
198+
199+
void 'test apply interceptor using interface parameter ENUM'() {
200+
given:
201+
ApplicationContext context = buildContext('''
202+
package test;
203+
204+
import java.lang.annotation.*;
205+
import io.micronaut.aop.*;
206+
import jakarta.inject.*;
207+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
208+
209+
interface IMyBean {
210+
@TestAnn
211+
<E extends Enum<E>> String test(E param);
212+
}
213+
214+
@Singleton
215+
class MyBean implements IMyBean {
216+
217+
@Override
218+
public <E extends Enum<E>> String test(E param) {
219+
return "sss";
220+
}
221+
222+
}
223+
224+
@Inherited
225+
@Retention(RUNTIME)
226+
@Target({ElementType.METHOD, ElementType.TYPE})
227+
@InterceptorBinding
228+
@interface TestAnn {
229+
}
230+
231+
@InterceptorBean(TestAnn.class)
232+
class TestInterceptor implements Interceptor {
233+
boolean invoked = false;
234+
@Override
235+
public Object intercept(InvocationContext context) {
236+
invoked = true;
237+
return context.proceed();
238+
}
239+
}
240+
241+
''')
242+
def instance = getBean(context, 'test.MyBean')
243+
def interceptor = getBean(context, 'test.TestInterceptor')
244+
245+
when:
246+
instance.test(null) == "sss"
247+
248+
then:"the interceptor was invoked"
249+
instance instanceof Intercepted
250+
interceptor.invoked
251+
}
252+
145253
void 'test stereotype method level interceptor matching'() {
146254
given:
147255
ApplicationContext context = buildContext('''

0 commit comments

Comments
 (0)