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 fixes for spec compatibility, round 2 #30666

Merged
merged 5 commits into from
Feb 2, 2023
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 @@ -846,6 +846,7 @@ protected void implementDestroy(BeanInfo bean, ClassCreator beanCreator, Provide
// Invoke the disposer method
// declaringProvider.get(new CreationalContextImpl<>()).dispose()
MethodInfo disposerMethod = bean.getDisposer().getDisposerMethod();
boolean isStatic = Modifier.isStatic(disposerMethod.flags());

ResultHandle declaringProviderSupplierHandle = destroy.readInstanceField(
FieldDescriptor.of(beanCreator.getClassName(), FIELD_NAME_DECLARING_PROVIDER_SUPPLIER,
Expand All @@ -855,15 +856,21 @@ protected void implementDestroy(BeanInfo bean, ClassCreator beanCreator, Provide
MethodDescriptors.SUPPLIER_GET, declaringProviderSupplierHandle);
ResultHandle ctxHandle = destroy.newInstance(
MethodDescriptor.ofConstructor(CreationalContextImpl.class, Contextual.class), destroy.loadNull());
ResultHandle declaringProviderInstanceHandle = destroy.invokeInterfaceMethod(
MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, declaringProviderHandle,
ctxHandle);

if (bean.getDeclaringBean().getScope().isNormal()) {
// We need to unwrap the client proxy
ResultHandle declaringProviderInstanceHandle;
if (isStatic) {
// for static disposers, we don't need to resolve this handle
// the `null` will only be used for reflective invocation in case the disposer is private, which is OK
declaringProviderInstanceHandle = destroy.loadNull();
} else {
declaringProviderInstanceHandle = destroy.invokeInterfaceMethod(
MethodDescriptors.CLIENT_PROXY_GET_CONTEXTUAL_INSTANCE,
declaringProviderInstanceHandle);
MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, declaringProviderHandle,
ctxHandle);
if (bean.getDeclaringBean().getScope().isNormal()) {
// We need to unwrap the client proxy
declaringProviderInstanceHandle = destroy.invokeInterfaceMethod(
MethodDescriptors.CLIENT_PROXY_GET_CONTEXTUAL_INSTANCE,
declaringProviderInstanceHandle);
}
}

ResultHandle[] referenceHandles = new ResultHandle[disposerMethod.parametersCount()];
Expand Down Expand Up @@ -904,6 +911,8 @@ protected void implementDestroy(BeanInfo bean, ClassCreator beanCreator, Provide
destroy.invokeStaticMethod(MethodDescriptors.REFLECTIONS_INVOKE_METHOD,
destroy.loadClass(disposerMethod.declaringClass().name().toString()),
destroy.load(disposerMethod.name()), paramTypesArray, declaringProviderInstanceHandle, argsArray);
} else if (isStatic) {
destroy.invokeStaticMethod(MethodDescriptor.of(disposerMethod), referenceHandles);
} else {
destroy.invokeVirtualMethod(MethodDescriptor.of(disposerMethod), declaringProviderInstanceHandle,
referenceHandles);
Expand All @@ -912,8 +921,8 @@ protected void implementDestroy(BeanInfo bean, ClassCreator beanCreator, Provide
// Destroy @Dependent instances injected into method parameters of a disposer method
destroy.invokeInterfaceMethod(MethodDescriptors.CREATIONAL_CTX_RELEASE, ctxHandle);

// If the declaring bean is @Dependent we must destroy the instance afterwards
if (BuiltinScope.DEPENDENT.is(bean.getDisposer().getDeclaringBean().getScope())) {
// If the declaring bean is @Dependent and the disposer is not static, we must destroy the instance afterwards
if (BuiltinScope.DEPENDENT.is(bean.getDisposer().getDeclaringBean().getScope()) && !isStatic) {
destroy.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_BEAN_DESTROY, declaringProviderHandle,
declaringProviderInstanceHandle, ctxHandle);
}
Expand All @@ -927,7 +936,7 @@ protected void implementDestroy(BeanInfo bean, ClassCreator beanCreator, Provide

// Bridge method needed
MethodCreator bridgeDestroy = beanCreator.getMethodCreator("destroy", void.class, Object.class, CreationalContext.class)
.setModifiers(ACC_PUBLIC);
.setModifiers(ACC_PUBLIC | ACC_BRIDGE);
bridgeDestroy.returnValue(bridgeDestroy.invokeVirtualMethod(destroy.getMethodDescriptor(), bridgeDestroy.getThis(),
bridgeDestroy.getMethodParam(0),
bridgeDestroy.getMethodParam(1)));
Expand All @@ -940,44 +949,61 @@ protected void implementCreate(ClassOutput classOutput, ClassCreator beanCreator
Map<DecoratorInfo, String> decoratorToProviderSupplierField,
String targetPackage, boolean isApplicationClass) {

MethodCreator create = beanCreator.getMethodCreator("create", providerType.descriptorName(), CreationalContext.class)
.setModifiers(ACC_PUBLIC);
MethodCreator doCreate = beanCreator
.getMethodCreator("doCreate", providerType.descriptorName(), CreationalContext.class)
.setModifiers(ACC_PRIVATE);

if (bean.isClassBean()) {
implementCreateForClassBean(classOutput, beanCreator, bean, providerType, baseName,
injectionPointToProviderSupplierField, interceptorToProviderSupplierField, decoratorToProviderSupplierField,
reflectionRegistration,
targetPackage, isApplicationClass, create);
targetPackage, isApplicationClass, doCreate);
} else if (bean.isProducerMethod()) {
implementCreateForProducerMethod(classOutput, beanCreator, bean, providerType, baseName,
injectionPointToProviderSupplierField, reflectionRegistration,
targetPackage, isApplicationClass, create);
targetPackage, isApplicationClass, doCreate);
} else if (bean.isProducerField()) {
implementCreateForProducerField(classOutput, beanCreator, bean, providerType, baseName,
injectionPointToProviderSupplierField, reflectionRegistration,
targetPackage, isApplicationClass, create);
targetPackage, isApplicationClass, doCreate);
} else if (bean.isSynthetic()) {
if (bean.getScope().isNormal()) {
// Normal scoped synthetic beans should never return null
MethodCreator createSynthetic = beanCreator
.getMethodCreator("createSynthetic", providerType.descriptorName(), CreationalContext.class)
.setModifiers(ACC_PRIVATE);
bean.getCreatorConsumer().accept(createSynthetic);
ResultHandle ret = create.invokeVirtualMethod(createSynthetic.getMethodDescriptor(), create.getThis(),
create.getMethodParam(0));
BytecodeCreator nullBeanInstance = create.ifNull(ret).trueBranch();
ResultHandle ret = doCreate.invokeVirtualMethod(createSynthetic.getMethodDescriptor(), doCreate.getThis(),
doCreate.getMethodParam(0));
BytecodeCreator nullBeanInstance = doCreate.ifNull(ret).trueBranch();
StringBuilderGenerator message = Gizmo.newStringBuilder(nullBeanInstance);
message.append("Null contextual instance was produced by a normal scoped synthetic bean: ");
message.append(Gizmo.toString(nullBeanInstance, nullBeanInstance.getThis()));
ResultHandle e = nullBeanInstance.newInstance(
MethodDescriptor.ofConstructor(CreationException.class, String.class), message.callToString());
nullBeanInstance.throwException(e);
create.returnValue(ret);
doCreate.returnValue(ret);
} else {
bean.getCreatorConsumer().accept(create);
bean.getCreatorConsumer().accept(doCreate);
}
}

MethodCreator create = beanCreator.getMethodCreator("create", providerType.descriptorName(), CreationalContext.class)
.setModifiers(ACC_PUBLIC);
TryBlock tryBlock = create.tryBlock();
tryBlock.returnValue(
tryBlock.invokeSpecialMethod(doCreate.getMethodDescriptor(), tryBlock.getThis(), tryBlock.getMethodParam(0)));
// `Reflections.newInstance()` throws `CreationException` on its own,
// but that's handled like all other `RuntimeException`s
// also ignore custom Throwables, they are virtually never used in practice
CatchBlockCreator catchBlock = tryBlock.addCatch(Exception.class);
catchBlock.ifFalse(catchBlock.instanceOf(catchBlock.getCaughtException(), RuntimeException.class))
.falseBranch().throwException(catchBlock.getCaughtException());
ResultHandle creationException = catchBlock.newInstance(
MethodDescriptor.ofConstructor(CreationException.class, Throwable.class),
catchBlock.getCaughtException());
catchBlock.throwException(creationException);

// Bridge method needed
MethodCreator bridgeCreate = beanCreator.getMethodCreator("create", Object.class, CreationalContext.class)
.setModifiers(ACC_PUBLIC | ACC_BRIDGE);
Expand Down Expand Up @@ -1224,6 +1250,8 @@ void implementCreateForProducerMethod(ClassOutput classOutput, ClassCreator bean
AssignableResultHandle instanceHandle;

MethodInfo producerMethod = bean.getTarget().get().asMethod();
boolean isStatic = Modifier.isStatic(producerMethod.flags());

instanceHandle = create.createVariable(DescriptorUtils.extToInt(providerType.className()));
// instance = declaringProviderSupplier.get().get(new CreationalContextImpl<>()).produce()
ResultHandle ctxHandle = create.newInstance(
Expand All @@ -1235,8 +1263,9 @@ void implementCreateForProducerMethod(ClassOutput classOutput, ClassCreator bean
create.getThis());
ResultHandle declaringProviderHandle = create.invokeInterfaceMethod(
MethodDescriptors.SUPPLIER_GET, declaringProviderSupplierHandle);
if (Modifier.isStatic(producerMethod.flags())) {
// for static producers, we don't need to resolve this this handle
if (isStatic) {
// for static producers, we don't need to resolve this handle
// the `null` will only be used for reflective invocation in case the producer is private, which is OK
declaringProviderInstanceHandle = create.loadNull();
} else {
declaringProviderInstanceHandle = create.invokeInterfaceMethod(
Expand Down Expand Up @@ -1292,7 +1321,7 @@ void implementCreateForProducerMethod(ClassOutput classOutput, ClassCreator bean
argsArray));
} else {
ResultHandle invokeMethodHandle;
if (Modifier.isStatic(producerMethod.flags())) {
if (isStatic) {
invokeMethodHandle = create.invokeStaticMethod(MethodDescriptor.of(producerMethod),
referenceHandles);
} else {
Expand All @@ -1309,8 +1338,8 @@ void implementCreateForProducerMethod(ClassOutput classOutput, ClassCreator bean
bean.getTarget().get().asMethod().name() + "()");
}

// If the declaring bean is @Dependent we must destroy the instance afterwards
if (BuiltinScope.DEPENDENT.is(bean.getDeclaringBean().getScope())) {
// If the declaring bean is @Dependent and the producer is not static, we must destroy the instance afterwards
if (BuiltinScope.DEPENDENT.is(bean.getDeclaringBean().getScope()) && !isStatic) {
create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_BEAN_DESTROY, declaringProviderHandle,
declaringProviderInstanceHandle, ctxHandle);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ static BeanInfo createProducerMethod(Set<Type> beanTypes, MethodInfo producerMet

if (isAlternative) {
if (priority == null) {
priority = declaringBean.getAlternativePriority();
priority = declaringBean.getPriority();
}
priority = initAlternativePriority(producerMethod, priority, stereotypes, beanDeployment);
if (priority == null) {
Expand Down Expand Up @@ -287,7 +287,7 @@ static BeanInfo createProducerField(FieldInfo producerField, BeanInfo declaringB

if (isAlternative) {
if (priority == null) {
priority = declaringBean.getAlternativePriority();
priority = declaringBean.getPriority();
}
priority = initAlternativePriority(producerField, priority, stereotypes, beanDeployment);
// after all attempts, priority is still null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ArrayType;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
Expand Down Expand Up @@ -186,13 +187,23 @@ private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreat
getParameterizedType(variable, creator, tccl, type.asParameterizedType(), cache, typeVariables);

} else if (Kind.ARRAY.equals(type.kind())) {
Type componentType = type.asArrayType().component();
// E.g. String[] -> new GenericArrayTypeImpl(String.class)
AssignableResultHandle componentTypeHandle = creator.createVariable(Object.class);
getTypeHandle(componentTypeHandle, creator, componentType, tccl, cache, typeVariables);
ResultHandle arrayHandle = creator.newInstance(
MethodDescriptor.ofConstructor(GenericArrayTypeImpl.class, java.lang.reflect.Type.class),
componentTypeHandle);
ArrayType array = type.asArrayType();
Type elementType = getArrayElementType(array);

ResultHandle arrayHandle;
if (elementType.kind() == Kind.PRIMITIVE || elementType.kind() == Kind.CLASS) {
// can produce a java.lang.Class representation of the array type
// E.g. String[] -> String[].class
arrayHandle = doLoadClass(creator, array.name().toString(), tccl);
} else {
// E.g. List<String>[] -> new GenericArrayTypeImpl(new ParameterizedTypeImpl(List.class, String.class))
Type componentType = type.asArrayType().component();
AssignableResultHandle componentTypeHandle = creator.createVariable(Object.class);
getTypeHandle(componentTypeHandle, creator, componentType, tccl, cache, typeVariables);
arrayHandle = creator.newInstance(
MethodDescriptor.ofConstructor(GenericArrayTypeImpl.class, java.lang.reflect.Type.class),
componentTypeHandle);
}
if (cache != null) {
cache.put(type, arrayHandle, creator);
}
Expand Down Expand Up @@ -337,13 +348,24 @@ static Type getProviderType(ClassInfo classInfo) {
}
}

static Type getArrayElementType(ArrayType array) {
Type elementType = array.component();
while (elementType.kind() == Kind.ARRAY) {
elementType = elementType.asArrayType().component();
}
return elementType;
}

static Set<Type> getProducerMethodTypeClosure(MethodInfo producerMethod, BeanDeployment beanDeployment) {
Set<Type> types;
Set<Type> unrestrictedBeanTypes = new HashSet<>();
Type returnType = producerMethod.returnType();
if (returnType.kind() == Kind.TYPE_VARIABLE) {
throw new DefinitionException("A type variable is not a legal bean type: " + producerMethod);
}
if (returnType.kind() == Kind.ARRAY) {
checkArrayType(returnType.asArrayType(), producerMethod);
}
if (returnType.kind() == Kind.PRIMITIVE || returnType.kind() == Kind.ARRAY) {
types = new HashSet<>();
types.add(returnType);
Expand Down Expand Up @@ -379,6 +401,9 @@ static Set<Type> getProducerFieldTypeClosure(FieldInfo producerField, BeanDeploy
if (fieldType.kind() == Kind.TYPE_VARIABLE) {
throw new DefinitionException("A type variable is not a legal bean type: " + producerField);
}
if (fieldType.kind() == Kind.ARRAY) {
checkArrayType(fieldType.asArrayType(), producerField);
}
if (fieldType.kind() == Kind.PRIMITIVE || fieldType.kind() == Kind.ARRAY) {
types = new HashSet<>();
types.add(fieldType);
Expand Down Expand Up @@ -468,6 +493,18 @@ static List<Type> getResolvedParameters(ClassInfo classInfo, Map<String, Type> r
}
}

/**
* Throws {@code DefinitionException} if given {@code producerFieldOrMethod},
* whose type is given {@code arrayType}, is invalid due to the rules for arrays.
*/
static void checkArrayType(ArrayType arrayType, AnnotationTarget producerFieldOrMethod) {
Type elementType = getArrayElementType(arrayType);
if (elementType.kind() == Kind.TYPE_VARIABLE) {
throw new DefinitionException("A type variable array is not a legal bean type: " + producerFieldOrMethod);
}
containsWildcard(elementType, producerFieldOrMethod, true);
}

/**
* Detects wildcard for given type.
* In case this is related to a producer field or method, it either logs or throws a {@link DefinitionException}
Expand Down
Loading