Take into account 'KTypeImpl.classifier' in its hashCode and equals
Since KT-53308, we started to cache results of typeOf invocation in reflection. The cache uses the origin 'KClassifier' as a key with an optional list of 'KTypeProjection' in type argument position, and computed result as a value. Without classifier check, the caching produces incorrect execution in a very specific classloaders usage which is leveraged by IDEA source-tree: * All Kotlin stdlib and reflect classes are always loaded by the same classloader * Other classes that depend on Kotlin are loaded by separate classloaders The reproducer: * Attempt to use typeOf<kotlin.List<Foo>> from one classloader caches the resulting KType * Attempt to use typeOf<kotlin.List<Foo>> from another classloader for the same 'Foo' that differs only in classloader reuses the computed KType, meaning that KType type argument classifier is simply incorrect and points to the 'Foo' from the first classloader #KT-54611 Fixed #KT-54629 Fixed Merge-request: KT-MR-7470 Merged-by: Vsevolod Tolstopyatov <qwwdfsad@gmail.com>
This commit is contained in:
committed by
Space Team
parent
c098ba789c
commit
b29309478e
@@ -0,0 +1,25 @@
|
||||
package test
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.reflect.full.*
|
||||
import kotlin.test.*
|
||||
|
||||
class K {
|
||||
fun getType() = typeOf<List<K>>()
|
||||
}
|
||||
|
||||
class Test {
|
||||
fun kClass(): Any = K::class
|
||||
|
||||
fun KClass<*>.invokeGetType() =
|
||||
java.declaredMethods.single { it.name == "getType" }.invoke(java.getDeclaredConstructor().newInstance()) as KType
|
||||
|
||||
fun doTest(k1: KClass<*>, k2: KClass<*>) {
|
||||
assertNotEquals(k1, k2)
|
||||
|
||||
val type1 = k1.invokeGetType()
|
||||
val type2 = k2.invokeGetType()
|
||||
assertNotEquals((type1.arguments[0].type)?.classifier, (type2.arguments[0].type)?.classifier)
|
||||
assertNotEquals(type1, type2)
|
||||
}
|
||||
}
|
||||
@@ -77,4 +77,17 @@ class ReflectionClassLoaderTest : CodegenTestCase() {
|
||||
ChildClassLoader(parent)
|
||||
)
|
||||
}
|
||||
|
||||
fun testKTypeEquality() {
|
||||
/*
|
||||
* Check that typeOf<List<Clz>>() when clz is loaded by different classloaders
|
||||
* differs in both its `equals` and its `classifier`.
|
||||
* It is important in the face of KType caching
|
||||
*/
|
||||
loadFile("$prefix/kTypeEquality.kt")
|
||||
doTest(
|
||||
createClassLoader(),
|
||||
createClassLoader()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ internal class KTypeImpl(
|
||||
val type: KotlinType,
|
||||
computeJavaType: (() -> Type)? = null
|
||||
) : KTypeBase {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val computeJavaType =
|
||||
computeJavaType as? ReflectProperties.LazySoftVal<Type> ?: computeJavaType?.let(ReflectProperties::lazySoft)
|
||||
|
||||
@@ -126,10 +125,10 @@ internal class KTypeImpl(
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
other is KTypeImpl && type == other.type
|
||||
other is KTypeImpl && type == other.type && classifier == other.classifier && arguments == other.arguments
|
||||
|
||||
override fun hashCode() =
|
||||
type.hashCode()
|
||||
(31 * ((31 * type.hashCode()) + classifier.hashCode())) + arguments.hashCode()
|
||||
|
||||
override fun toString() =
|
||||
ReflectionObjectRenderer.renderType(type)
|
||||
|
||||
Reference in New Issue
Block a user