b29309478e
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>
94 lines
2.9 KiB
Kotlin
94 lines
2.9 KiB
Kotlin
/*
|
|
* Copyright 2010-2015 JetBrains s.r.o.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.jetbrains.kotlin.codegen
|
|
|
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
|
|
|
class ReflectionClassLoaderTest : CodegenTestCase() {
|
|
override fun getPrefix() = "reflection/classLoaders"
|
|
|
|
override fun setUp() {
|
|
super.setUp()
|
|
configurationKind = ConfigurationKind.ALL
|
|
createEnvironmentWithMockJdkAndIdeaAnnotations(configurationKind)
|
|
}
|
|
|
|
private fun Class<*>.methodByName(name: String) = declaredMethods.single { it.name == name }
|
|
|
|
fun doTest(cl1: ClassLoader, cl2: ClassLoader) {
|
|
val t1 = cl1.loadClass("test.Test")
|
|
val t2 = cl2.loadClass("test.Test")
|
|
|
|
fun Class<*>.getKClass() = methodByName("kClass")(newInstance())
|
|
|
|
t1.methodByName("doTest")(t1.newInstance(), t1.getKClass(), t2.getKClass())
|
|
}
|
|
|
|
fun testSimpleDifferentClassLoaders() {
|
|
loadFile("$prefix/differentClassLoaders.kt")
|
|
|
|
doTest(
|
|
createClassLoader(),
|
|
createClassLoader()
|
|
)
|
|
}
|
|
|
|
fun testClassLoaderWithNonTrivialEqualsAndHashCode() {
|
|
// Check that class loaders do not participate as keys in hash maps (use identity hash maps instead)
|
|
|
|
loadFile("$prefix/differentClassLoaders.kt")
|
|
|
|
class BrokenEqualsClassLoader(parent: ClassLoader) : ClassLoader(parent) {
|
|
override fun equals(other: Any?) = true
|
|
override fun hashCode() = 0
|
|
}
|
|
|
|
doTest(
|
|
BrokenEqualsClassLoader(createClassLoader()),
|
|
BrokenEqualsClassLoader(createClassLoader())
|
|
)
|
|
}
|
|
|
|
fun testParentFirst() {
|
|
// Check that for a child class loader, a class reference would be the same as for his parent
|
|
|
|
loadFile("$prefix/parentFirst.kt")
|
|
|
|
class ChildClassLoader(parent: ClassLoader) : ClassLoader(parent)
|
|
|
|
val parent = createClassLoader()
|
|
|
|
doTest(
|
|
parent,
|
|
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()
|
|
)
|
|
}
|
|
}
|