Fix a regression on value class parameter handling

This commit fixes a regression introduced by gh-36449 for
nullable value class with an non-null value.

Closes gh-36665
Signed-off-by: Sigurd Gerke <sigurd.gerke@onedata.de>
This commit is contained in:
Sigurd Gerke
2026-04-17 12:18:01 +02:00
committed by Sébastien Deleuze
parent 32170e5847
commit 3dfd6838c0
6 changed files with 57 additions and 13 deletions
@@ -134,9 +134,10 @@ public abstract class CoroutinesUtils {
Object arg = args[index];
if (!(parameter.isOptional() && arg == null)) {
KType type = parameter.getType();
if (!type.isMarkedNullable() &&
if (!(type.isMarkedNullable() && arg == null) &&
type.getClassifier() instanceof KClass<?> kClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass)) &&
!JvmClassMappingKt.getJavaClass(kClass).isInstance(arg)) {
arg = box(kClass, arg);
}
argMap.put(parameter, arg);
@@ -166,9 +167,10 @@ public abstract class CoroutinesUtils {
private static Object box(KClass<?> kClass, @Nullable Object arg) {
KFunction<?> constructor = Objects.requireNonNull(KClasses.getPrimaryConstructor(kClass));
KType type = constructor.getParameters().get(0).getType();
if (!type.isMarkedNullable() &&
if (!(type.isMarkedNullable() && arg == null) &&
type.getClassifier() instanceof KClass<?> parameterClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass))) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass)) &&
!JvmClassMappingKt.getJavaClass(parameterClass).isInstance(arg)) {
arg = box(parameterClass, arg);
}
if (!KCallablesJvm.isAccessible(constructor)) {
@@ -236,6 +236,13 @@ class CoroutinesUtilsTests {
Assertions.assertThat(mono.awaitSingleOrNull()).isEqualTo("foo")
}
@Test
suspend fun invokeSuspendingFunctionWithNullableValueClassParameterAndUnderlyingValue() {
val method = CoroutinesUtilsTests::class.java.declaredMethods.first { it.name.startsWith("suspendingFunctionWithNullableValueClass") }
val mono = CoroutinesUtils.invokeSuspendingFunction(method, this, "foo", null) as Mono
Assertions.assertThat(mono.awaitSingleOrNull()).isEqualTo("foo")
}
@Test
suspend fun invokeSuspendingFunctionWithNullableValueClassParameter() {
val method = CoroutinesUtilsTests::class.java.declaredMethods.first { it.name.startsWith("suspendingFunctionWithNullableValueClass") }
@@ -316,9 +316,10 @@ public class InvocableHandlerMethod extends HandlerMethod {
Object arg = args[index];
if (!(parameter.isOptional() && arg == null)) {
KType type = parameter.getType();
if (!type.isMarkedNullable() &&
if (!(type.isMarkedNullable() && arg == null) &&
type.getClassifier() instanceof KClass<?> kClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass)) &&
!JvmClassMappingKt.getJavaClass(kClass).isInstance(arg)) {
arg = box(kClass, arg);
}
argMap.put(parameter, arg);
@@ -337,9 +338,10 @@ public class InvocableHandlerMethod extends HandlerMethod {
private static Object box(KClass<?> kClass, @Nullable Object arg) {
KFunction<?> constructor = Objects.requireNonNull(KClasses.getPrimaryConstructor(kClass));
KType type = constructor.getParameters().get(0).getType();
if (!type.isMarkedNullable() &&
if (!(type.isMarkedNullable() && arg == null) &&
type.getClassifier() instanceof KClass<?> parameterClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass))) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass)) &&
!JvmClassMappingKt.getJavaClass(parameterClass).isInstance(arg)) {
arg = box(parameterClass, arg);
}
if (!KCallablesJvm.isAccessible(constructor)) {
@@ -155,6 +155,13 @@ class InvocableHandlerMethodKotlinTests {
Assertions.assertThat(value).isEqualTo(1L)
}
@Test
fun valueClassWithNullableAndUnderlyingValue() {
composite.addResolver(StubArgumentResolver(LongValueClass::class.java, 1L))
val value = getInvocable(ValueClassHandler::valueClassWithNullable.javaMethod!!).invokeForRequest(request, null)
Assertions.assertThat(value).isEqualTo(1L)
}
@Test
fun valueClassWithNullable() {
composite.addResolver(StubArgumentResolver(LongValueClass::class.java, null))
@@ -215,6 +222,14 @@ class InvocableHandlerMethodKotlinTests {
StepVerifier.create(value as Mono<Long>).verifyComplete()
}
@Test
fun suspendingValueClassWithNullableAndUnderlyingValue() {
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
composite.addResolver(StubArgumentResolver(LongValueClass::class.java, 1L))
val value = getInvocable(SuspendingValueClassHandler::valueClassWithNullable.javaMethod!!).invokeForRequest(request, null)
StepVerifier.create(value as Mono<Long>).expectNext(1L).verifyComplete()
}
@Test
fun suspendingValueClassWithPrivateConstructor() {
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
@@ -356,9 +356,10 @@ public class InvocableHandlerMethod extends HandlerMethod {
Object arg = args[index];
if (!(parameter.isOptional() && arg == null)) {
KType type = parameter.getType();
if (!type.isMarkedNullable() &&
if (!(type.isMarkedNullable() && arg == null) &&
type.getClassifier() instanceof KClass<?> kClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass)) &&
!JvmClassMappingKt.getJavaClass(kClass).isInstance(arg)) {
arg = box(kClass, arg);
}
argMap.put(parameter, arg);
@@ -378,9 +379,10 @@ public class InvocableHandlerMethod extends HandlerMethod {
private static Object box(KClass<?> kClass, @Nullable Object arg) {
KFunction<?> constructor = Objects.requireNonNull(KClasses.getPrimaryConstructor(kClass));
KType type = constructor.getParameters().get(0).getType();
if (!type.isMarkedNullable() &&
if (!(type.isMarkedNullable() && arg == null) &&
type.getClassifier() instanceof KClass<?> parameterClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass))) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass)) &&
!JvmClassMappingKt.getJavaClass(parameterClass).isInstance(arg)) {
arg = box(parameterClass, arg);
}
if (!KCallablesJvm.isAccessible(constructor)) {
@@ -258,6 +258,14 @@ class InvocableHandlerMethodKotlinTests {
assertHandlerResultValue(result, "1")
}
@Test
fun valueClassWithNullableAndUnderlyingValue() {
this.resolvers.add(stubResolver(1L, LongValueClass::class.java))
val method = ValueClassController::valueClassWithNullable.javaMethod!!
val result = invoke(ValueClassController(), method)
assertHandlerResultValue(result, "1")
}
@Test
fun valueClassWithNullable() {
this.resolvers.add(stubResolver(null, LongValueClass::class.java))
@@ -320,6 +328,14 @@ class InvocableHandlerMethodKotlinTests {
assertHandlerResultValue(result, "null")
}
@Test
fun suspendingValueClassWithNullableAndUnderlyingValue() {
this.resolvers.add(stubResolver(1L, LongValueClass::class.java))
val method = SuspendingValueClassController::valueClassWithNullable.javaMethod!!
val result = invoke(SuspendingValueClassController(), method)
assertHandlerResultValue(result, "1")
}
@Test
fun suspendingValueClassWithPrivateConstructor() {
this.resolvers.add(stubResolver(1L, Long::class.java))
@@ -590,4 +606,4 @@ class InvocableHandlerMethodKotlinTests {
}
class CustomException(message: String) : Throwable(message)
}
}