Files
kotlin-fork/compiler/tests/org/jetbrains/kotlin/codegen/ReflectionClassLoaderTest.kt
T
Vsevolod Tolstopyatov b29309478e 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>
2022-10-24 19:05:08 +00:00

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()
)
}
}