From 58eada962bdf2f64a89388f8c1d92d8629f73e29 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Wed, 16 Mar 2022 15:57:19 +0100 Subject: [PATCH] ArC - injection point validation - type variable is not a legal injection point type - also validate type params of types related to programmatic lookup, i.e. Instance<> and List<> with All qualifier --- docs/src/main/asciidoc/cdi-reference.adoc | 2 + .../quarkus/arc/deployment/ArcProcessor.java | 16 +- .../test/lookup/ListInvalidTypeParamTest.java | 51 +++ .../inject/InjectionFailedTest.java | 2 +- .../io/quarkus/arc/processor/BeanInfo.java | 6 +- .../java/io/quarkus/arc/processor/Beans.java | 16 +- .../io/quarkus/arc/processor/BuiltinBean.java | 364 +++++++++++------- .../arc/processor/InjectionPointInfo.java | 2 +- .../TypeVariableFieldInjectionPointTest.java | 37 ++ ...VariableInitializerInjectionPointTest.java | 38 ++ ...ypeVariableInstanceInjectionPointTest.java | 38 ++ .../WildcardInstanceInjectionPointTest.java | 38 ++ .../test/unused/RemoveUnusedBeansTest.java | 13 +- 13 files changed, 449 insertions(+), 174 deletions(-) create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInvalidTypeParamTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableFieldInjectionPointTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableInitializerInjectionPointTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/TypeVariableInstanceInjectionPointTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/WildcardInstanceInjectionPointTest.java diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc index fb57163d3a5e1..2c38b95a96ee2 100644 --- a/docs/src/main/asciidoc/cdi-reference.adoc +++ b/docs/src/main/asciidoc/cdi-reference.adoc @@ -958,6 +958,8 @@ public class Processor { } ---- +NOTE: Neither a type variable nor a wildcard is a legal type parameter for an `@All List<>` injection point, i.e. `@Inject @All List all` is not supported and results in a deployment error. + === Ignoring Class-Level Interceptor Bindings for Methods and Constructors If a managed bean declares interceptor binding annotations on the class level, the corresponding `@AroundInvoke` interceptors will apply to all business methods. diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java index 7c694505a9a71..6b2df3ce56c7f 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java @@ -19,6 +19,7 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.AmbiguousResolutionException; import javax.enterprise.inject.UnsatisfiedResolutionException; +import javax.enterprise.inject.spi.DefinitionException; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; @@ -422,7 +423,8 @@ public ObserverRegistrationPhaseBuildItem registerSyntheticObservers(BeanRegistr List beanConfigurators, BuildProducer reflectiveMethods, BuildProducer reflectiveFields, - BuildProducer unremovableBeans) { + BuildProducer unremovableBeans, + BuildProducer validationErrors) { for (BeanConfiguratorBuildItem configurator : beanConfigurators) { // Just make sure the configurator is processed @@ -435,6 +437,18 @@ public ObserverRegistrationPhaseBuildItem registerSyntheticObservers(BeanRegistr // Register a synthetic bean for each List with qualifier @All List listAll = beanRegistrationPhase.getInjectionPoints().stream() .filter(this::isListAllInjectionPoint).collect(Collectors.toList()); + for (InjectionPointInfo injectionPoint : listAll) { + // Note that at this point we can be sure that the required type is List<> + Type typeParam = injectionPoint.getType().asParameterizedType().arguments().get(0); + if (typeParam.kind() == Type.Kind.WILDCARD_TYPE) { + validationErrors.produce(new ValidationErrorBuildItem( + new DefinitionException( + "Wildcard is not a legal type argument for " + injectionPoint.getTargetInfo()))); + } else if (typeParam.kind() == Type.Kind.TYPE_VARIABLE) { + validationErrors.produce(new ValidationErrorBuildItem(new DefinitionException( + "Type variable is not a legal type argument for " + injectionPoint.getTargetInfo()))); + } + } if (!listAll.isEmpty()) { registerListInjectionPointsBeans(beanRegistrationPhase, listAll, reflectiveMethods, reflectiveFields, unremovableBeans); diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInvalidTypeParamTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInvalidTypeParamTest.java new file mode 100644 index 0000000000000..1c9f5679ba5c2 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInvalidTypeParamTest.java @@ -0,0 +1,51 @@ +package io.quarkus.arc.test.lookup; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import javax.enterprise.inject.spi.DeploymentException; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.All; +import io.quarkus.runtime.util.ExceptionUtil; +import io.quarkus.test.QuarkusUnitTest; + +public class ListInvalidTypeParamTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(root -> root.addClasses(Foo.class)).assertException(t -> { + Throwable rootCause = ExceptionUtil.getRootCause(t); + assertTrue(rootCause instanceof DeploymentException); + String suppressedMessage = Arrays.stream(rootCause.getSuppressed()).map(Throwable::getMessage) + .collect(Collectors.joining("::")); + assertTrue(suppressedMessage.contains("Type variable is not a legal type argument"), rootCause.toString()); + assertTrue(suppressedMessage.contains("Wildcard is not a legal type argument"), rootCause.toString()); + }); + + @Test + public void testFailure() { + fail(); + } + + @Singleton + static class Foo { + + @Inject + @All + List services; + + @All + List counters; + + } + +} diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectionFailedTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectionFailedTest.java index c9785179ca903..49e44a7077d2b 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectionFailedTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectionFailedTest.java @@ -37,7 +37,7 @@ public class InjectionFailedTest { assertTrue(s.getMessage().contains( "No template found for path [foo] defined at io.quarkus.qute.deployment.inject.InjectionFailedTest#foo") || s.getMessage().contains( - "No template found for path [alpha] defined at io.quarkus.qute.deployment.inject.InjectionFailedTest$Client().alpha"), + "No template found for path [alpha] defined at io.quarkus.qute.deployment.inject.InjectionFailedTest$Client()"), s.getMessage()); } }); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index bbfd4ef080899..e3ca6cad3eaf3 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -520,8 +520,12 @@ void init(List errors, Consumer bytecodeTransfor if (injectionPoint.isDelegate() && !isDecorator()) { errors.add(new DeploymentException(String.format( "Only decorators can declare a delegate injection point: %s", this))); + } else if (injectionPoint.getType().kind() == org.jboss.jandex.Type.Kind.TYPE_VARIABLE) { + errors.add(new DefinitionException(String.format("Type variable is not a legal injection point type: %s", + injectionPoint.getTargetInfo()))); + } else { + Beans.resolveInjectionPoint(beanDeployment, this, injectionPoint, errors); } - Beans.resolveInjectionPoint(beanDeployment, this, injectionPoint, errors); } } if (disposer != null) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java index 607abf67052ee..4a8eeaa552a7f 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java @@ -3,7 +3,6 @@ import static io.quarkus.arc.processor.IndexClassLookupUtils.getClassByName; import io.quarkus.arc.processor.InjectionPointInfo.TypeAndQualifiers; -import io.quarkus.arc.processor.InjectionTargetInfo.TargetKind; import io.quarkus.gizmo.Gizmo; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -387,20 +386,7 @@ static void resolveInjectionPoint(BeanDeployment deployment, InjectionTargetInfo } BuiltinBean builtinBean = BuiltinBean.resolve(injectionPoint); if (builtinBean != null) { - if (BuiltinBean.INJECTION_POINT == builtinBean - && (target.kind() != TargetKind.BEAN || !BuiltinScope.DEPENDENT.is(target.asBean().getScope()))) { - errors.add(new DefinitionException("Only @Dependent beans can access metadata about an injection point: " - + injectionPoint.getTargetInfo())); - } else if (BuiltinBean.EVENT_METADATA == builtinBean - && target.kind() != TargetKind.OBSERVER) { - errors.add(new DefinitionException("EventMetadata can be only injected into an observer method: " - + injectionPoint.getTargetInfo())); - } else if (BuiltinBean.INSTANCE == builtinBean - && injectionPoint.getType().kind() != Kind.PARAMETERIZED_TYPE) { - errors.add( - new DefinitionException("An injection point of raw type javax.enterprise.inject.Instance is defined: " - + injectionPoint.getTargetInfo())); - } + builtinBean.validate(target, injectionPoint, errors::add); // Skip built-in beans return; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java index 3bd1b9c895f56..3290abb89e786 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java @@ -11,6 +11,7 @@ import io.quarkus.arc.impl.InterceptedBeanMetadataProvider; import io.quarkus.arc.impl.ResourceProvider; import io.quarkus.arc.processor.InjectionPointInfo.InjectionPointKind; +import io.quarkus.arc.processor.InjectionTargetInfo.TargetKind; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassOutput; import io.quarkus.gizmo.FieldDescriptor; @@ -20,11 +21,15 @@ import java.lang.reflect.Member; import java.util.HashSet; import java.util.Set; +import java.util.function.BiPredicate; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; +import javax.enterprise.inject.spi.DefinitionException; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.Type.Kind; /** * @@ -32,7 +37,149 @@ */ enum BuiltinBean { - INSTANCE(ctx -> { + INSTANCE(BuiltinBean::generateInstanceBytecode, BuiltinBean::cdiAndRawTypeMatches, + BuiltinBean::validateInstance, DotNames.INSTANCE, DotNames.PROVIDER, DotNames.INJECTABLE_INSTANCE), + INJECTION_POINT(BuiltinBean::generateInjectionPointBytecode, BuiltinBean::cdiAndRawTypeMatches, + BuiltinBean::validateInjectionPoint, DotNames.INJECTION_POINT), + BEAN(BuiltinBean::generateBeanBytecode, + (ip, names) -> cdiAndRawTypeMatches(ip, DotNames.BEAN, DotNames.INJECTABLE_BEAN) && ip.hasDefaultedQualifier(), + DotNames.BEAN), + + INTERCEPTED_BEAN(BuiltinBean::generateInterceptedBeanBytecode, + (ip, names) -> cdiAndRawTypeMatches(ip, DotNames.BEAN, DotNames.INJECTABLE_BEAN) && !ip.hasDefaultedQualifier() + && ip.getRequiredQualifiers().size() == 1 + && ip.getRequiredQualifiers().iterator().next().name().equals(DotNames.INTERCEPTED), + DotNames.BEAN), + BEAN_MANAGER(BuiltinBean::generateBeanManagerBytecode, DotNames.BEAN_MANAGER), + EVENT(BuiltinBean::generateEventBytecode, DotNames.EVENT), + RESOURCE(BuiltinBean::generateResourceBytecode, (ip, names) -> ip.getKind() == InjectionPointKind.RESOURCE, + DotNames.OBJECT), + EVENT_METADATA(Generator.NOOP, BuiltinBean::cdiAndRawTypeMatches, + BuiltinBean::validateEventMetadata, DotNames.EVENT_METADATA), + ; + + private final DotName[] rawTypeDotNames; + private final Generator generator; + private final BiPredicate matcher; + private final Validator validator; + + private BuiltinBean(Generator generator, DotName... rawTypeDotNames) { + this(generator, BuiltinBean::cdiAndRawTypeMatches, rawTypeDotNames); + } + + private BuiltinBean(Generator generator, BiPredicate matcher, DotName... rawTypeDotNames) { + this(generator, matcher, Validator.NOOP, rawTypeDotNames); + } + + private BuiltinBean(Generator generator, BiPredicate matcher, Validator validator, + DotName... rawTypeDotNames) { + this.rawTypeDotNames = rawTypeDotNames; + this.generator = generator; + this.matcher = matcher; + this.validator = validator; + } + + boolean matches(InjectionPointInfo injectionPoint) { + return matcher.test(injectionPoint, rawTypeDotNames); + } + + void validate(InjectionTargetInfo injectionTarget, InjectionPointInfo injectionPoint, Consumer errors) { + validator.validate(injectionTarget, injectionPoint, errors); + } + + DotName[] getRawTypeDotNames() { + return rawTypeDotNames; + } + + boolean hasRawTypeDotName(DotName name) { + for (DotName rawTypeDotName : rawTypeDotNames) { + if (rawTypeDotName.equals(name)) { + return true; + } + } + return false; + } + + Generator getGenerator() { + return generator; + } + + static boolean resolvesTo(InjectionPointInfo injectionPoint) { + return resolve(injectionPoint) != null; + } + + static BuiltinBean resolve(InjectionPointInfo injectionPoint) { + for (BuiltinBean bean : values()) { + if (bean.matches(injectionPoint)) { + return bean; + } + } + return null; + } + + public static class GeneratorContext { + + final ClassOutput classOutput; + final BeanDeployment beanDeployment; + final InjectionPointInfo injectionPoint; + final ClassCreator clazzCreator; + final MethodCreator constructor; + final String providerName; + final AnnotationLiteralProcessor annotationLiterals; + final InjectionTargetInfo targetInfo; + final ReflectionRegistration reflectionRegistration; + final Predicate injectionPointAnnotationsPredicate; + + public GeneratorContext(ClassOutput classOutput, BeanDeployment beanDeployment, InjectionPointInfo injectionPoint, + ClassCreator clazzCreator, MethodCreator constructor, String providerName, + AnnotationLiteralProcessor annotationLiterals, InjectionTargetInfo targetInfo, + ReflectionRegistration reflectionRegistration, Predicate injectionPointAnnotationsPredicate) { + this.classOutput = classOutput; + this.beanDeployment = beanDeployment; + this.injectionPoint = injectionPoint; + this.clazzCreator = clazzCreator; + this.constructor = constructor; + this.providerName = providerName; + this.annotationLiterals = annotationLiterals; + this.targetInfo = targetInfo; + this.reflectionRegistration = reflectionRegistration; + this.injectionPointAnnotationsPredicate = injectionPointAnnotationsPredicate; + } + } + + @FunctionalInterface + interface Generator { + + Generator NOOP = ctx -> { + }; + + void generate(GeneratorContext context); + + } + + @FunctionalInterface + interface Validator { + + Validator NOOP = (it, ip, e) -> { + }; + + void validate(InjectionTargetInfo injectionTarget, InjectionPointInfo injectionPoint, Consumer errors); + + } + + private static boolean cdiAndRawTypeMatches(InjectionPointInfo injectionPoint, DotName... rawTypeDotNames) { + if (injectionPoint.getKind() != InjectionPointKind.CDI) { + return false; + } + for (DotName rawTypeDotName : rawTypeDotNames) { + if (rawTypeDotName.equals(injectionPoint.getType().name())) { + return true; + } + } + return false; + } + + private static void generateInstanceBytecode(GeneratorContext ctx) { ResultHandle qualifiers = BeanGenerator.collectInjectionPointQualifiers(ctx.classOutput, ctx.clazzCreator, ctx.beanDeployment, ctx.constructor, ctx.injectionPoint, ctx.annotationLiterals); @@ -66,8 +213,43 @@ enum BuiltinBean { FieldDescriptor.of(ctx.clazzCreator.getClassName(), ctx.providerName, Supplier.class.getName()), ctx.constructor.getThis(), instanceProviderSupplier); - }, DotNames.INSTANCE, DotNames.PROVIDER, DotNames.INJECTABLE_INSTANCE), - INJECTION_POINT(ctx -> { + } + + private static void generateEventBytecode(GeneratorContext ctx) { + ResultHandle qualifiers = ctx.constructor.newInstance(MethodDescriptor.ofConstructor(HashSet.class)); + if (!ctx.injectionPoint.getRequiredQualifiers().isEmpty()) { + // Set instanceProvider1Qualifiers = new HashSet<>() + // instanceProvider1Qualifiers.add(javax.enterprise.inject.Default.Literal.INSTANCE) + + for (AnnotationInstance qualifierAnnotation : ctx.injectionPoint.getRequiredQualifiers()) { + BuiltinQualifier qualifier = BuiltinQualifier.of(qualifierAnnotation); + if (qualifier != null) { + ctx.constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, qualifiers, + qualifier.getLiteralInstance(ctx.constructor)); + } else { + // Create annotation literal first + ClassInfo qualifierClass = ctx.beanDeployment.getQualifier(qualifierAnnotation.name()); + ctx.constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, qualifiers, + ctx.annotationLiterals.process(ctx.constructor, ctx.classOutput, + qualifierClass, qualifierAnnotation, + Types.getPackageName(ctx.clazzCreator.getClassName()))); + } + } + } + ResultHandle parameterizedType = Types.getTypeHandle(ctx.constructor, ctx.injectionPoint.getType()); + ResultHandle eventProvider = ctx.constructor.newInstance( + MethodDescriptor.ofConstructor(EventProvider.class, java.lang.reflect.Type.class, + Set.class), + parameterizedType, qualifiers); + ResultHandle eventProviderSupplier = ctx.constructor.newInstance( + MethodDescriptors.FIXED_VALUE_SUPPLIER_CONSTRUCTOR, eventProvider); + ctx.constructor.writeInstanceField( + FieldDescriptor.of(ctx.clazzCreator.getClassName(), ctx.providerName, + Supplier.class.getName()), + ctx.constructor.getThis(), eventProviderSupplier); + } + + private static void generateInjectionPointBytecode(GeneratorContext ctx) { // this.injectionPointProvider1 = () -> new InjectionPointProvider(); ResultHandle injectionPointProvider = ctx.constructor.newInstance( MethodDescriptor.ofConstructor(InjectionPointProvider.class)); @@ -78,8 +260,9 @@ enum BuiltinBean { Supplier.class.getName()), ctx.constructor.getThis(), injectionPointProviderSupplier); - }, DotNames.INJECTION_POINT), - BEAN(ctx -> { + } + + private static void generateBeanBytecode(GeneratorContext ctx) { // this.beanProvider1 = () -> new BeanMetadataProvider<>(); if (ctx.targetInfo.kind() != InjectionTargetInfo.TargetKind.BEAN) { throw new IllegalStateException("Invalid injection target info: " + ctx.targetInfo); @@ -94,10 +277,9 @@ enum BuiltinBean { Supplier.class.getName()), ctx.constructor.getThis(), beanProviderSupplier); - }, ip -> { - return isCdiAndRawTypeMatches(ip, DotNames.BEAN, DotNames.INJECTABLE_BEAN) && ip.hasDefaultedQualifier(); - }, DotNames.BEAN), - INTERCEPTED_BEAN(ctx -> { + } + + private static void generateInterceptedBeanBytecode(GeneratorContext ctx) { if (!(ctx.targetInfo instanceof InterceptorInfo)) { throw new IllegalStateException("Invalid injection target info: " + ctx.targetInfo); } @@ -111,12 +293,9 @@ enum BuiltinBean { Supplier.class.getName()), ctx.constructor.getThis(), interceptedBeanMetadataProviderSupplier); - }, ip -> { - return isCdiAndRawTypeMatches(ip, DotNames.BEAN, DotNames.INJECTABLE_BEAN) && !ip.hasDefaultedQualifier() - && ip.getRequiredQualifiers().size() == 1 - && ip.getRequiredQualifiers().iterator().next().name().equals(DotNames.INTERCEPTED); - }, DotNames.BEAN), - BEAN_MANAGER(ctx -> { + } + + private static void generateBeanManagerBytecode(GeneratorContext ctx) { ResultHandle beanManagerProvider = ctx.constructor.newInstance( MethodDescriptor.ofConstructor(BeanManagerProvider.class)); ResultHandle injectionPointProviderSupplier = ctx.constructor.newInstance( @@ -126,43 +305,9 @@ enum BuiltinBean { Supplier.class.getName()), ctx.constructor.getThis(), injectionPointProviderSupplier); - }, DotNames.BEAN_MANAGER), - EVENT(ctx -> { - - ResultHandle qualifiers = ctx.constructor.newInstance(MethodDescriptor.ofConstructor(HashSet.class)); - if (!ctx.injectionPoint.getRequiredQualifiers().isEmpty()) { - // Set instanceProvider1Qualifiers = new HashSet<>() - // instanceProvider1Qualifiers.add(javax.enterprise.inject.Default.Literal.INSTANCE) - - for (AnnotationInstance qualifierAnnotation : ctx.injectionPoint.getRequiredQualifiers()) { - BuiltinQualifier qualifier = BuiltinQualifier.of(qualifierAnnotation); - if (qualifier != null) { - ctx.constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, qualifiers, - qualifier.getLiteralInstance(ctx.constructor)); - } else { - // Create annotation literal first - ClassInfo qualifierClass = ctx.beanDeployment.getQualifier(qualifierAnnotation.name()); - ctx.constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, qualifiers, - ctx.annotationLiterals.process(ctx.constructor, ctx.classOutput, - qualifierClass, qualifierAnnotation, - Types.getPackageName(ctx.clazzCreator.getClassName()))); - } - } - } - ResultHandle parameterizedType = Types.getTypeHandle(ctx.constructor, ctx.injectionPoint.getType()); - ResultHandle eventProvider = ctx.constructor.newInstance( - MethodDescriptor.ofConstructor(EventProvider.class, java.lang.reflect.Type.class, - Set.class), - parameterizedType, qualifiers); - ResultHandle eventProviderSupplier = ctx.constructor.newInstance( - MethodDescriptors.FIXED_VALUE_SUPPLIER_CONSTRUCTOR, eventProvider); - ctx.constructor.writeInstanceField( - FieldDescriptor.of(ctx.clazzCreator.getClassName(), ctx.providerName, - Supplier.class.getName()), - ctx.constructor.getThis(), eventProviderSupplier); - }, DotNames.EVENT), - RESOURCE(ctx -> { + } + private static void generateResourceBytecode(GeneratorContext ctx) { ResultHandle annotations = ctx.constructor.newInstance(MethodDescriptor.ofConstructor(HashSet.class)); // For a resource field the required qualifiers contain all annotations declared on the field if (!ctx.injectionPoint.getRequiredQualifiers().isEmpty()) { @@ -186,108 +331,39 @@ enum BuiltinBean { FieldDescriptor.of(ctx.clazzCreator.getClassName(), ctx.providerName, Supplier.class.getName()), ctx.constructor.getThis(), resourceProviderSupplier); - }, ip -> ip.getKind() == InjectionPointKind.RESOURCE, DotNames.OBJECT), - EVENT_METADATA(ctx -> { - }, DotNames.EVENT_METADATA), - ; - - private final DotName[] rawTypeDotNames; - - private final Generator generator; - - private final Predicate matcher; - - BuiltinBean(Generator generator, DotName... rawTypeDotNames) { - this(generator, ip -> isCdiAndRawTypeMatches(ip, rawTypeDotNames), rawTypeDotNames); - } - - BuiltinBean(Generator generator, Predicate matcher, DotName... rawTypeDotNames) { - this.rawTypeDotNames = rawTypeDotNames; - this.generator = generator; - this.matcher = matcher; - } - - boolean matches(InjectionPointInfo injectionPoint) { - return matcher.test(injectionPoint); - } - - DotName[] getRawTypeDotNames() { - return rawTypeDotNames; } - boolean hasRawTypeDotName(DotName name) { - for (DotName rawTypeDotName : rawTypeDotNames) { - if (rawTypeDotName.equals(name)) { - return true; - } + private static void validateInstance(InjectionTargetInfo injectionTarget, InjectionPointInfo injectionPoint, + Consumer errors) { + if (injectionPoint.getType().kind() != Kind.PARAMETERIZED_TYPE) { + errors.accept( + new DefinitionException("An injection point of raw type javax.enterprise.inject.Instance is defined: " + + injectionPoint.getTargetInfo())); + } else if (injectionPoint.getRequiredType().kind() == Kind.WILDCARD_TYPE) { + errors.accept( + new DefinitionException("Wildcard is not a legal type argument for javax.enterprise.inject.Instance: " + + injectionPoint.getTargetInfo())); + } else if (injectionPoint.getRequiredType().kind() == Kind.TYPE_VARIABLE) { + errors.accept(new DefinitionException( + "Type variable is not a legal type argument for javax.enterprise.inject.Instance: " + + injectionPoint.getTargetInfo())); } - return false; } - Generator getGenerator() { - return generator; - } - - static boolean resolvesTo(InjectionPointInfo injectionPoint) { - return resolve(injectionPoint) != null; - } - - static BuiltinBean resolve(InjectionPointInfo injectionPoint) { - for (BuiltinBean bean : values()) { - if (bean.matches(injectionPoint)) { - return bean; - } + private static void validateInjectionPoint(InjectionTargetInfo injectionTarget, InjectionPointInfo injectionPoint, + Consumer errors) { + if (injectionTarget.kind() != TargetKind.BEAN || !BuiltinScope.DEPENDENT.is(injectionTarget.asBean().getScope())) { + errors.accept(new DefinitionException("Only @Dependent beans can access metadata about an injection point: " + + injectionPoint.getTargetInfo())); } - return null; } - public static class GeneratorContext { - - final ClassOutput classOutput; - final BeanDeployment beanDeployment; - final InjectionPointInfo injectionPoint; - final ClassCreator clazzCreator; - final MethodCreator constructor; - final String providerName; - final AnnotationLiteralProcessor annotationLiterals; - final InjectionTargetInfo targetInfo; - final ReflectionRegistration reflectionRegistration; - final Predicate injectionPointAnnotationsPredicate; - - public GeneratorContext(ClassOutput classOutput, BeanDeployment beanDeployment, InjectionPointInfo injectionPoint, - ClassCreator clazzCreator, MethodCreator constructor, String providerName, - AnnotationLiteralProcessor annotationLiterals, InjectionTargetInfo targetInfo, - ReflectionRegistration reflectionRegistration, Predicate injectionPointAnnotationsPredicate) { - this.classOutput = classOutput; - this.beanDeployment = beanDeployment; - this.injectionPoint = injectionPoint; - this.clazzCreator = clazzCreator; - this.constructor = constructor; - this.providerName = providerName; - this.annotationLiterals = annotationLiterals; - this.targetInfo = targetInfo; - this.reflectionRegistration = reflectionRegistration; - this.injectionPointAnnotationsPredicate = injectionPointAnnotationsPredicate; - } - } - - @FunctionalInterface - interface Generator { - - void generate(GeneratorContext context); - - } - - private static boolean isCdiAndRawTypeMatches(InjectionPointInfo injectionPoint, DotName... rawTypeDotNames) { - if (injectionPoint.getKind() != InjectionPointKind.CDI) { - return false; - } - for (DotName rawTypeDotName : rawTypeDotNames) { - if (rawTypeDotName.equals(injectionPoint.getType().name())) { - return true; - } + private static void validateEventMetadata(InjectionTargetInfo injectionTarget, InjectionPointInfo injectionPoint, + Consumer errors) { + if (injectionTarget.kind() != TargetKind.OBSERVER) { + errors.accept(new DefinitionException("EventMetadata can be only injected into an observer method: " + + injectionPoint.getTargetInfo())); } - return false; } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java index 919dbed96ef62..0c4ce80847a04 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java @@ -246,7 +246,7 @@ public String getTargetInfo() { } else { method = "#" + method; } - return target.asMethod().declaringClass().name() + method + "()" + "." + param; + return target.asMethod().declaringClass().name() + method + "()" + ":" + param; default: return target.toString(); } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableFieldInjectionPointTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableFieldInjectionPointTest.java new file mode 100644 index 0000000000000..788b776f47dde --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableFieldInjectionPointTest.java @@ -0,0 +1,37 @@ +package io.quarkus.arc.test.injection.illegal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class TypeVariableFieldInjectionPointTest { + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(Head.class).shouldFail().build();; + + @Test + public void testError() { + Throwable failure = container.getFailure(); + assertNotNull(failure); + assertTrue(failure instanceof DefinitionException); + assertEquals( + "Type variable is not a legal injection point type: io.quarkus.arc.test.injection.illegal.TypeVariableFieldInjectionPointTest$Head#it", + failure.getMessage()); + } + + @Dependent + static class Head { + + @Inject + T it; + + } + +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableInitializerInjectionPointTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableInitializerInjectionPointTest.java new file mode 100644 index 0000000000000..42581bc18a589 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/illegal/TypeVariableInitializerInjectionPointTest.java @@ -0,0 +1,38 @@ +package io.quarkus.arc.test.injection.illegal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class TypeVariableInitializerInjectionPointTest { + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(Head.class).shouldFail().build();; + + @Test + public void testError() { + Throwable failure = container.getFailure(); + assertNotNull(failure); + assertTrue(failure instanceof DefinitionException); + assertEquals( + "Type variable is not a legal injection point type: io.quarkus.arc.test.injection.illegal.TypeVariableInitializerInjectionPointTest$Head#setIt():it", + failure.getMessage()); + } + + @Dependent + static class Head { + + @Inject + void setIt(T it) { + } + + } + +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/TypeVariableInstanceInjectionPointTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/TypeVariableInstanceInjectionPointTest.java new file mode 100644 index 0000000000000..1d961ca039e0c --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/TypeVariableInstanceInjectionPointTest.java @@ -0,0 +1,38 @@ +package io.quarkus.arc.test.instance.illegal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class TypeVariableInstanceInjectionPointTest { + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(Head.class).shouldFail().build();; + + @Test + public void testError() { + Throwable failure = container.getFailure(); + assertNotNull(failure); + assertTrue(failure instanceof DefinitionException); + assertEquals( + "Type variable is not a legal type argument for javax.enterprise.inject.Instance: io.quarkus.arc.test.instance.illegal.TypeVariableInstanceInjectionPointTest$Head#instance", + failure.getMessage()); + } + + @Dependent + static class Head { + + @Inject + Instance instance; + + } + +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/WildcardInstanceInjectionPointTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/WildcardInstanceInjectionPointTest.java new file mode 100644 index 0000000000000..530082ece139a --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/illegal/WildcardInstanceInjectionPointTest.java @@ -0,0 +1,38 @@ +package io.quarkus.arc.test.instance.illegal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class WildcardInstanceInjectionPointTest { + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(Head.class).shouldFail().build();; + + @Test + public void testError() { + Throwable failure = container.getFailure(); + assertNotNull(failure); + assertTrue(failure instanceof DefinitionException); + assertEquals( + "Wildcard is not a legal type argument for javax.enterprise.inject.Instance: io.quarkus.arc.test.instance.illegal.WildcardInstanceInjectionPointTest$Head#instance", + failure.getMessage()); + } + + @Dependent + static class Head { + + @Inject + Instance instance; + + } + +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/unused/RemoveUnusedBeansTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/unused/RemoveUnusedBeansTest.java index 3890ce4777fef..0cd9ed2888808 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/unused/RemoveUnusedBeansTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/unused/RemoveUnusedBeansTest.java @@ -29,7 +29,7 @@ public class RemoveUnusedBeansTest extends RemoveUnusedComponentsTest { @RegisterExtension public ArcTestContainer container = ArcTestContainer.builder() .beanClasses(HasObserver.class, Foo.class, FooAlternative.class, HasName.class, UnusedProducers.class, - InjectedViaInstance.class, InjectedViaInstanceWithWildcard.class, InjectedViaInstanceWithWildcard2.class, + InjectedViaInstance.class, InjectedViaInstanceWithWildcard.class, InjectedViaProvider.class, Excluded.class, UsedProducers.class, UnusedProducerButInjected.class, UsedViaInstanceWithUnusedProducer.class, UsesBeanViaInstance.class) @@ -43,7 +43,6 @@ public void testRemoval() { assertPresent(HasName.class); assertPresent(InjectedViaInstance.class); assertPresent(InjectedViaInstanceWithWildcard.class); - assertPresent(InjectedViaInstanceWithWildcard2.class); assertPresent(InjectedViaProvider.class); assertPresent(String.class); assertPresent(UsedProducers.class); @@ -104,9 +103,6 @@ static class FooAlternative extends Foo { @Inject Instance instance; - @Inject - Instance instanceWildcard; - @Inject Instance> instanceWildcard2; @@ -121,12 +117,7 @@ static class InjectedViaInstance { } @Singleton - static class InjectedViaInstanceWithWildcard { - - } - - @Singleton - static class InjectedViaInstanceWithWildcard2 implements Comparable { + static class InjectedViaInstanceWithWildcard implements Comparable { @Override public int compareTo(FooAlternative o) {