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

ArC: optimize Instance injection points #45353

Merged
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading