Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mutated metadata being cleared before it was written in Groovy classes #6749

Merged
merged 5 commits into from Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -105,6 +105,7 @@ class TypeElementVisitorEnd implements ASTTransformation, CompilationUnitAware {
TypeElementVisitorTransform.loadedVisitors.remove()
TypeElementVisitorTransform.beanDefinitionBuilders.remove()
AstAnnotationUtils.invalidateCache()
AbstractAnnotationMetadataBuilder.clearMutated()
}

@Override
Expand Down
@@ -0,0 +1,31 @@
package io.micronaut.inject.visitor

import io.micronaut.ast.transform.test.AbstractBeanDefinitionSpec

class AnnotationMetadataSpec extends AbstractBeanDefinitionSpec {

void "test mutated metadata from a visitor is available on beans"() {
when:
def definition = buildBeanDefinition('test.TestListener', '''
package test

import io.micronaut.context.annotation.*
import jakarta.inject.Singleton
import io.micronaut.inject.visitor.SomeAnn

@Singleton
class TestListener {

@SomeAnn
@Executable
void receive(String v) {
}
}

''')

then:
noExceptionThrown() //asserts default values are available
definition.findMethod("receive", String).get().hasAnnotation('my.custom.Annotation')
}
}
@@ -0,0 +1,11 @@
package io.micronaut.inject.visitor

import io.micronaut.inject.ast.MethodElement

class MutatingVisitor implements TypeElementVisitor<Object, SomeAnn> {

void visitMethod(MethodElement element, VisitorContext context) {
element.annotate("my.custom.Annotation")
element.annotationMetadata.findAnnotation(SomeAnn).get().getRequiredValue("someValue", String)
}
}
@@ -1,5 +1,5 @@
package io.micronaut.inject.visitor

@interface SomeAnn {

String someValue() default "OK"
}
Expand Up @@ -2,4 +2,5 @@ io.micronaut.inject.visitor.TestInjectVisitor
io.micronaut.inject.visitor.AllElementsVisitor
io.micronaut.inject.visitor.AllClassesVisitor
io.micronaut.inject.visitor.ControllerGetVisitor
io.micronaut.inject.visitor.IntroductionVisitor
io.micronaut.inject.visitor.IntroductionVisitor
io.micronaut.inject.visitor.MutatingVisitor
Expand Up @@ -31,6 +31,7 @@
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.OptionalValues;
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder;
import io.micronaut.inject.annotation.AnnotationMetadataHierarchy;
import io.micronaut.inject.annotation.AnnotationMetadataReference;
import io.micronaut.inject.ast.*;
Expand Down Expand Up @@ -268,6 +269,7 @@ public final boolean process(Set<? extends TypeElement> annotations, RoundEnviro
}
} finally {
AnnotationUtils.invalidateCache();
AbstractAnnotationMetadataBuilder.clearMutated();
JavaAnnotationMetadataBuilder.clearCaches();
}
}
Expand Down
Expand Up @@ -2004,8 +2004,15 @@ public static boolean isMetadataMutated(String declaringType, Object element) {
* Used to clear mutated metadata at the end of a compilation cycle.
*/
@Internal
public static void clearCaches() {
public static void clearMutated() {
MUTATED_ANNOTATION_METADATA.clear();
}

/**
* Used to clear caches at the end of a compilation cycle.
*/
@Internal
public static void clearCaches() {
ANNOTATION_DEFAULTS.clear();
}

Expand Down
Expand Up @@ -982,7 +982,7 @@ <T> Optional<T> getDefaultValue(@NonNull String annotation, @NonNull String memb
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);

Map<String, Object> defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation);
Map<String, Object> defaultValues = getDefaultValues(annotation);
if (defaultValues.containsKey(member)) {
final Object v = defaultValues.get(member);
if (requiredType.isInstance(v)) {
Expand Down Expand Up @@ -1270,11 +1270,11 @@ <T extends Annotation> Optional<AnnotationValue<T>> findAnnotation(@NonNull Stri
if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map<CharSequence, Object> values = allAnnotations.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
} else if (allStereotypes != null) {
values = allStereotypes.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
}
}
}
Expand All @@ -1289,11 +1289,11 @@ <T extends Annotation> Optional<AnnotationValue<T>> findDeclaredAnnotation(@NonN
if (declaredAnnotations != null && StringUtils.isNotEmpty(annotation)) {
Map<CharSequence, Object> values = declaredAnnotations.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
} else if (declaredStereotypes != null) {
values = declaredStereotypes.get(annotation);
if (values != null) {
return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation)));
return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation)));
}
}
}
Expand Down Expand Up @@ -1344,7 +1344,7 @@ <T> Optional<T> getDefaultValue(@NonNull String annotation, @NonNull String memb
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);
// Note this method should never reference the "annotationDefaultValues" field, which is used only at compile time
Map<String, Object> defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation);
Map<String, Object> defaultValues = getDefaultValues(annotation);
if (defaultValues.containsKey(member)) {
return ConversionService.SHARED.convert(defaultValues.get(member), requiredType);
}
Expand Down
Expand Up @@ -20,6 +20,7 @@
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;
import org.jetbrains.annotations.NotNull;

import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
Expand All @@ -30,6 +31,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* A mutable various of {@link DefaultAnnotationMetadata} that is used only at build time.
Expand All @@ -38,6 +40,7 @@
* @since 2.4.0
*/
public class MutableAnnotationMetadata extends DefaultAnnotationMetadata {

private boolean hasPropertyExpressions = false;

/**
Expand Down Expand Up @@ -82,6 +85,20 @@ public MutableAnnotationMetadata clone() {
return cloned;
}

@NotNull
@Override
public Map<String, Object> getDefaultValues(@NonNull String annotation) {
Map<String, Object> values = super.getDefaultValues(annotation);
if (values.isEmpty() && annotationDefaultValues != null) {
final Map<CharSequence, Object> compileTimeDefaults = annotationDefaultValues.get(annotation);
if (compileTimeDefaults != null && !compileTimeDefaults.isEmpty()) {
return compileTimeDefaults.entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey().toString(), Map.Entry::getValue));
}
}
return values;
}

@Override
public <A extends Annotation> void removeAnnotationIf(@NonNull Predicate<AnnotationValue<A>> predicate) {
super.removeAnnotationIf(predicate);
Expand Down