diff --git a/core-processor/src/main/java/io/micronaut/inject/writer/EvaluatedExpressionProcessor.java b/core-processor/src/main/java/io/micronaut/inject/writer/EvaluatedExpressionProcessor.java index be04ef7a96e..e2f42e50407 100644 --- a/core-processor/src/main/java/io/micronaut/inject/writer/EvaluatedExpressionProcessor.java +++ b/core-processor/src/main/java/io/micronaut/inject/writer/EvaluatedExpressionProcessor.java @@ -26,8 +26,10 @@ import io.micronaut.expressions.util.EvaluatedExpressionsUtils; import io.micronaut.inject.annotation.AnnotationMetadataHierarchy; import io.micronaut.inject.ast.ClassElement; +import io.micronaut.inject.ast.ConstructorElement; import io.micronaut.inject.ast.Element; import io.micronaut.inject.ast.MethodElement; +import io.micronaut.inject.ast.ParameterElement; import io.micronaut.inject.visitor.VisitorContext; import java.io.IOException; @@ -95,6 +97,11 @@ public void processEvaluatedExpressions(MethodElement methodElement) { return new ExpressionWithContext(expression, evaluationContext); }) .forEach(this::addExpression); + + ClassElement resolvedThis = methodElement.isStatic() || methodElement instanceof ConstructorElement ? null : methodElement.getOwningType(); + for (ParameterElement parameter: methodElement.getParameters()) { + processEvaluatedExpressions(parameter.getAnnotationMetadata(), resolvedThis); + } } private void addExpression(ExpressionWithContext ee) { diff --git a/inject-java/src/test/groovy/io/micronaut/expressions/ParameterExpressionsSpec.groovy b/inject-java/src/test/groovy/io/micronaut/expressions/ParameterExpressionsSpec.groovy new file mode 100644 index 00000000000..40413259dfd --- /dev/null +++ b/inject-java/src/test/groovy/io/micronaut/expressions/ParameterExpressionsSpec.groovy @@ -0,0 +1,96 @@ +package io.micronaut.expressions + +import io.micronaut.annotation.processing.test.AbstractEvaluatedExpressionsSpec +import io.micronaut.context.annotation.Value +import io.micronaut.inject.annotation.EvaluatedAnnotationValue + +class ParameterExpressionsSpec extends AbstractEvaluatedExpressionsSpec { + + void "test evaluating method parameter expression"() { + given: + def ctx = buildContext('tst.MyBean', """ + + package tst; + + import io.micronaut.context.annotation.Executable; + import io.micronaut.context.annotation.Value; + import jakarta.inject.Inject; + import jakarta.inject.Singleton; + + @Singleton + class MyBean { + String someParam; + @Executable + void doStuff(@Value("#{1 + 1 + this.myNumber()}") String someParam) { + this.someParam = someParam; + } + + int myNumber() { + return 5; + } + } + + """) + + + def bean = ctx.getBean(ctx.getClassLoader().loadClass("tst.MyBean")) + def beanDefinition = ctx.getBeanDefinition(ctx.getClassLoader().loadClass("tst.MyBean")) + when: + def av = beanDefinition.getRequiredMethod("doStuff", String.class).getArguments()[0].getAnnotationMetadata().getAnnotation(Value.class) + then: + (av as EvaluatedAnnotationValue).withArguments(bean, "MyValue").stringValue().get() == "7" + when: + buildContext('tst.MyBean', """ + + package tst; + + import io.micronaut.context.annotation.Executable; + import io.micronaut.context.annotation.Value; + import jakarta.inject.Inject; + import jakarta.inject.Singleton; + + @Singleton + class MyBean { + + @Executable + static void doStuffStatic(@Value("#{1 + 1 + this.myNumber()}") String someParam) { + } + + int myNumber() { + return 5; + } + } + + """) + then: + def e = thrown(Exception) + e.message.contains("Cannot reference 'this'") + when: + buildContext('tst.MyBean2', """ + + package tst; + + import io.micronaut.context.annotation.Executable; + import io.micronaut.context.annotation.Value; + import jakarta.inject.Inject; + import jakarta.inject.Singleton; + + @Singleton + class MyBean2 { + String someParam; + MyBean2(@Value("#{1 + 1 + this.myNumber()}") String someParam) { + this.someParam = someParam; + } + + } + + """) + then: + def ee = thrown(Exception) + ee.message.contains("Cannot reference 'this'") + + cleanup: + ctx.stop() + } + +} diff --git a/inject/src/main/java/io/micronaut/context/AbstractExecutableMethodsDefinition.java b/inject/src/main/java/io/micronaut/context/AbstractExecutableMethodsDefinition.java index 3257421f25c..31c8f4597ad 100644 --- a/inject/src/main/java/io/micronaut/context/AbstractExecutableMethodsDefinition.java +++ b/inject/src/main/java/io/micronaut/context/AbstractExecutableMethodsDefinition.java @@ -23,6 +23,7 @@ import io.micronaut.core.annotation.UsedByGeneratedCode; import io.micronaut.core.reflect.ClassUtils; import io.micronaut.core.type.Argument; +import io.micronaut.core.type.GenericPlaceholder; import io.micronaut.core.type.ReturnType; import io.micronaut.core.type.UnsafeExecutable; import io.micronaut.core.util.ArgumentUtils; @@ -335,6 +336,8 @@ private static final class DispatchedExecutableMethod implements Executabl private final MethodReference methodReference; private AnnotationMetadata annotationMetadata; private ReturnType returnType; + private final Argument[] arguments; + private final boolean argumentsAnnotationsWithExpressions; private DispatchedExecutableMethod(AbstractExecutableMethodsDefinition dispatcher, int index, @@ -344,6 +347,9 @@ private DispatchedExecutableMethod(AbstractExecutableMethodsDefinition dispatche this.index = index; this.methodReference = methodReference; this.annotationMetadata = annotationMetadata; + MethodArguments methodArguments = methodArguments(methodReference); + this.arguments = methodArguments.arguments; + this.argumentsAnnotationsWithExpressions = methodArguments.argumentsAnnotationsWithExpressions; } @Override @@ -359,6 +365,14 @@ public void configure(BeanContext beanContext) { if (annotationMetadata instanceof EvaluatedAnnotationMetadata eam) { eam.configure(beanContext); } + if (argumentsAnnotationsWithExpressions) { + for (Argument argument : arguments) { + AnnotationMetadata argumentAnnotationMetadata = argument.getAnnotationMetadata(); + if (argumentAnnotationMetadata instanceof EvaluatedAnnotationMetadata eam) { + eam.configure(beanContext); + } + } + } } @Override @@ -393,7 +407,7 @@ public String getMethodName() { @Override public Argument[] getArguments() { - return methodReference.arguments; + return arguments; } @Override @@ -475,6 +489,30 @@ public String toString() { return getReturnType().getType().getSimpleName() + " " + getMethodName() + "(" + text + ")"; } + private record MethodArguments(Argument[] arguments, boolean argumentsAnnotationsWithExpressions) { + } + + private MethodArguments methodArguments(MethodReference methodReference) { + final Argument[] arguments= new Argument[methodReference.arguments.length]; + boolean foundExpressions = false; + Argument[] methodArguments = methodReference.arguments; + for (int i = 0; i < methodArguments.length; i++) { + Argument argument = methodArguments[i]; + AnnotationMetadata argumentAnnotationMetadata = argument.getAnnotationMetadata(); + AnnotationMetadata wrappedArgumentAnnotationMetadata = EvaluatedAnnotationMetadata.wrapIfNecessary(argumentAnnotationMetadata); + if (argumentAnnotationMetadata == wrappedArgumentAnnotationMetadata) { + arguments[i] = argument; + } else { + foundExpressions = true; + if (argument instanceof GenericPlaceholder genericPlaceholder) { + arguments[i] = Argument.ofTypeVariable(argument.getType(), argument.getName(), genericPlaceholder.getVariableName(), wrappedArgumentAnnotationMetadata, argument.getTypeParameters()); + } else { + arguments[i] = Argument.of(argument.getType(), argument.getName(), wrappedArgumentAnnotationMetadata, argument.getTypeParameters()); + } + } + } + return new MethodArguments(arguments, foundExpressions); + } } /**