Skip to content

Commit

Permalink
ArC: optimize Instance injection points
Browse files Browse the repository at this point in the history
- only collect InjectionPoint metadata if any of the resolved beans is
dependent
  • Loading branch information
mkouba committed Jan 3, 2025
1 parent 33c6b36 commit 34d9dd9
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
import jakarta.enterprise.inject.spi.InjectionPoint;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;
import org.jboss.jandex.Type.Kind;

import io.quarkus.arc.InjectableBean;
import io.quarkus.arc.WithCaching;
import io.quarkus.arc.impl.BeanManagerProvider;
import io.quarkus.arc.impl.BeanMetadataProvider;
import io.quarkus.arc.impl.EventProvider;
Expand All @@ -35,6 +37,7 @@
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.ClassOutput;
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.gizmo.Gizmo;
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
Expand Down Expand Up @@ -189,30 +192,46 @@ private static boolean cdiAndRawTypeMatches(InjectionPointInfo injectionPoint, D

private static void generateInstanceBytecode(GeneratorContext ctx) {
ResultHandle qualifiers = BeanGenerator.collectInjectionPointQualifiers(
ctx.beanDeployment,
ctx.constructor, ctx.injectionPoint, ctx.annotationLiterals);
ctx.beanDeployment, ctx.constructor, ctx.injectionPoint, ctx.annotationLiterals);
ResultHandle parameterizedType = Types.getTypeHandle(ctx.constructor, ctx.injectionPoint.getType());
ResultHandle annotationsHandle = BeanGenerator.collectInjectionPointAnnotations(
ctx.beanDeployment,
ctx.constructor, ctx.injectionPoint, ctx.annotationLiterals, ctx.injectionPointAnnotationsPredicate);
ResultHandle javaMemberHandle = BeanGenerator.getJavaMemberHandle(ctx.constructor, ctx.injectionPoint,
ctx.reflectionRegistration);

// Note that we only collect the injection point metadata if needed, i.e. if any of the resolved beans is dependent,
// and requires InjectionPoint metadata
Set<BeanInfo> beans = ctx.beanDeployment.beanResolver.resolveBeans(ctx.injectionPoint.getRequiredType(),
ctx.injectionPoint.getRequiredQualifiers());
boolean collectMetadata = beans.stream()
.anyMatch(b -> BuiltinScope.DEPENDENT.isDeclaredBy(b) && b.requiresInjectionPointMetadata());

ResultHandle annotationsHandle;
ResultHandle javaMemberHandle;
ResultHandle beanHandle;
switch (ctx.targetInfo.kind()) {
case OBSERVER:
// For observers the first argument is always the declaring bean
beanHandle = ctx.constructor.invokeInterfaceMethod(
MethodDescriptors.SUPPLIER_GET, ctx.constructor.getMethodParam(0));
break;
case BEAN:
beanHandle = ctx.constructor.getThis();
break;
case INVOKER:
beanHandle = loadInvokerTargetBean(ctx.targetInfo.asInvoker(), ctx.constructor);
break;
default:
throw new IllegalStateException("Unsupported target info: " + ctx.targetInfo);
if (collectMetadata) {
annotationsHandle = BeanGenerator.collectInjectionPointAnnotations(
ctx.beanDeployment, ctx.constructor, ctx.injectionPoint, ctx.annotationLiterals,
ctx.injectionPointAnnotationsPredicate);
javaMemberHandle = BeanGenerator.getJavaMemberHandle(ctx.constructor, ctx.injectionPoint,
ctx.reflectionRegistration);
switch (ctx.targetInfo.kind()) {
case OBSERVER:
// For observers the first argument is always the declaring bean
beanHandle = ctx.constructor.invokeInterfaceMethod(
MethodDescriptors.SUPPLIER_GET, ctx.constructor.getMethodParam(0));
break;
case BEAN:
beanHandle = ctx.constructor.getThis();
break;
case INVOKER:
beanHandle = loadInvokerTargetBean(ctx.targetInfo.asInvoker(), ctx.constructor);
break;
default:
throw new IllegalStateException("Unsupported target info: " + ctx.targetInfo);
}
} else {
annotationsHandle = collectWithCaching(ctx.beanDeployment, ctx.constructor, ctx.injectionPoint);
javaMemberHandle = ctx.constructor.loadNull();
beanHandle = ctx.constructor.loadNull();
}

ResultHandle instanceProvider = ctx.constructor.newInstance(
MethodDescriptor.ofConstructor(InstanceProvider.class, java.lang.reflect.Type.class, Set.class,
InjectableBean.class, Set.class, Member.class, int.class, boolean.class),
Expand Down Expand Up @@ -566,4 +585,19 @@ private static void validateInterceptionProxy(ValidatorContext ctx) {
}
}

private static ResultHandle collectWithCaching(BeanDeployment beanDeployment, MethodCreator bytecode,
InjectionPointInfo injectionPoint) {
ResultHandle annotationsHandle;
AnnotationTarget annotationTarget = injectionPoint.isParam()
? injectionPoint.getAnnotationTarget().asMethodParameter().method()
: injectionPoint.getAnnotationTarget();
if (!injectionPoint.isSynthetic() && Annotations
.contains(beanDeployment.getAnnotations(annotationTarget), DotNames.WITH_CACHING)) {
annotationsHandle = Gizmo.setOperations(bytecode).of(bytecode
.readStaticField(FieldDescriptor.of(WithCaching.Literal.class, "INSTANCE", WithCaching.Literal.class)));
} else {
annotationsHandle = Gizmo.setOperations(bytecode).of();
}
return annotationsHandle;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public boolean is(ScopeInfo scope) {
return getInfo().equals(scope);
}

public boolean isDeclaredBy(BeanInfo bean) {
return is(bean.getScope());
}

public static boolean isIn(Iterable<AnnotationInstance> annotations) {
for (AnnotationInstance annotation : annotations) {
if (from(annotation.name()) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import io.quarkus.arc.NoClassInterceptors;
import io.quarkus.arc.Unremovable;
import io.quarkus.arc.VetoedProducer;
import io.quarkus.arc.WithCaching;
import io.quarkus.arc.impl.ComputingCache;
import io.quarkus.arc.impl.Identified;
import io.smallrye.mutiny.Multi;
Expand Down Expand Up @@ -147,6 +148,7 @@ public final class DotNames {
public static final DotName DEPRECATED = create(Deprecated.class);
public static final DotName INTERCEPTION_PROXY = create(InterceptionProxy.class);
public static final DotName BINDINGS_SOURCE = create(BindingsSource.class);
public static final DotName WITH_CACHING = create(WithCaching.class);

public static final DotName BOOLEAN = create(Boolean.class);
public static final DotName BYTE = create(Byte.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.util.AnnotationLiteral;

/**
* An injected {@link Instance} annotated with this annotation will cache the result of the {@link Instance#get()} operation.
Expand Down Expand Up @@ -93,4 +94,15 @@
@Retention(RUNTIME)
public @interface WithCaching {

/**
* Supports inline instantiation of the {@link WithCaching} annotation.
*/
public static final class Literal extends AnnotationLiteral<WithCaching> implements WithCaching {

private static final long serialVersionUID = 1L;

public static final Literal INSTANCE = new Literal();

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
import jakarta.enterprise.event.Event;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.spi.AnnotatedParameter;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.InjectionPoint;
import jakarta.enterprise.invoke.Invoker;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.inject.Singleton;
Expand All @@ -40,7 +38,6 @@ public class ArgumentLookupBuiltinBeansTest {
.withArgumentLookup(1)
.withArgumentLookup(2)
.withArgumentLookup(3)
.withArgumentLookup(4)
.build());
}))
.build();
Expand All @@ -52,7 +49,7 @@ public void test() throws Exception {
InstanceHandle<MyService> service = Arc.container().instance(MyService.class);

Invoker<MyService, String> invoker = helper.getInvoker("hello");
assertEquals("foobar0_MyDependency__1__MyService_hello_4", invoker.invoke(service.get(), new Object[5]));
assertEquals("foobar0_MyDependency__1", invoker.invoke(service.get(), new Object[4]));
assertEquals(List.of("foo", "bar", "baz"), MyService.observed);
assertEquals(2, MyService.listAll.size());
assertEquals(1, MyService.listAll.stream().filter(it -> it instanceof MyInterfaceImpl1).count());
Expand All @@ -71,7 +68,7 @@ static class MyService {
static final List<MyInterface> listAll = new ArrayList<>();

public String hello(Instance<MyDependency> instanceOfDependency, Event<List<String>> event, BeanManager beanManager,
@All List<MyInterface> list, Instance<Object> lookup) {
@All List<MyInterface> list) {
Class<?> beanClass = instanceOfDependency.getHandle().getBean().getBeanClass();
MyDependency dependency1 = instanceOfDependency.get();

Expand All @@ -87,15 +84,7 @@ public String hello(Instance<MyDependency> instanceOfDependency, Event<List<Stri

listAll.addAll(list);

InjectionPoint ip = lookup.select(InjectionPoint.class).get();
String ipBeanClass = ip.getBean().getBeanClass().getSimpleName();
String ipMemberName = ip.getMember().getName();
int ipPosition = ip.getAnnotated() instanceof AnnotatedParameter<?>
? ((AnnotatedParameter<?>) ip.getAnnotated()).getPosition()
: -1;

return "foobar" + dependency1.getId() + "_" + beanClass.getSimpleName() + "__" + id
+ "__" + ipBeanClass + "_" + ipMemberName + "_" + ipPosition;
return "foobar" + dependency1.getId() + "_" + beanClass.getSimpleName() + "__" + id;
}

public void observe(@Observes List<String> event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
import jakarta.enterprise.event.Event;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.spi.AnnotatedParameter;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.InjectionPoint;
import jakarta.enterprise.invoke.Invoker;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.inject.Singleton;
Expand Down Expand Up @@ -41,7 +39,6 @@ public class MonstrousLookupTest {
.withArgumentLookup(2)
.withArgumentLookup(3)
.withArgumentLookup(4)
.withArgumentLookup(5)
.build());
}))
.build();
Expand All @@ -51,7 +48,7 @@ public void test() throws Exception {
InvokerHelper helper = Arc.container().instance(InvokerHelper.class).get();

Invoker<MyService, String> invoker = helper.getInvoker("hello");
assertEquals("foobar0_1_MyDependency__2__MyService_hello_5", invoker.invoke(null, new Object[6]));
assertEquals("foobar0_1_MyDependency__2", invoker.invoke(null, new Object[5]));
assertEquals(List.of("foo", "bar", "baz"), MyService.observed);
assertEquals(2, MyService.listAll.size());
assertEquals(1, MyService.listAll.stream().filter(it -> it instanceof MyInterfaceImpl1).count());
Expand All @@ -71,8 +68,7 @@ static class MyService {
static final List<MyInterface> listAll = new ArrayList<>();

public String hello(MyDependency dependency, Instance<MyDependency> instanceOfDependency,
Event<List<String>> event, BeanManager beanManager, @All List<MyInterface> list,
Instance<Object> lookup) {
Event<List<String>> event, BeanManager beanManager, @All List<MyInterface> list) {
Class<?> dependency1Class = instanceOfDependency.getHandle().getBean().getBeanClass();
MyDependency dependency1 = instanceOfDependency.get();

Expand All @@ -88,15 +84,8 @@ public String hello(MyDependency dependency, Instance<MyDependency> instanceOfDe

listAll.addAll(list);

InjectionPoint ip = lookup.select(InjectionPoint.class).get();
String ipBeanClass = ip.getBean().getBeanClass().getSimpleName();
String ipMemberName = ip.getMember().getName();
int ipPosition = ip.getAnnotated() instanceof AnnotatedParameter<?>
? ((AnnotatedParameter<?>) ip.getAnnotated()).getPosition()
: -1;

return "foobar" + dependency.getId() + "_" + dependency1.getId() + "_" + dependency1Class.getSimpleName()
+ "__" + dependency2Id + "__" + ipBeanClass + "_" + ipMemberName + "_" + ipPosition;
+ "__" + dependency2Id;
}

public void observe(@Observes List<String> event) {
Expand Down

0 comments on commit 34d9dd9

Please sign in to comment.