Skip to content

Commit b44f03f

Browse files
authored
Fix IllegalAccessException (#2469)
Being thrown on an attempt to retrieve serializer for some private implementation classes from stdlib. Fixes #2449
1 parent 919062f commit b44f03f

File tree

4 files changed

+82
-11
lines changed

4 files changed

+82
-11
lines changed

core/jvmMain/src/kotlinx/serialization/internal/Platform.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ private fun <T : Any> Class<T>.createEnumSerializer(): KSerializer<T> {
153153
}
154154

155155
private fun <T : Any> Class<T>.findObjectSerializer(): KSerializer<T>? {
156+
// Special case to avoid IllegalAccessException on Java11+ (#2449)
157+
// There are no serializable objects in the stdlib anyway.
158+
if (this.canonicalName?.let { it.startsWith("java.") || it.startsWith("kotlin.") } != false) return null
156159
// Check it is an object without using kotlin-reflect
157160
val field =
158161
declaredFields.singleOrNull { it.name == "INSTANCE" && it.type == this && Modifier.isStatic(it.modifiers) }

formats/json-tests/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@
1717
package kotlinx.serialization
1818

1919
import kotlinx.serialization.json.Json
20+
import kotlinx.serialization.test.typeTokenOf
2021
import org.junit.Test
2122
import java.util.HashMap
2223
import java.util.HashSet
2324
import kotlin.collections.LinkedHashMap
2425
import kotlin.collections.Map
2526
import kotlin.collections.hashMapOf
2627
import kotlin.collections.hashSetOf
27-
import kotlin.test.assertEquals
28+
import kotlin.reflect.*
29+
import kotlin.test.*
2830

2931

3032
class JavaCollectionsTest {
@@ -38,7 +40,7 @@ class JavaCollectionsTest {
3840
)
3941

4042
@Test
41-
fun test() {
43+
fun testJavaCollectionsInsideClass() {
4244
val original = HasHashMap("42", hashMapOf(1 to "1", 2 to "2"), hashSetOf(11), LinkedHashMap(), null)
4345
val serializer = HasHashMap.serializer()
4446
val string = Json.encodeToString(serializer = serializer, value = original)
@@ -49,4 +51,62 @@ class JavaCollectionsTest {
4951
val restored = Json.decodeFromString(deserializer = serializer, string = string)
5052
assertEquals(expected = original, actual = restored)
5153
}
54+
55+
@Test
56+
fun testTopLevelMaps() {
57+
// Returning null here is a deliberate choice: map constructor functions may return different specialized
58+
// implementations (e.g., kotlin.collections.EmptyMap or java.util.Collections.SingletonMap)
59+
// that may or may not be generic. Since we generally cannot return a generic serializer using Java class only,
60+
// all attempts to get map serializer using only .javaClass should return null.
61+
assertNull(serializerOrNull(emptyMap<String, String>().javaClass))
62+
assertNull(serializerOrNull(mapOf<String, String>("a" to "b").javaClass))
63+
assertNull(serializerOrNull(mapOf<String, String>("a" to "b", "b" to "c").javaClass))
64+
// Correct ways of retrieving map serializer:
65+
assertContains(
66+
serializer(typeTokenOf<Map<String, String>>()).descriptor.serialName,
67+
"kotlin.collections.LinkedHashMap"
68+
)
69+
assertContains(
70+
serializer(typeTokenOf<java.util.LinkedHashMap<String, String>>()).descriptor.serialName,
71+
"kotlin.collections.LinkedHashMap"
72+
)
73+
assertContains(
74+
serializer(typeOf<LinkedHashMap<String, String>>()).descriptor.serialName,
75+
"kotlin.collections.LinkedHashMap"
76+
)
77+
}
78+
79+
@Test
80+
fun testTopLevelSetsAndLists() {
81+
// Same reasoning as for maps
82+
assertNull(serializerOrNull(emptyList<String>().javaClass))
83+
assertNull(serializerOrNull(listOf<String>("a").javaClass))
84+
assertNull(serializerOrNull(listOf<String>("a", "b").javaClass))
85+
assertNull(serializerOrNull(emptySet<String>().javaClass))
86+
assertNull(serializerOrNull(setOf<String>("a").javaClass))
87+
assertNull(serializerOrNull(setOf<String>("a", "b").javaClass))
88+
assertContains(
89+
serializer(typeTokenOf<Set<String>>()).descriptor.serialName,
90+
"kotlin.collections.LinkedHashSet"
91+
)
92+
assertContains(
93+
serializer(typeTokenOf<List<String>>()).descriptor.serialName,
94+
"kotlin.collections.ArrayList"
95+
)
96+
assertContains(
97+
serializer(typeTokenOf<java.util.LinkedHashSet<String>>()).descriptor.serialName,
98+
"kotlin.collections.LinkedHashSet"
99+
)
100+
assertContains(
101+
serializer(typeTokenOf<java.util.ArrayList<String>>()).descriptor.serialName,
102+
"kotlin.collections.ArrayList"
103+
)
104+
}
105+
106+
@Test
107+
fun testAnonymousObject() {
108+
val obj: Any = object {}
109+
assertNull(serializerOrNull(obj.javaClass))
110+
}
52111
}
112+

formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,6 @@ class SerializerByTypeTest {
209209
assertEquals(expected, json.encodeToString(serial2 as KSerializer<T>, value))
210210
}
211211

212-
@PublishedApi
213-
internal open class TypeBase<T>
214-
215-
public inline fun <reified T> typeTokenOf(): Type {
216-
val base = object : TypeBase<T>() {}
217-
val superType = base::class.java.genericSuperclass!!
218-
return (superType as ParameterizedType).actualTypeArguments.first()!!
219-
}
220-
221212
class IntBox(val i: Int)
222213

223214
object CustomIntSerializer : KSerializer<IntBox> {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.test
6+
7+
import java.lang.reflect.*
8+
9+
10+
@PublishedApi
11+
internal open class TypeBase<T>
12+
13+
public inline fun <reified T> typeTokenOf(): Type {
14+
val base = object : TypeBase<T>() {}
15+
val superType = base::class.java.genericSuperclass!!
16+
return (superType as ParameterizedType).actualTypeArguments.first()!!
17+
}

0 commit comments

Comments
 (0)