mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-10-01 03:39:05 +00:00
Refine Kotlin value class contribution
Only unbox value class results when the caller expects their unboxed representation (non-primitive underlying type, non-nullable underlying type for nullable return types, and no overridden function with a different return type), and add related tests. Cache the unbox method resolution per method and make it accessible to support non-public value classes. Closes gh-37191
This commit is contained in:
@@ -8,6 +8,7 @@ dependencies {
|
|||||||
compileOnly("com.google.code.findbugs:jsr305") // for the AOP Alliance fork
|
compileOnly("com.google.code.findbugs:jsr305") // for the AOP Alliance fork
|
||||||
optional("org.apache.commons:commons-pool2")
|
optional("org.apache.commons:commons-pool2")
|
||||||
optional("org.aspectj:aspectjweaver")
|
optional("org.aspectj:aspectjweaver")
|
||||||
|
optional("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
optional("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
optional("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
||||||
testFixturesImplementation(testFixtures(project(":spring-beans")))
|
testFixturesImplementation(testFixtures(project(":spring-beans")))
|
||||||
testFixturesImplementation(testFixtures(project(":spring-core")))
|
testFixturesImplementation(testFixtures(project(":spring-core")))
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package org.springframework.aop.framework;
|
package org.springframework.aop.framework;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
@@ -52,7 +51,6 @@ import org.springframework.cglib.proxy.MethodProxy;
|
|||||||
import org.springframework.cglib.proxy.NoOp;
|
import org.springframework.cglib.proxy.NoOp;
|
||||||
import org.springframework.cglib.transform.impl.UndeclaredThrowableStrategy;
|
import org.springframework.cglib.transform.impl.UndeclaredThrowableStrategy;
|
||||||
import org.springframework.core.KotlinDetector;
|
import org.springframework.core.KotlinDetector;
|
||||||
import org.springframework.core.MethodParameter;
|
|
||||||
import org.springframework.core.SmartClassLoader;
|
import org.springframework.core.SmartClassLoader;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
@@ -97,8 +95,6 @@ class CglibAopProxy implements AopProxy, Serializable {
|
|||||||
private static final int INVOKE_HASHCODE = 6;
|
private static final int INVOKE_HASHCODE = 6;
|
||||||
|
|
||||||
|
|
||||||
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
|
|
||||||
|
|
||||||
private static final boolean COROUTINES_REACTOR_PRESENT = ClassUtils.isPresent(
|
private static final boolean COROUTINES_REACTOR_PRESENT = ClassUtils.isPresent(
|
||||||
"kotlinx.coroutines.reactor.MonoKt", CglibAopProxy.class.getClassLoader());
|
"kotlinx.coroutines.reactor.MonoKt", CglibAopProxy.class.getClassLoader());
|
||||||
|
|
||||||
@@ -422,8 +418,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
|||||||
* Also takes care of the conversion from {@code Mono} to Kotlin Coroutines if needed.
|
* Also takes care of the conversion from {@code Mono} to Kotlin Coroutines if needed.
|
||||||
*/
|
*/
|
||||||
private static @Nullable Object processReturnType(
|
private static @Nullable Object processReturnType(
|
||||||
Object proxy, @Nullable Object target, Method method, Object[] arguments, @Nullable Object returnValue) throws
|
Object proxy, @Nullable Object target, Method method, Object[] arguments, @Nullable Object returnValue) {
|
||||||
NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
|
||||||
|
|
||||||
// Massage return value if necessary
|
// Massage return value if necessary
|
||||||
if (returnValue != null && returnValue == target &&
|
if (returnValue != null && returnValue == target &&
|
||||||
@@ -438,14 +433,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
|||||||
"Null return value from advice does not match primitive return type for: " + method);
|
"Null return value from advice does not match primitive return type for: " + method);
|
||||||
}
|
}
|
||||||
if (COROUTINES_REACTOR_PRESENT && KotlinDetector.isSuspendingFunction(method)) {
|
if (COROUTINES_REACTOR_PRESENT && KotlinDetector.isSuspendingFunction(method)) {
|
||||||
Class<?> returnParameterType = new MethodParameter(method, -1).getParameterType();
|
return CoroutinesUtils.adaptReturnValue(method, returnValue, arguments[arguments.length - 1]);
|
||||||
if (COROUTINES_FLOW_CLASS_NAME.equals(returnParameterType.getName())) {
|
|
||||||
return CoroutinesUtils.asFlow(returnValue);
|
|
||||||
}
|
|
||||||
Object awaitResult = CoroutinesUtils.awaitSingleOrNull(returnValue, arguments[arguments.length - 1]);
|
|
||||||
return KotlinDetector.isInlineClass(returnParameterType) &&
|
|
||||||
awaitResult != null && KotlinDetector.isInlineClass(awaitResult.getClass()) ?
|
|
||||||
awaitResult.getClass().getDeclaredMethod("unbox-impl").invoke(awaitResult) : awaitResult;
|
|
||||||
}
|
}
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,32 @@
|
|||||||
|
|
||||||
package org.springframework.aop.framework;
|
package org.springframework.aop.framework;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import kotlin.coroutines.Continuation;
|
import kotlin.coroutines.Continuation;
|
||||||
|
import kotlin.jvm.JvmClassMappingKt;
|
||||||
|
import kotlin.reflect.KClass;
|
||||||
|
import kotlin.reflect.KFunction;
|
||||||
|
import kotlin.reflect.KParameter;
|
||||||
|
import kotlin.reflect.KType;
|
||||||
|
import kotlin.reflect.KTypeParameter;
|
||||||
|
import kotlin.reflect.full.KClasses;
|
||||||
|
import kotlin.reflect.jvm.KTypesJvm;
|
||||||
|
import kotlin.reflect.jvm.ReflectJvmMapping;
|
||||||
import kotlinx.coroutines.reactive.ReactiveFlowKt;
|
import kotlinx.coroutines.reactive.ReactiveFlowKt;
|
||||||
import kotlinx.coroutines.reactor.MonoKt;
|
import kotlinx.coroutines.reactor.MonoKt;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import org.springframework.core.KotlinDetector;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package-visible class designed to avoid a hard dependency on Kotlin and Coroutines dependency at runtime.
|
* Package-visible class designed to avoid a hard dependency on Kotlin and Coroutines dependency at runtime.
|
||||||
*
|
*
|
||||||
@@ -31,6 +50,21 @@ import reactor.core.publisher.Mono;
|
|||||||
*/
|
*/
|
||||||
abstract class CoroutinesUtils {
|
abstract class CoroutinesUtils {
|
||||||
|
|
||||||
|
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
|
||||||
|
|
||||||
|
private static final Method NO_UNBOX_METHOD;
|
||||||
|
|
||||||
|
private static final Map<Method, Method> unboxMethodCache = new ConcurrentReferenceHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
NO_UNBOX_METHOD = CoroutinesUtils.class.getDeclaredMethod("noUnboxMethod");
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException ex) {
|
||||||
|
throw new IllegalStateException("Expected method not found: " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Object asFlow(@Nullable Object publisher) {
|
static Object asFlow(@Nullable Object publisher) {
|
||||||
if (publisher instanceof Publisher<?> rsPublisher) {
|
if (publisher instanceof Publisher<?> rsPublisher) {
|
||||||
return ReactiveFlowKt.asFlow(rsPublisher);
|
return ReactiveFlowKt.asFlow(rsPublisher);
|
||||||
@@ -46,4 +80,109 @@ abstract class CoroutinesUtils {
|
|||||||
(Continuation<Object>) continuation);
|
(Continuation<Object>) continuation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapt the return value of a proxied suspending function: convert it to a
|
||||||
|
* {@code Flow} or await its completion, unboxing Kotlin value classes when the
|
||||||
|
* caller expects their unboxed representation.
|
||||||
|
* @param method the suspending function
|
||||||
|
* @param returnValue the return value, typically a {@code Publisher}
|
||||||
|
* @param continuation the {@code Continuation} argument of the suspending function
|
||||||
|
* @return the adapted return value
|
||||||
|
*/
|
||||||
|
static @Nullable Object adaptReturnValue(Method method, @Nullable Object returnValue, Object continuation) {
|
||||||
|
MethodParameter returnParameter = new MethodParameter(method, -1);
|
||||||
|
Class<?> returnParameterType = returnParameter.getParameterType();
|
||||||
|
if (COROUTINES_FLOW_CLASS_NAME.equals(returnParameterType.getName())) {
|
||||||
|
return asFlow(returnValue);
|
||||||
|
}
|
||||||
|
Object result = awaitSingleOrNull(returnValue, continuation);
|
||||||
|
if (KotlinDetector.isInlineClass(returnParameterType) && returnParameterType.isInstance(result)) {
|
||||||
|
Method unboxMethod = unboxMethodCache.computeIfAbsent(method, key ->
|
||||||
|
findUnboxMethod(key, returnParameterType, returnParameter.isOptional()));
|
||||||
|
if (unboxMethod != NO_UNBOX_METHOD) {
|
||||||
|
return ReflectionUtils.invokeMethod(unboxMethod, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method findUnboxMethod(Method method, Class<?> valueClass, boolean nullableReturnType) {
|
||||||
|
Method unboxMethod = ReflectionUtils.findMethod(valueClass, "unbox-impl");
|
||||||
|
if (unboxMethod == null || unboxMethod.getReturnType().isPrimitive() ||
|
||||||
|
(nullableReturnType && isUnderlyingTypeNullable(valueClass)) ||
|
||||||
|
overridesFunctionWithDifferentReturnType(method, valueClass)) {
|
||||||
|
return NO_UNBOX_METHOD;
|
||||||
|
}
|
||||||
|
ReflectionUtils.makeAccessible(unboxMethod);
|
||||||
|
return unboxMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean overridesFunctionWithDifferentReturnType(Method method, Class<?> valueClass) {
|
||||||
|
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
|
||||||
|
if (function == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
KClass<?> valueKClass = JvmClassMappingKt.getKotlinClass(valueClass);
|
||||||
|
for (KType superType : KClasses.getAllSupertypes(JvmClassMappingKt.getKotlinClass(method.getDeclaringClass()))) {
|
||||||
|
if (superType.getClassifier() instanceof KClass<?> superClass) {
|
||||||
|
for (KFunction<?> candidate : KClasses.getDeclaredMemberFunctions(superClass)) {
|
||||||
|
if (candidate.getName().equals(function.getName()) && candidate.isSuspend() &&
|
||||||
|
hasSameParameterTypes(function, candidate, superType) &&
|
||||||
|
candidate.getReturnType().getClassifier() != valueKClass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasSameParameterTypes(KFunction<?> function, KFunction<?> candidate, KType superType) {
|
||||||
|
List<KParameter> parameters = function.getParameters();
|
||||||
|
List<KParameter> candidateParameters = candidate.getParameters();
|
||||||
|
if (parameters.size() != candidateParameters.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < parameters.size(); i++) {
|
||||||
|
KType candidateType = candidateParameters.get(i).getType();
|
||||||
|
if (candidateType.getClassifier() instanceof KTypeParameter typeParameter &&
|
||||||
|
superType.getClassifier() instanceof KClass<?> superClass) {
|
||||||
|
int index = superClass.getTypeParameters().indexOf(typeParameter);
|
||||||
|
KType argumentType = (index != -1 ? superType.getArguments().get(index).getType() : null);
|
||||||
|
if (argumentType != null) {
|
||||||
|
candidateType = argumentType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!KTypesJvm.getJvmErasure(candidateType).equals(KTypesJvm.getJvmErasure(parameters.get(i).getType()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isUnderlyingTypeNullable(Class<?> valueClass) {
|
||||||
|
KFunction<?> constructor = Objects.requireNonNull(
|
||||||
|
KClasses.getPrimaryConstructor(JvmClassMappingKt.getKotlinClass(valueClass)));
|
||||||
|
return isNullable(constructor.getParameters().get(0).getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNullable(KType type) {
|
||||||
|
if (type.isMarkedNullable()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (type.getClassifier() instanceof KTypeParameter typeParameter) {
|
||||||
|
return typeParameter.getUpperBounds().stream().anyMatch(CoroutinesUtils::isNullable);
|
||||||
|
}
|
||||||
|
return (type.getClassifier() instanceof KClass<?> kClass &&
|
||||||
|
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass)) &&
|
||||||
|
isUnderlyingTypeNullable(JvmClassMappingKt.getJavaClass(kClass)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the {@link #NO_UNBOX_METHOD} constant.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static void noUnboxMethod() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import org.springframework.aop.TargetSource;
|
|||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.core.DecoratingProxy;
|
import org.springframework.core.DecoratingProxy;
|
||||||
import org.springframework.core.KotlinDetector;
|
import org.springframework.core.KotlinDetector;
|
||||||
import org.springframework.core.MethodParameter;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
@@ -73,8 +72,6 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
|||||||
private static final long serialVersionUID = 5531744639992436476L;
|
private static final long serialVersionUID = 5531744639992436476L;
|
||||||
|
|
||||||
|
|
||||||
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
|
|
||||||
|
|
||||||
private static final boolean COROUTINES_REACTOR_PRESENT = ClassUtils.isPresent(
|
private static final boolean COROUTINES_REACTOR_PRESENT = ClassUtils.isPresent(
|
||||||
"kotlinx.coroutines.reactor.MonoKt", JdkDynamicAopProxy.class.getClassLoader());
|
"kotlinx.coroutines.reactor.MonoKt", JdkDynamicAopProxy.class.getClassLoader());
|
||||||
|
|
||||||
@@ -237,14 +234,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
|||||||
"Null return value from advice does not match primitive return type for: " + method);
|
"Null return value from advice does not match primitive return type for: " + method);
|
||||||
}
|
}
|
||||||
if (COROUTINES_REACTOR_PRESENT && KotlinDetector.isSuspendingFunction(method)) {
|
if (COROUTINES_REACTOR_PRESENT && KotlinDetector.isSuspendingFunction(method)) {
|
||||||
Class<?> returnParameterType = new MethodParameter(method, -1).getParameterType();
|
return CoroutinesUtils.adaptReturnValue(method, retVal, args[args.length - 1]);
|
||||||
if (COROUTINES_FLOW_CLASS_NAME.equals(returnParameterType.getName())) {
|
|
||||||
return CoroutinesUtils.asFlow(retVal);
|
|
||||||
}
|
|
||||||
Object awaitResult = CoroutinesUtils.awaitSingleOrNull(retVal, args[args.length - 1]);
|
|
||||||
return KotlinDetector.isInlineClass(returnParameterType) &&
|
|
||||||
awaitResult != null && KotlinDetector.isInlineClass(awaitResult.getClass()) ?
|
|
||||||
awaitResult.getClass().getDeclaredMethod("unbox-impl").invoke(awaitResult) : awaitResult;
|
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|||||||
+302
-6
@@ -149,6 +149,172 @@ class CglibAopProxyKotlinTests {
|
|||||||
assertThat(proxy.returnAny()).isEqualTo(ValueClass("bar"))
|
assertThat(proxy.returnAny()).isEqualTo(ValueClass("bar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassPrimitiveValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassPrimitiveValue(1)
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnValueClassPrimitiveValue()).isEqualTo(ValueClassPrimitiveValue(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassPrimitiveValueProceed() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
it.proceed()
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnValueClassPrimitiveValue()).isEqualTo(ValueClassPrimitiveValue(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassPrimitiveValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassPrimitiveValue(1)
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassPrimitiveValue()).isEqualTo(ValueClassPrimitiveValue(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassPrimitiveValueNull() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
null
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassPrimitiveValue()).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassNullableValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassNullableValue("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassNullableValue()).isEqualTo(ValueClassNullableValue("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassNullableValueNull() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassNullableValue(null)
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassNullableValue()).isEqualTo(ValueClassNullableValue(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableGenericValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
GenericValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableGenericValueClass()).isEqualTo(GenericValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNestedValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NestedValueClass(ValueClass("bar"))
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNestedValueClass()).isEqualTo(NestedValueClass(ValueClass("bar")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableNestedValueClassPrimitiveValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NestedValueClassPrimitiveValue(ValueClassPrimitiveValue(1))
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableNestedValueClassPrimitiveValue())
|
||||||
|
.isEqualTo(NestedValueClassPrimitiveValue(ValueClassPrimitiveValue(1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableNestedValueClassNullableValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NestedValueClassNullableValue(ValueClassNullableValue("bar"))
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableNestedValueClassNullableValue())
|
||||||
|
.isEqualTo(NestedValueClassNullableValue(ValueClassNullableValue("bar")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableNonNullGenericValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NonNullGenericValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableNonNullGenericValueClass()).isEqualTo(NonNullGenericValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableResult() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBean())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
Result.success("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableResult()?.getOrNull()).isEqualTo("bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassOverridingAny() {
|
||||||
|
val proxyFactory = ProxyFactory(ValueClassOverridingAnyBean())
|
||||||
|
proxyFactory.isProxyTargetClass = true
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as ValueClassOverridingAnyBean
|
||||||
|
assertThat(proxy.returnValue()).isEqualTo(ValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassOverridingGeneric() {
|
||||||
|
val proxyFactory = ProxyFactory(ValueClassOverridingGenericBean())
|
||||||
|
proxyFactory.isProxyTargetClass = true
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as ValueClassOverridingGenericBean
|
||||||
|
assertThat(proxy.returnValue()).isEqualTo(ValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassOverridingGenericParameter() {
|
||||||
|
val proxyFactory = ProxyFactory(ValueClassOverridingGenericParameterBean())
|
||||||
|
proxyFactory.isProxyTargetClass = true
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as ValueClassOverridingGenericParameterBean
|
||||||
|
assertThat(proxy.returnValue("foo")).isEqualTo(ValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassOverloadingGenericParameter() {
|
||||||
|
val proxyFactory = ProxyFactory(ValueClassOverloadingGenericParameterBean())
|
||||||
|
proxyFactory.isProxyTargetClass = true
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as ValueClassOverloadingGenericParameterBean
|
||||||
|
assertThat(proxy.returnValue("foo").value).isEqualTo("bar")
|
||||||
|
}
|
||||||
|
|
||||||
open class MyKotlinBean {
|
open class MyKotlinBean {
|
||||||
|
|
||||||
open fun capitalize(value: String) = value.uppercase()
|
open fun capitalize(value: String) = value.uppercase()
|
||||||
@@ -183,42 +349,172 @@ class CglibAopProxyKotlinTests {
|
|||||||
val updatedAt: LocalDateTime? = null,
|
val updatedAt: LocalDateTime? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationPrivateValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(PrivateValueClassBean())
|
||||||
|
proxyFactory.isProxyTargetClass = true
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
PrivateValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as PrivateValueClassBean
|
||||||
|
assertThat(proxy.returnValue()).isEqualTo(PrivateValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class ValueClass(val value: String)
|
value class ValueClass(val value: String)
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class ValueClassNullableValue(val value: String?)
|
value class ValueClassNullableValue(val value: String?)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class ValueClassPrimitiveValue(val value: Int)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class GenericValueClass<T>(val value: T)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NestedValueClass(val value: ValueClass)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NestedValueClassPrimitiveValue(val value: ValueClassPrimitiveValue)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NestedValueClassNullableValue(val value: ValueClassNullableValue)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NonNullGenericValueClass<T : Any>(val value: T)
|
||||||
|
|
||||||
open class TestBean {
|
open class TestBean {
|
||||||
open suspend fun returnValueClass(): ValueClass {
|
open suspend fun returnValueClass(): ValueClass {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return ValueClass("foo")
|
return ValueClass("foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun returnNullableValueClass(): ValueClass? {
|
open suspend fun returnNullableValueClass(): ValueClass? {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun returnValueClassNullableValue(): ValueClassNullableValue {
|
open suspend fun returnValueClassNullableValue(): ValueClassNullableValue {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return ValueClassNullableValue(null)
|
return ValueClassNullableValue(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun returnResult(): Result<String> {
|
open suspend fun returnResult(): Result<String> {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return Result.success("foo")
|
return Result.success("foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun returnString(): String {
|
open suspend fun returnString(): String {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return "foo"
|
return "foo"
|
||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun returnAny(): Any {
|
open suspend fun returnAny(): Any {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
|
return ValueClass("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnValueClassPrimitiveValue(): ValueClassPrimitiveValue {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return ValueClassPrimitiveValue(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNullableValueClassPrimitiveValue(): ValueClassPrimitiveValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNullableValueClassNullableValue(): ValueClassNullableValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNullableGenericValueClass(): GenericValueClass<String>? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNestedValueClass(): NestedValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return NestedValueClass(ValueClass("foo"))
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNullableNestedValueClassPrimitiveValue(): NestedValueClassPrimitiveValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNullableNestedValueClassNullableValue(): NestedValueClassNullableValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNullableNonNullGenericValueClass(): NonNullGenericValueClass<String>? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnNullableResult(): Result<String>? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface AnyBean {
|
||||||
|
suspend fun returnValue(): Any
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ValueClassOverridingAnyBean : AnyBean {
|
||||||
|
override suspend fun returnValue(): ValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
return ValueClass("foo")
|
return ValueClass("foo")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GenericBean<T> {
|
||||||
|
suspend fun returnValue(): T
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ValueClassOverridingGenericBean : GenericBean<ValueClass> {
|
||||||
|
override suspend fun returnValue(): ValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return ValueClass("foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GenericParameterBean<T> {
|
||||||
|
suspend fun returnValue(value: T): Any
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ValueClassOverridingGenericParameterBean : GenericParameterBean<String> {
|
||||||
|
override suspend fun returnValue(value: String): ValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return ValueClass(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ValueClassOverloadingGenericParameterBean : GenericParameterBean<Int> {
|
||||||
|
override suspend fun returnValue(value: Int): Any {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
open suspend fun returnValue(value: String): ValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return ValueClass(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
private value class PrivateValueClass(val value: String)
|
||||||
|
|
||||||
|
private open class PrivateValueClassBean {
|
||||||
|
open suspend fun returnValue(): PrivateValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return PrivateValueClass("foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+286
-7
@@ -26,6 +26,7 @@ import kotlin.time.Duration.Companion.milliseconds
|
|||||||
* Tests for Kotlin support in [JdkDynamicAopProxy].
|
* Tests for Kotlin support in [JdkDynamicAopProxy].
|
||||||
*
|
*
|
||||||
* @author Dmitry Sulman
|
* @author Dmitry Sulman
|
||||||
|
* @author Sebastien Deleuze
|
||||||
*/
|
*/
|
||||||
class JdkDynamicAopProxyKotlinTests {
|
class JdkDynamicAopProxyKotlinTests {
|
||||||
|
|
||||||
@@ -119,12 +120,182 @@ class JdkDynamicAopProxyKotlinTests {
|
|||||||
assertThat(proxy.returnAny()).isEqualTo(ValueClass("bar"))
|
assertThat(proxy.returnAny()).isEqualTo(ValueClass("bar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassPrimitiveValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassPrimitiveValue(1)
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnValueClassPrimitiveValue()).isEqualTo(ValueClassPrimitiveValue(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassPrimitiveValueProceed() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
it.proceed()
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnValueClassPrimitiveValue()).isEqualTo(ValueClassPrimitiveValue(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassPrimitiveValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassPrimitiveValue(1)
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassPrimitiveValue()).isEqualTo(ValueClassPrimitiveValue(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassPrimitiveValueNull() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
null
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassPrimitiveValue()).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassNullableValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassNullableValue("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassNullableValue()).isEqualTo(ValueClassNullableValue("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableValueClassNullableValueNull() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClassNullableValue(null)
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableValueClassNullableValue()).isEqualTo(ValueClassNullableValue(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableGenericValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
GenericValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableGenericValueClass()).isEqualTo(GenericValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNestedValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NestedValueClass(ValueClass("bar"))
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNestedValueClass()).isEqualTo(NestedValueClass(ValueClass("bar")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableNestedValueClassPrimitiveValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NestedValueClassPrimitiveValue(ValueClassPrimitiveValue(1))
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableNestedValueClassPrimitiveValue())
|
||||||
|
.isEqualTo(NestedValueClassPrimitiveValue(ValueClassPrimitiveValue(1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableNestedValueClassNullableValue() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NestedValueClassNullableValue(ValueClassNullableValue("bar"))
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableNestedValueClassNullableValue())
|
||||||
|
.isEqualTo(NestedValueClassNullableValue(ValueClassNullableValue("bar")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableNonNullGenericValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
NonNullGenericValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableNonNullGenericValueClass()).isEqualTo(NonNullGenericValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationNullableResult() {
|
||||||
|
val proxyFactory = ProxyFactory(TestBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
Result.success("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as TestBean
|
||||||
|
assertThat(proxy.returnNullableResult()?.getOrNull()).isEqualTo("bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassOverridingAny() {
|
||||||
|
val proxyFactory = ProxyFactory(ValueClassOverridingAnyBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as ValueClassOverridingAnyBean
|
||||||
|
assertThat(proxy.returnValue()).isEqualTo(ValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationValueClassOverridingGeneric() {
|
||||||
|
val proxyFactory = ProxyFactory(ValueClassOverridingGenericBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
ValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as ValueClassOverridingGenericBean
|
||||||
|
assertThat(proxy.returnValue()).isEqualTo(ValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
suspend fun proxiedSuspendedInvocationPrivateValueClass() {
|
||||||
|
val proxyFactory = ProxyFactory(PrivateValueClassBeanImpl())
|
||||||
|
proxyFactory.addAdvice(MethodInterceptor {
|
||||||
|
PrivateValueClass("bar")
|
||||||
|
})
|
||||||
|
val proxy = proxyFactory.proxy as PrivateValueClassBean
|
||||||
|
assertThat(proxy.returnValue()).isEqualTo(PrivateValueClass("bar"))
|
||||||
|
}
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class ValueClass(val value: String)
|
value class ValueClass(val value: String)
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class ValueClassNullableValue(val value: String?)
|
value class ValueClassNullableValue(val value: String?)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class ValueClassPrimitiveValue(val value: Int)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class GenericValueClass<T>(val value: T)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NestedValueClass(val value: ValueClass)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NestedValueClassPrimitiveValue(val value: ValueClassPrimitiveValue)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NestedValueClassNullableValue(val value: ValueClassNullableValue)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class NonNullGenericValueClass<T : Any>(val value: T)
|
||||||
|
|
||||||
interface TestBean {
|
interface TestBean {
|
||||||
suspend fun returnValueClass(): ValueClass
|
suspend fun returnValueClass(): ValueClass
|
||||||
|
|
||||||
@@ -137,38 +308,146 @@ class JdkDynamicAopProxyKotlinTests {
|
|||||||
suspend fun returnString(): String
|
suspend fun returnString(): String
|
||||||
|
|
||||||
suspend fun returnAny(): Any
|
suspend fun returnAny(): Any
|
||||||
|
|
||||||
|
suspend fun returnValueClassPrimitiveValue(): ValueClassPrimitiveValue
|
||||||
|
|
||||||
|
suspend fun returnNullableValueClassPrimitiveValue(): ValueClassPrimitiveValue?
|
||||||
|
|
||||||
|
suspend fun returnNullableValueClassNullableValue(): ValueClassNullableValue?
|
||||||
|
|
||||||
|
suspend fun returnNullableGenericValueClass(): GenericValueClass<String>?
|
||||||
|
|
||||||
|
suspend fun returnNestedValueClass(): NestedValueClass
|
||||||
|
|
||||||
|
suspend fun returnNullableNestedValueClassPrimitiveValue(): NestedValueClassPrimitiveValue?
|
||||||
|
|
||||||
|
suspend fun returnNullableNestedValueClassNullableValue(): NestedValueClassNullableValue?
|
||||||
|
|
||||||
|
suspend fun returnNullableNonNullGenericValueClass(): NonNullGenericValueClass<String>?
|
||||||
|
|
||||||
|
suspend fun returnNullableResult(): Result<String>?
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestBeanImpl : TestBean {
|
class TestBeanImpl : TestBean {
|
||||||
override suspend fun returnValueClass(): ValueClass {
|
override suspend fun returnValueClass(): ValueClass {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return ValueClass("foo")
|
return ValueClass("foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun returnNullableValueClass(): ValueClass? {
|
override suspend fun returnNullableValueClass(): ValueClass? {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun returnValueClassNullableValue(): ValueClassNullableValue {
|
override suspend fun returnValueClassNullableValue(): ValueClassNullableValue {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return ValueClassNullableValue(null)
|
return ValueClassNullableValue(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun returnResult(): Result<String> {
|
override suspend fun returnResult(): Result<String> {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return Result.success("foo")
|
return Result.success("foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun returnString(): String {
|
override suspend fun returnString(): String {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
return "foo"
|
return "foo"
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun returnAny(): Any {
|
override suspend fun returnAny(): Any {
|
||||||
delay(1000.milliseconds)
|
delay(10.milliseconds)
|
||||||
|
return ValueClass("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnValueClassPrimitiveValue(): ValueClassPrimitiveValue {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return ValueClassPrimitiveValue(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNullableValueClassPrimitiveValue(): ValueClassPrimitiveValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNullableValueClassNullableValue(): ValueClassNullableValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNullableGenericValueClass(): GenericValueClass<String>? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNestedValueClass(): NestedValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return NestedValueClass(ValueClass("foo"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNullableNestedValueClassPrimitiveValue(): NestedValueClassPrimitiveValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNullableNestedValueClassNullableValue(): NestedValueClassNullableValue? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNullableNonNullGenericValueClass(): NonNullGenericValueClass<String>? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun returnNullableResult(): Result<String>? {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface AnyBean {
|
||||||
|
suspend fun returnValue(): Any
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ValueClassOverridingAnyBean : AnyBean {
|
||||||
|
override suspend fun returnValue(): ValueClass
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValueClassOverridingAnyBeanImpl : ValueClassOverridingAnyBean {
|
||||||
|
override suspend fun returnValue(): ValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
return ValueClass("foo")
|
return ValueClass("foo")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
interface GenericBean<T> {
|
||||||
|
suspend fun returnValue(): T
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ValueClassOverridingGenericBean : GenericBean<ValueClass> {
|
||||||
|
override suspend fun returnValue(): ValueClass
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValueClassOverridingGenericBeanImpl : ValueClassOverridingGenericBean {
|
||||||
|
override suspend fun returnValue(): ValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return ValueClass("foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
private value class PrivateValueClass(val value: String)
|
||||||
|
|
||||||
|
private interface PrivateValueClassBean {
|
||||||
|
suspend fun returnValue(): PrivateValueClass
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PrivateValueClassBeanImpl : PrivateValueClassBean {
|
||||||
|
override suspend fun returnValue(): PrivateValueClass {
|
||||||
|
delay(10.milliseconds)
|
||||||
|
return PrivateValueClass("foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user