Skip to content

Commit

Permalink
Fix parameter expressions for executable methods (#10469)
Browse files Browse the repository at this point in the history
* Fix parameter expressions for executable methods

* extract private method (#10473)

* More tests

---------

Co-authored-by: Sergio del Amo <[email protected]>
  • Loading branch information
dstepanov and sdelamo authored Feb 9, 2024
1 parent e67d3a8 commit 919924b
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -335,6 +336,8 @@ private static final class DispatchedExecutableMethod<T, R> implements Executabl
private final MethodReference methodReference;
private AnnotationMetadata annotationMetadata;
private ReturnType<R> returnType;
private final Argument<?>[] arguments;
private final boolean argumentsAnnotationsWithExpressions;

private DispatchedExecutableMethod(AbstractExecutableMethodsDefinition dispatcher,
int index,
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -393,7 +407,7 @@ public String getMethodName() {

@Override
public Argument<?>[] getArguments() {
return methodReference.arguments;
return arguments;
}

@Override
Expand Down Expand Up @@ -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);
}
}

/**
Expand Down

0 comments on commit 919924b

Please sign in to comment.