Skip to content

Commit b28153e

Browse files
T45Ksdeleuze
authored andcommitted
Fix handling of value class with private constructor
See gh-32536
1 parent 88b9c05 commit b28153e

File tree

6 files changed

+65
-3
lines changed

6 files changed

+65
-3
lines changed

spring-core/src/main/java/org/springframework/core/CoroutinesUtils.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ public static Publisher<?> invokeSuspendingFunction(
130130
KType type = parameter.getType();
131131
if (!(type.isMarkedNullable() && arg == null) && type.getClassifier() instanceof KClass<?> kClass
132132
&& KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
133-
arg = KClasses.getPrimaryConstructor(kClass).call(arg);
133+
KFunction<?> valueClassConstructor = KClasses.getPrimaryConstructor(kClass);
134+
KCallablesJvm.setAccessible(valueClassConstructor, true);
135+
arg = valueClassConstructor.call(arg);
134136
}
135137
argMap.put(parameter, arg);
136138
}

spring-core/src/test/kotlin/org/springframework/core/CoroutinesUtilsTests.kt

+23
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import reactor.core.publisher.Mono
2828
import reactor.test.StepVerifier
2929
import kotlin.coroutines.Continuation
3030
import kotlin.coroutines.coroutineContext
31+
import kotlin.reflect.full.primaryConstructor
32+
import kotlin.reflect.jvm.isAccessible
3133

3234
/**
3335
* Kotlin tests for [CoroutinesUtils].
@@ -206,6 +208,15 @@ class CoroutinesUtilsTests {
206208
}
207209
}
208210

211+
@Test
212+
fun invokeSuspendingFunctionWithValueClassWithPrivateConstructorParameter() {
213+
val method = CoroutinesUtilsTests::class.java.declaredMethods.first { it.name.startsWith("suspendingFunctionWithValueClassWithPrivateConstructor") }
214+
val mono = CoroutinesUtils.invokeSuspendingFunction(method, this, "foo", null) as Mono
215+
runBlocking {
216+
Assertions.assertThat(mono.awaitSingleOrNull()).isEqualTo("foo")
217+
}
218+
}
219+
209220
@Test
210221
fun invokeSuspendingFunctionWithExtension() {
211222
val method = CoroutinesUtilsTests::class.java.getDeclaredMethod("suspendingFunctionWithExtension",
@@ -293,6 +304,11 @@ class CoroutinesUtilsTests {
293304
return value?.value
294305
}
295306

307+
suspend fun suspendingFunctionWithValueClassWithPrivateConstructor(value: ValueClassWithPrivateConstructor): String? {
308+
delay(1)
309+
return value.value
310+
}
311+
296312
suspend fun CustomException.suspendingFunctionWithExtension(): String {
297313
delay(1)
298314
return "${this.message}"
@@ -331,6 +347,13 @@ class CoroutinesUtilsTests {
331347
}
332348
}
333349

350+
@JvmInline
351+
value class ValueClassWithPrivateConstructor private constructor(val value: String) {
352+
companion object {
353+
fun from(value: String) = ValueClassWithPrivateConstructor(value)
354+
}
355+
}
356+
334357
class CustomException(message: String) : Throwable(message)
335358

336359
}

spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,9 @@ public static Object invokeFunction(Method method, Object target, Object[] args)
319319
KType type = parameter.getType();
320320
if (!(type.isMarkedNullable() && arg == null) && type.getClassifier() instanceof KClass<?> kClass
321321
&& KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
322-
arg = KClasses.getPrimaryConstructor(kClass).call(arg);
322+
KFunction<?> valueClassConstructor = KClasses.getPrimaryConstructor(kClass);
323+
KCallablesJvm.setAccessible(valueClassConstructor, true);
324+
arg = valueClassConstructor.call(arg);
323325
}
324326
argMap.put(parameter, arg);
325327
}

spring-web/src/test/kotlin/org/springframework/web/method/support/InvocableHandlerMethodKotlinTests.kt

+16
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ class InvocableHandlerMethodKotlinTests {
112112
Assertions.assertThat(value).isNull()
113113
}
114114

115+
@Test
116+
fun valueClassWithPrivateConstructor() {
117+
composite.addResolver(StubArgumentResolver(Char::class.java, 'a'))
118+
val value = getInvocable(ValueClassHandler::class.java, Char::class.java).invokeForRequest(request, null)
119+
Assertions.assertThat(value).isEqualTo('a')
120+
}
121+
115122
@Test
116123
fun propertyAccessor() {
117124
val value = getInvocable(PropertyAccessorHandler::class.java).invokeForRequest(request, null)
@@ -191,6 +198,8 @@ class InvocableHandlerMethodKotlinTests {
191198
fun valueClassWithNullable(limit: LongValueClass?) =
192199
limit?.value
193200

201+
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) =
202+
limit.value
194203
}
195204

196205
private class PropertyAccessorHandler {
@@ -238,6 +247,13 @@ class InvocableHandlerMethodKotlinTests {
238247
}
239248
}
240249

250+
@JvmInline
251+
value class ValueClassWithPrivateConstructor private constructor(val value: Char) {
252+
companion object {
253+
fun from(value: Char) = ValueClassWithPrivateConstructor(value)
254+
}
255+
}
256+
241257
class CustomException(message: String) : Throwable(message)
242258

243259
}

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,9 @@ public static Object invokeFunction(Method method, Object target, Object[] args,
330330
KType type = parameter.getType();
331331
if (!(type.isMarkedNullable() && arg == null) && type.getClassifier() instanceof KClass<?> kClass
332332
&& KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
333-
arg = KClasses.getPrimaryConstructor(kClass).call(arg);
333+
KFunction<?> valueClassConstructor = KClasses.getPrimaryConstructor(kClass);
334+
KCallablesJvm.setAccessible(valueClassConstructor, true);
335+
arg = valueClassConstructor.call(arg);
334336
}
335337
argMap.put(parameter, arg);
336338
}

spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/InvocableHandlerMethodKotlinTests.kt

+17
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@ class InvocableHandlerMethodKotlinTests {
222222
assertHandlerResultValue(result, "null")
223223
}
224224

225+
@Test
226+
fun valueClassWithPrivateConstructor() {
227+
this.resolvers.add(stubResolver(1L, Long::class.java))
228+
val method = ValueClassController::valueClassWithPrivateConstructor.javaMethod!!
229+
val result = invoke(ValueClassController(), method, 1L)
230+
assertHandlerResultValue(result, "1")
231+
}
232+
225233
@Test
226234
fun propertyAccessor() {
227235
this.resolvers.add(stubResolver(null, String::class.java))
@@ -368,6 +376,8 @@ class InvocableHandlerMethodKotlinTests {
368376
fun valueClassWithNullable(limit: LongValueClass?) =
369377
"${limit?.value}"
370378

379+
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) =
380+
"${limit.value}"
371381
}
372382

373383
class PropertyAccessorController {
@@ -416,5 +426,12 @@ class InvocableHandlerMethodKotlinTests {
416426
}
417427
}
418428

429+
@JvmInline
430+
value class ValueClassWithPrivateConstructor private constructor(val value: Long) {
431+
companion object {
432+
fun from(value: Long) = ValueClassWithPrivateConstructor(value)
433+
}
434+
}
435+
419436
class CustomException(message: String) : Throwable(message)
420437
}

0 commit comments

Comments
 (0)