[kotlin.reflect] Introduce ClassValue-based cache for KClassImpl
* Replace pcollections with ClassValue/ConcurrentHashMap-based caches * Do not store weak references, instead cache strong references and count on ClassValue to unload the corresponding classloader if necessary * ConcurrentHashMap does not rely on WeakReference as it's only selected on Android where classloader leaks don't exist * Update reflect/scripting JDK requirement to Java 8 in order to proceed #KT-53454 #KT-50705 Merge-request: KT-MR-6788 Merged-by: Vsevolod Tolstopyatov <qwwdfsad@gmail.com>
This commit is contained in:
committed by
Space
parent
d5164fbc86
commit
14b13a2f17
-46
@@ -19022,52 +19022,6 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class HashPMap {
|
||||
@Test
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("empty.kt")
|
||||
public void testEmpty() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/empty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("manyNumbers.kt")
|
||||
public void testManyNumbers() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/manyNumbers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rewriteWithDifferent.kt")
|
||||
public void testRewriteWithDifferent() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithDifferent.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rewriteWithEqual.kt")
|
||||
public void testRewriteWithEqual() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithEqual.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simplePlusGet.kt")
|
||||
public void testSimplePlusGet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusGet.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simplePlusMinus.kt")
|
||||
public void testSimplePlusMinus() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusMinus.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.jvm.internal.pcollections.HashPMap
|
||||
import kotlin.test.*
|
||||
|
||||
fun box(): String {
|
||||
val map = HashPMap.empty<String, Any>()!!
|
||||
|
||||
assertEquals(0, map.size())
|
||||
|
||||
assertFalse(map.containsKey(""))
|
||||
assertFalse(map.containsKey("abacaba"))
|
||||
assertEquals(null, map[""])
|
||||
assertEquals(null, map["lol"])
|
||||
|
||||
// Check that doesn't create a new map
|
||||
assertEquals(map, map.minus(""))
|
||||
|
||||
// Check that all empty()s are equal
|
||||
val other = HashPMap.empty<String, Any>()!!
|
||||
assertEquals(map, other)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// Out of memory on Android 4.4
|
||||
// IGNORE_BACKEND: ANDROID
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
import java.util.*
|
||||
import kotlin.reflect.jvm.internal.pcollections.HashPMap
|
||||
import kotlin.test.*
|
||||
|
||||
fun digitSum(number: Int): Int {
|
||||
var x = number
|
||||
var ans = 0
|
||||
while (x != 0) {
|
||||
ans += x % 10
|
||||
x /= 10
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
val N = 1000000
|
||||
|
||||
fun box(): String {
|
||||
var map = HashPMap.empty<Int, Any>()!!
|
||||
|
||||
for (x in 1..N) {
|
||||
map = map.plus(x, digitSum(x))!!
|
||||
}
|
||||
|
||||
assertEquals(N, map.size())
|
||||
|
||||
// Check in reverse order just in case
|
||||
for (x in N downTo 1) {
|
||||
assertTrue(map.containsKey(x), "Not found: $x")
|
||||
assertEquals(digitSum(x), map[x], "Incorrect value for $x")
|
||||
}
|
||||
|
||||
// Delete in random order
|
||||
val list = (1..N).toCollection(ArrayList<Int>())
|
||||
Collections.shuffle(list, Random(42))
|
||||
for (x in list) {
|
||||
map = map.minus(x)!!
|
||||
}
|
||||
|
||||
assertEquals(0, map.size())
|
||||
|
||||
for (x in 1..N) {
|
||||
assertFalse(map.containsKey(x), "Incorrectly found: $x")
|
||||
assertEquals(null, map[x], "Incorrectly found value for $x")
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.jvm.internal.pcollections.HashPMap
|
||||
import kotlin.test.*
|
||||
|
||||
fun box(): String {
|
||||
var map = HashPMap.empty<String, Any>()!!
|
||||
|
||||
map = map.plus("lol", 42)!!
|
||||
map = map.plus("lol", 239)!!
|
||||
|
||||
assertEquals(1, map.size())
|
||||
assertTrue(map.containsKey("lol"))
|
||||
assertFalse(map.containsKey(""))
|
||||
assertEquals(239, map["lol"])
|
||||
assertEquals(null, map[""])
|
||||
|
||||
map = map.plus("", 0)!!
|
||||
map = map.plus("", 2.71828)!!
|
||||
map = map.plus("lol", 42)!!
|
||||
map = map.plus("", 3.14)!!
|
||||
|
||||
assertEquals(2, map.size())
|
||||
assertTrue(map.containsKey("lol"))
|
||||
assertTrue(map.containsKey(""))
|
||||
assertEquals(42, map["lol"])
|
||||
assertEquals(3.14, map[""])
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.jvm.internal.pcollections.HashPMap
|
||||
import kotlin.test.*
|
||||
|
||||
fun box(): String {
|
||||
var map = HashPMap.empty<String, Any>()!!
|
||||
|
||||
map = map.plus("lol", 42)!!
|
||||
map = map.plus("lol", 42)!!
|
||||
|
||||
assertEquals(1, map.size())
|
||||
assertTrue(map.containsKey("lol"))
|
||||
assertFalse(map.containsKey(""))
|
||||
assertEquals(42, map["lol"])
|
||||
assertEquals(null, map[""])
|
||||
|
||||
map = map.plus("", 0)!!
|
||||
map = map.plus("", 0)!!
|
||||
map = map.plus("lol", 42)!!
|
||||
map = map.plus("", 0)!!
|
||||
|
||||
assertEquals(2, map.size())
|
||||
assertTrue(map.containsKey("lol"))
|
||||
assertTrue(map.containsKey(""))
|
||||
assertEquals(42, map["lol"])
|
||||
assertEquals(0, map[""])
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.jvm.internal.pcollections.HashPMap
|
||||
import kotlin.test.*
|
||||
|
||||
fun box(): String {
|
||||
var map = HashPMap.empty<String, Any>()!!
|
||||
|
||||
map = map.plus("lol", 42)!!
|
||||
|
||||
assertEquals(1, map.size())
|
||||
assertTrue(map.containsKey("lol"))
|
||||
assertFalse(map.containsKey(""))
|
||||
assertEquals(42, map["lol"])
|
||||
assertEquals(null, map[""])
|
||||
|
||||
map = map.plus("", 0)!!
|
||||
|
||||
assertEquals(2, map.size())
|
||||
assertTrue(map.containsKey("lol"))
|
||||
assertTrue(map.containsKey(""))
|
||||
assertEquals(42, map["lol"])
|
||||
assertEquals(0, map[""])
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.jvm.internal.pcollections.HashPMap
|
||||
import kotlin.test.*
|
||||
|
||||
fun box(): String {
|
||||
var map = HashPMap.empty<String, Any>()!!
|
||||
|
||||
map = map.plus("lol", 42)!!
|
||||
map = map.minus("lol")!!
|
||||
|
||||
assertEquals(0, map.size())
|
||||
assertFalse(map.containsKey("lol"))
|
||||
assertEquals(null, map["lol"])
|
||||
|
||||
map = map.plus("abc", "a")!!
|
||||
map = map.minus("abc")!!
|
||||
map = map.plus("abc", "d")!!
|
||||
|
||||
assertEquals(1, map.size())
|
||||
assertTrue(map.containsKey("abc"))
|
||||
assertEquals("d", map["abc"])
|
||||
|
||||
return "OK"
|
||||
}
|
||||
-46
@@ -18452,52 +18452,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class HashPMap {
|
||||
@Test
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("empty.kt")
|
||||
public void testEmpty() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/empty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("manyNumbers.kt")
|
||||
public void testManyNumbers() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/manyNumbers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rewriteWithDifferent.kt")
|
||||
public void testRewriteWithDifferent() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithDifferent.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rewriteWithEqual.kt")
|
||||
public void testRewriteWithEqual() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithEqual.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simplePlusGet.kt")
|
||||
public void testSimplePlusGet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusGet.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simplePlusMinus.kt")
|
||||
public void testSimplePlusMinus() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusMinus.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
-46
@@ -19022,52 +19022,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class HashPMap {
|
||||
@Test
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("empty.kt")
|
||||
public void testEmpty() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/empty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("manyNumbers.kt")
|
||||
public void testManyNumbers() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/manyNumbers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rewriteWithDifferent.kt")
|
||||
public void testRewriteWithDifferent() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithDifferent.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rewriteWithEqual.kt")
|
||||
public void testRewriteWithEqual() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithEqual.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simplePlusGet.kt")
|
||||
public void testSimplePlusGet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusGet.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simplePlusMinus.kt")
|
||||
public void testSimplePlusMinus() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusMinus.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
-43
@@ -15341,49 +15341,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class HashPMap extends AbstractLightAnalysisModeTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("empty.kt")
|
||||
public void testEmpty() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/empty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("manyNumbers.kt")
|
||||
public void testManyNumbers() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/manyNumbers.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rewriteWithDifferent.kt")
|
||||
public void testRewriteWithDifferent() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithDifferent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rewriteWithEqual.kt")
|
||||
public void testRewriteWithEqual() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/rewriteWithEqual.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simplePlusGet.kt")
|
||||
public void testSimplePlusGet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusGet.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simplePlusMinus.kt")
|
||||
public void testSimplePlusMinus() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/hashPMap/simplePlusMinus.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -31,7 +31,6 @@ class CodeConformanceTest : TestCase() {
|
||||
"compiler/testData/psi/kdoc",
|
||||
"compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt",
|
||||
"compiler/util/src/org/jetbrains/kotlin/config/MavenComparableVersion.java",
|
||||
"core/reflection.jvm/src/kotlin/reflect/jvm/internal/pcollections",
|
||||
"dependencies",
|
||||
"dependencies/protobuf/protobuf-relocated/build",
|
||||
"dist",
|
||||
|
||||
@@ -25,13 +25,6 @@
|
||||
** toString();
|
||||
}
|
||||
|
||||
# For tests on HashPMap, see compiler/testData/codegen/box/hashPMap
|
||||
-keepclassmembers class kotlin.reflect.jvm.internal.pcollections.HashPMap {
|
||||
public int size();
|
||||
public boolean containsKey(java.lang.Object);
|
||||
public kotlin.reflect.jvm.internal.pcollections.HashPMap minus(java.lang.Object);
|
||||
}
|
||||
|
||||
# This is needed because otherwise ProGuard strips generic signature of this class (even though we pass `-keepattributes Signature` above)
|
||||
# See KT-23962 and https://sourceforge.net/p/proguard/bugs/482/
|
||||
-keep class kotlin.reflect.jvm.internal.impl.protobuf.GeneratedMessageLite$ExtendableMessageOrBuilder
|
||||
|
||||
+4
-1
@@ -18,4 +18,7 @@
|
||||
-dontnote kotlin.internal.PlatformImplementationsKt
|
||||
|
||||
# Don't note on internal APIs, as there is some class relocating that shrinkers may unnecessarily find suspicious.
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
|
||||
# Statically guarded by try-catch block and not used on Android, see CacheByClass
|
||||
-dontwarn java.lang.ClassValue
|
||||
|
||||
+9
-1
@@ -13,4 +13,12 @@
|
||||
-dontnote kotlin.internal.PlatformImplementationsKt
|
||||
|
||||
# Don't note on internal APIs, as there is some class relocating that shrinkers may unnecessarily find suspicious.
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
|
||||
# Statically guarded by try-catch block and not used on Android, see CacheByClass
|
||||
-dontwarn java.lang.ClassValue
|
||||
|
||||
# Do not even execute try-catch block for ClassValue
|
||||
-assumenosideeffects class kotlin.reflect.jvm.internal.CacheByClassKt {
|
||||
boolean useClassValue return false;
|
||||
}
|
||||
|
||||
+4
-1
@@ -20,4 +20,7 @@
|
||||
-dontnote kotlin.internal.PlatformImplementationsKt
|
||||
|
||||
# Don't note on internal APIs, as there is some class relocating that shrinkers may unnecessarily find suspicious.
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
|
||||
# Statically guarded by try-catch block and not used on Android, see CacheByClass
|
||||
-dontwarn java.lang.ClassValue
|
||||
|
||||
@@ -20,4 +20,7 @@
|
||||
-dontnote kotlin.internal.PlatformImplementationsKt
|
||||
|
||||
# Don't note on internal APIs, as there is some class relocating that shrinkers may unnecessarily find suspicious.
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
-dontwarn kotlin.reflect.jvm.internal.**
|
||||
|
||||
# Statically guarded by try-catch block and not used on Android, see CacheByClass
|
||||
-dontwarn java.lang.ClassValue
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
package kotlin.reflect.full
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import kotlin.reflect.KClassifier
|
||||
import kotlin.reflect.KType
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/*
|
||||
* By default, we use ClassValue-based caches in reflection to avoid classloader leaks,
|
||||
* but ClassValue is not available on Android, thus we attempt to check it dynamically
|
||||
* and fallback to ConcurrentHashMap-based cache.
|
||||
*
|
||||
* NB: if you are changing the name of the outer file (CacheByClass.kt), please also change the corresponding
|
||||
* proguard rules
|
||||
*/
|
||||
private val useClassValue = runCatching {
|
||||
Class.forName("java.lang.ClassValue")
|
||||
}.map { true }.getOrDefault(false)
|
||||
|
||||
internal abstract class CacheByClass<V> {
|
||||
abstract fun get(key: Class<*>): V
|
||||
|
||||
abstract fun clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a **strongly referenced** cache of values associated with [Class].
|
||||
* Values are computed using provided [compute] function.
|
||||
*
|
||||
* `null` values are not supported, though there aren't any technical limitations.
|
||||
*/
|
||||
internal fun <V : Any> createCache(compute: (Class<*>) -> V): CacheByClass<V> {
|
||||
return if (useClassValue) ClassValueCache(compute) else ConcurrentHashMapCache(compute)
|
||||
}
|
||||
|
||||
private class ClassValueCache<V>(private val compute: (Class<*>) -> V) : CacheByClass<V>() {
|
||||
|
||||
@Volatile
|
||||
private var classValue = initClassValue()
|
||||
|
||||
private fun initClassValue() = object : ClassValue<V>() {
|
||||
override fun computeValue(type: Class<*>): V {
|
||||
return compute(type)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(key: Class<*>): V = classValue[key]
|
||||
|
||||
override fun clear() {
|
||||
/*
|
||||
* ClassValue does not have a proper `clear()` method but is properly weak-referenced,
|
||||
* thus abandoning ClassValue instance will eventually clear all associated values.
|
||||
*/
|
||||
classValue = initClassValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We no longer support Java 6, so the only place we use this cache is Android, where there
|
||||
* are no classloader leaks issue, thus we can safely use strong references and do not bother
|
||||
* with WeakReference wrapping.
|
||||
*/
|
||||
private class ConcurrentHashMapCache<V>(private val compute: (Class<*>) -> V) : CacheByClass<V>() {
|
||||
private val cache = ConcurrentHashMap<Class<*>, V>()
|
||||
|
||||
override fun get(key: Class<*>): V = cache.getOrPut(key) { compute(key) }
|
||||
|
||||
override fun clear() {
|
||||
cache.clear()
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
|
||||
package kotlin.reflect.jvm.internal;
|
||||
|
||||
import kotlin.SinceKotlin;
|
||||
import kotlin.jvm.internal.*;
|
||||
import kotlin.reflect.*;
|
||||
import kotlin.reflect.full.KClassifiers;
|
||||
|
||||
@@ -1,69 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.reflect.jvm.internal.pcollections.HashPMap
|
||||
|
||||
// TODO: collect nulls periodically
|
||||
// Key of the map is Class.getName(), each value is either a WeakReference<KClassImpl<*>> or an Array<WeakReference<KClassImpl<*>>>.
|
||||
// Arrays are needed because the same class can be loaded by different class loaders, which results in different Class instances.
|
||||
// This variable is not volatile intentionally: we don't care if there's a data race on it and some KClass instances will be lost.
|
||||
// We do care however about general performance on read access to it, thus no synchronization is done here whatsoever
|
||||
private var K_CLASS_CACHE = HashPMap.empty<String, Any>()
|
||||
private val K_CLASS_CACHE = createCache { KClassImpl(it) }
|
||||
|
||||
// This function is invoked on each reflection access to Java classes, properties, etc. Performance is critical here.
|
||||
internal fun <T : Any> getOrCreateKotlinClass(jClass: Class<T>): KClassImpl<T> {
|
||||
val name = jClass.name
|
||||
val cached = K_CLASS_CACHE[name]
|
||||
if (cached is WeakReference<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val kClass = cached.get() as KClassImpl<T>?
|
||||
if (kClass?.jClass == jClass) {
|
||||
return kClass
|
||||
}
|
||||
} else if (cached != null) {
|
||||
// If the cached value is not a weak reference, it's an array of weak references
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(cached as Array<WeakReference<KClassImpl<T>>>)
|
||||
for (ref in cached) {
|
||||
val kClass = ref.get()
|
||||
if (kClass?.jClass == jClass) {
|
||||
return kClass
|
||||
}
|
||||
}
|
||||
|
||||
// This is the most unlikely case: we found a cached array of references of length at least 2 (can't be 1 because
|
||||
// the single element would be cached instead), and none of those classes is the one we're looking for
|
||||
val size = cached.size
|
||||
val newArray = arrayOfNulls<WeakReference<KClassImpl<*>>>(size + 1)
|
||||
// Don't use Arrays.copyOf because it works reflectively
|
||||
System.arraycopy(cached, 0, newArray, 0, size)
|
||||
val newKClass = KClassImpl(jClass)
|
||||
newArray[size] = WeakReference(newKClass)
|
||||
K_CLASS_CACHE = K_CLASS_CACHE.plus(name, newArray)
|
||||
return newKClass
|
||||
}
|
||||
|
||||
val newKClass = KClassImpl(jClass)
|
||||
K_CLASS_CACHE = K_CLASS_CACHE.plus(name, WeakReference(newKClass))
|
||||
return newKClass
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <T : Any> getOrCreateKotlinClass(jClass: Class<T>): KClassImpl<T> = K_CLASS_CACHE.get(jClass) as KClassImpl<T>
|
||||
|
||||
internal fun clearKClassCache() {
|
||||
K_CLASS_CACHE = HashPMap.empty()
|
||||
K_CLASS_CACHE.clear()
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* 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 kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* A simple persistent stack of non-null values.
|
||||
* <p/>
|
||||
* This implementation is thread-safe, although its iterators may not be.
|
||||
*/
|
||||
final class ConsPStack<E> implements Iterable<E> {
|
||||
private static final ConsPStack<Object> EMPTY = new ConsPStack<Object>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> ConsPStack<E> empty() {
|
||||
return (ConsPStack<E>) EMPTY;
|
||||
}
|
||||
|
||||
final E first;
|
||||
final ConsPStack<E> rest;
|
||||
private final int size;
|
||||
|
||||
private ConsPStack() { // EMPTY constructor
|
||||
size = 0;
|
||||
first = null;
|
||||
rest = null;
|
||||
}
|
||||
|
||||
private ConsPStack(E first, ConsPStack<E> rest) {
|
||||
this.first = first;
|
||||
this.rest = rest;
|
||||
this.size = 1 + rest.size;
|
||||
}
|
||||
|
||||
public E get(int index) {
|
||||
if (index < 0 || index > size) throw new IndexOutOfBoundsException();
|
||||
|
||||
try {
|
||||
return iterator(index).next();
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IndexOutOfBoundsException("Index: " + index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return iterator(0);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
private Iterator<E> iterator(int index) {
|
||||
return new Itr<E>(subList(index));
|
||||
}
|
||||
|
||||
private static class Itr<E> implements Iterator<E> {
|
||||
private ConsPStack<E> next;
|
||||
|
||||
public Itr(ConsPStack<E> first) {
|
||||
this.next = first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next.size > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
E e = next.first;
|
||||
next = next.rest;
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public ConsPStack<E> plus(E e) {
|
||||
return new ConsPStack<E>(e, this);
|
||||
}
|
||||
|
||||
private ConsPStack<E> minus(Object e) {
|
||||
if (size == 0) return this;
|
||||
if (first.equals(e)) // found it
|
||||
return rest; // don't recurse (only remove one)
|
||||
// otherwise keep looking:
|
||||
ConsPStack<E> newRest = rest.minus(e);
|
||||
if (newRest == rest) return this;
|
||||
return new ConsPStack<E>(first, newRest);
|
||||
}
|
||||
|
||||
public ConsPStack<E> minus(int i) {
|
||||
return minus(get(i));
|
||||
}
|
||||
|
||||
private ConsPStack<E> subList(int start) {
|
||||
if (start < 0 || start > size)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if (start == 0)
|
||||
return this;
|
||||
return rest.subList(start - 1);
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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 kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A persistent map from non-null keys to non-null values.
|
||||
* @suppress
|
||||
*/
|
||||
public final class HashPMap<K, V> {
|
||||
private static final HashPMap<Object, Object> EMPTY = new HashPMap<Object, Object>(IntTreePMap.<ConsPStack<MapEntry<Object, Object>>>empty(), 0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NotNull
|
||||
public static <K, V> HashPMap<K, V> empty() {
|
||||
return (HashPMap<K, V>) EMPTY;
|
||||
}
|
||||
|
||||
private final IntTreePMap<ConsPStack<MapEntry<K, V>>> intMap;
|
||||
private final int size;
|
||||
|
||||
private HashPMap(IntTreePMap<ConsPStack<MapEntry<K, V>>> intMap, int size) {
|
||||
this.intMap = intMap;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return keyIndexIn(getEntries(key.hashCode()), key) != -1;
|
||||
}
|
||||
|
||||
public V get(Object key) {
|
||||
ConsPStack<MapEntry<K, V>> entries = getEntries(key.hashCode());
|
||||
while (entries != null && entries.size() > 0) {
|
||||
MapEntry<K, V> entry = entries.first;
|
||||
if (entry.key.equals(key))
|
||||
return entry.value;
|
||||
entries = entries.rest;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HashPMap<K, V> plus(K key, V value) {
|
||||
ConsPStack<MapEntry<K, V>> entries = getEntries(key.hashCode());
|
||||
int size0 = entries.size();
|
||||
int i = keyIndexIn(entries, key);
|
||||
if (i != -1) entries = entries.minus(i);
|
||||
entries = entries.plus(new MapEntry<K, V>(key, value));
|
||||
return new HashPMap<K, V>(intMap.plus(key.hashCode(), entries), size - size0 + entries.size());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HashPMap<K, V> minus(Object key) {
|
||||
ConsPStack<MapEntry<K, V>> entries = getEntries(key.hashCode());
|
||||
int i = keyIndexIn(entries, key);
|
||||
if (i == -1) // key not in this
|
||||
return this;
|
||||
entries = entries.minus(i);
|
||||
if (entries.size() == 0) // get rid of the entire hash entry
|
||||
return new HashPMap<K, V>(intMap.minus(key.hashCode()), size - 1);
|
||||
// otherwise replace hash entry with new smaller one:
|
||||
return new HashPMap<K, V>(intMap.plus(key.hashCode(), entries), size - 1);
|
||||
}
|
||||
|
||||
private ConsPStack<MapEntry<K, V>> getEntries(int hash) {
|
||||
ConsPStack<MapEntry<K, V>> entries = intMap.get(hash);
|
||||
if (entries == null) return ConsPStack.empty();
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static <K, V> int keyIndexIn(ConsPStack<MapEntry<K, V>> entries, Object key) {
|
||||
int i = 0;
|
||||
while (entries != null && entries.size() > 0) {
|
||||
MapEntry<K, V> entry = entries.first;
|
||||
if (entry.key.equals(key))
|
||||
return i;
|
||||
entries = entries.rest;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
/*
|
||||
* 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 kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
/**
|
||||
* A non-public utility class for persistent balanced tree maps with integer keys.
|
||||
* <p/>
|
||||
* To allow for efficiently increasing all keys above a certain value or decreasing
|
||||
* all keys below a certain value, the keys values are stored relative to their parent.
|
||||
* This makes this map a good backing for fast insertion and removal of indices in a
|
||||
* vector.
|
||||
* <p/>
|
||||
* This implementation is thread-safe except for its iterators.
|
||||
* <p/>
|
||||
* Other than that, this tree is based on the Glasgow Haskell Compiler's Data.Map implementation,
|
||||
* which in turn is based on "size balanced binary trees" as described by:
|
||||
* <p/>
|
||||
* Stephen Adams, "Efficient sets: a balancing act",
|
||||
* Journal of Functional Programming 3(4):553-562, October 1993,
|
||||
* http://www.swiss.ai.mit.edu/~adams/BB/.
|
||||
* <p/>
|
||||
* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance",
|
||||
* SIAM journal of computing 2(1), March 1973.
|
||||
*
|
||||
* @author harold
|
||||
*/
|
||||
final class IntTree<V> {
|
||||
// marker value:
|
||||
static final IntTree<Object> EMPTYNODE = new IntTree<Object>();
|
||||
|
||||
// we use longs so relative keys can express all ints
|
||||
// (e.g. if this has key -10 and right has 'absolute' key MAXINT,
|
||||
// then its relative key is MAXINT+10 which overflows)
|
||||
// there might be some way to deal with this based on left-verse-right logic,
|
||||
// but that sounds like a mess.
|
||||
private final long key;
|
||||
private final V value; // null value means this is empty node
|
||||
private final IntTree<V> left, right;
|
||||
private final int size;
|
||||
|
||||
private IntTree() {
|
||||
size = 0;
|
||||
key = 0;
|
||||
value = null;
|
||||
left = null;
|
||||
right = null;
|
||||
}
|
||||
|
||||
private IntTree(long key, V value, IntTree<V> left, IntTree<V> right) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
size = 1 + left.size + right.size;
|
||||
}
|
||||
|
||||
private IntTree<V> withKey(long newKey) {
|
||||
if (size == 0 || newKey == key) return this;
|
||||
return new IntTree<V>(newKey, value, left, right);
|
||||
}
|
||||
|
||||
boolean containsKey(long key) {
|
||||
if (size == 0)
|
||||
return false;
|
||||
if (key < this.key)
|
||||
return left.containsKey(key - this.key);
|
||||
if (key > this.key)
|
||||
return right.containsKey(key - this.key);
|
||||
// otherwise key==this.key:
|
||||
return true;
|
||||
}
|
||||
|
||||
V get(long key) {
|
||||
if (size == 0)
|
||||
return null;
|
||||
if (key < this.key)
|
||||
return left.get(key - this.key);
|
||||
if (key > this.key)
|
||||
return right.get(key - this.key);
|
||||
// otherwise key==this.key:
|
||||
return value;
|
||||
}
|
||||
|
||||
IntTree<V> plus(long key, V value) {
|
||||
if (size == 0)
|
||||
return new IntTree<V>(key, value, this, this);
|
||||
if (key < this.key)
|
||||
return rebalanced(left.plus(key - this.key, value), right);
|
||||
if (key > this.key)
|
||||
return rebalanced(left, right.plus(key - this.key, value));
|
||||
// otherwise key==this.key, so we simply replace this, with no effect on balance:
|
||||
if (value == this.value)
|
||||
return this;
|
||||
return new IntTree<V>(key, value, left, right);
|
||||
}
|
||||
|
||||
IntTree<V> minus(long key) {
|
||||
if (size == 0)
|
||||
return this;
|
||||
if (key < this.key)
|
||||
return rebalanced(left.minus(key - this.key), right);
|
||||
if (key > this.key)
|
||||
return rebalanced(left, right.minus(key - this.key));
|
||||
|
||||
// otherwise key==this.key, so we are killing this node:
|
||||
|
||||
if (left.size == 0) // we can just become right node
|
||||
// make key 'absolute':
|
||||
return right.withKey(right.key + this.key);
|
||||
if (right.size == 0) // we can just become left node
|
||||
return left.withKey(left.key + this.key);
|
||||
|
||||
// otherwise replace this with the next key (i.e. the smallest key to the right):
|
||||
|
||||
// TODO have minNode() instead of minKey to avoid having to call get()
|
||||
// TODO get node from larger subtree, i.e. if left.size>right.size use left.maxNode()
|
||||
// TODO have faster minusMin() instead of just using minus()
|
||||
|
||||
long newKey = right.minKey() + this.key;
|
||||
//(right.minKey() is relative to this; adding this.key makes it 'absolute'
|
||||
// where 'absolute' really means relative to the parent of this)
|
||||
|
||||
V newValue = right.get(newKey - this.key);
|
||||
// now that we've got the new stuff, take it out of the right subtree:
|
||||
IntTree<V> newRight = right.minus(newKey - this.key);
|
||||
|
||||
// lastly, make the subtree keys relative to newKey (currently they are relative to this.key):
|
||||
newRight = newRight.withKey((newRight.key + this.key) - newKey);
|
||||
// left is definitely not empty:
|
||||
IntTree<V> newLeft = left.withKey((left.key + this.key) - newKey);
|
||||
|
||||
return rebalanced(newKey, newValue, newLeft, newRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes every key k>=key to k+delta.
|
||||
* <p/>
|
||||
* This method will create an _invalid_ tree if delta<0
|
||||
* and the distance between the smallest k>=key in this
|
||||
* and the largest j<key in this is |delta| or less.
|
||||
* <p/>
|
||||
* In other words, this method must not result in any change
|
||||
* in the order of the keys in this, since the tree structure is
|
||||
* not being changed at all.
|
||||
*/
|
||||
IntTree<V> changeKeysAbove(long key, int delta) {
|
||||
if (size == 0 || delta == 0)
|
||||
return this;
|
||||
|
||||
if (this.key >= key)
|
||||
// adding delta to this.key changes the keys of _all_ children of this,
|
||||
// so we now need to un-change the children of this smaller than key,
|
||||
// all of which are to the left. note that we still use the 'old' relative key...:
|
||||
return new IntTree<V>(this.key + delta, value, left.changeKeysBelow(key - this.key, -delta), right);
|
||||
|
||||
// otherwise, doesn't apply yet, look to the right:
|
||||
IntTree<V> newRight = right.changeKeysAbove(key - this.key, delta);
|
||||
if (newRight == right) return this;
|
||||
return new IntTree<V>(this.key, value, left, newRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes every key k<key to k+delta.
|
||||
* <p/>
|
||||
* This method will create an _invalid_ tree if delta>0
|
||||
* and the distance between the largest k<key in this
|
||||
* and the smallest j>=key in this is delta or less.
|
||||
* <p/>
|
||||
* In other words, this method must not result in any overlap or change
|
||||
* in the order of the keys in this, since the tree _structure_ is
|
||||
* not being changed at all.
|
||||
*/
|
||||
IntTree<V> changeKeysBelow(long key, int delta) {
|
||||
if (size == 0 || delta == 0)
|
||||
return this;
|
||||
|
||||
if (this.key < key)
|
||||
// adding delta to this.key changes the keys of _all_ children of this,
|
||||
// so we now need to un-change the children of this larger than key,
|
||||
// all of which are to the right. note that we still use the 'old' relative key...:
|
||||
return new IntTree<V>(this.key + delta, value, left, right.changeKeysAbove(key - this.key, -delta));
|
||||
|
||||
// otherwise, doesn't apply yet, look to the left:
|
||||
IntTree<V> newLeft = left.changeKeysBelow(key - this.key, delta);
|
||||
if (newLeft == left) return this;
|
||||
return new IntTree<V>(this.key, value, newLeft, right);
|
||||
}
|
||||
|
||||
// min key in this:
|
||||
private long minKey() {
|
||||
if (left.size == 0)
|
||||
return key;
|
||||
// make key 'absolute' (i.e. relative to the parent of this):
|
||||
return left.minKey() + this.key;
|
||||
}
|
||||
|
||||
private IntTree<V> rebalanced(IntTree<V> newLeft, IntTree<V> newRight) {
|
||||
if (newLeft == left && newRight == right)
|
||||
return this; // already balanced
|
||||
return rebalanced(key, value, newLeft, newRight);
|
||||
}
|
||||
|
||||
private static final int OMEGA = 5;
|
||||
private static final int ALPHA = 2;
|
||||
|
||||
// rebalance a tree that is off-balance by at most 1:
|
||||
private static <V> IntTree<V> rebalanced(long key, V value, IntTree<V> left, IntTree<V> right) {
|
||||
if (left.size + right.size > 1) {
|
||||
if (left.size >= OMEGA * right.size) { // rotate to the right
|
||||
IntTree<V> ll = left.left, lr = left.right;
|
||||
if (lr.size < ALPHA * ll.size) // single rotation
|
||||
return new IntTree<V>(left.key + key, left.value,
|
||||
ll,
|
||||
new IntTree<V>(-left.key, value,
|
||||
lr.withKey(lr.key + left.key),
|
||||
right));
|
||||
else { // double rotation:
|
||||
IntTree<V> lrl = lr.left, lrr = lr.right;
|
||||
return new IntTree<V>(lr.key + left.key + key, lr.value,
|
||||
new IntTree<V>(-lr.key, left.value,
|
||||
ll,
|
||||
lrl.withKey(lrl.key + lr.key)),
|
||||
new IntTree<V>(-left.key - lr.key, value,
|
||||
lrr.withKey(lrr.key + lr.key + left.key),
|
||||
right));
|
||||
}
|
||||
} else if (right.size >= OMEGA * left.size) { // rotate to the left
|
||||
IntTree<V> rl = right.left, rr = right.right;
|
||||
if (rl.size < ALPHA * rr.size) // single rotation
|
||||
return new IntTree<V>(right.key + key, right.value,
|
||||
new IntTree<V>(-right.key, value,
|
||||
left,
|
||||
rl.withKey(rl.key + right.key)),
|
||||
rr);
|
||||
else { // double rotation:
|
||||
IntTree<V> rll = rl.left, rlr = rl.right;
|
||||
return new IntTree<V>(rl.key + right.key + key, rl.value,
|
||||
new IntTree<V>(-right.key - rl.key, value,
|
||||
left,
|
||||
rll.withKey(rll.key + rl.key + right.key)),
|
||||
new IntTree<V>(-rl.key, right.value,
|
||||
rlr.withKey(rlr.key + rl.key),
|
||||
rr));
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise already balanced enough:
|
||||
return new IntTree<V>(key, value, left, right);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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 kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
/**
|
||||
* An efficient persistent map from integer keys to non-null values.
|
||||
*/
|
||||
final class IntTreePMap<V> {
|
||||
private static final IntTreePMap<Object> EMPTY = new IntTreePMap<Object>(IntTree.EMPTYNODE);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V> IntTreePMap<V> empty() {
|
||||
return (IntTreePMap<V>) EMPTY;
|
||||
}
|
||||
|
||||
private final IntTree<V> root;
|
||||
|
||||
private IntTreePMap(IntTree<V> root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
private IntTreePMap<V> withRoot(IntTree<V> root) {
|
||||
if (root == this.root) return this;
|
||||
return new IntTreePMap<V>(root);
|
||||
}
|
||||
|
||||
public V get(int key) {
|
||||
return root.get(key);
|
||||
}
|
||||
|
||||
public IntTreePMap<V> plus(int key, V value) {
|
||||
return withRoot(root.plus(key, value));
|
||||
}
|
||||
|
||||
public IntTreePMap<V> minus(int key) {
|
||||
return withRoot(root.minus(key));
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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 kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
final class MapEntry<K, V> implements java.io.Serializable {
|
||||
private static final long serialVersionUID = 7138329143949025153L;
|
||||
|
||||
public final K key;
|
||||
public final V value;
|
||||
|
||||
public MapEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof MapEntry)) return false;
|
||||
MapEntry<?, ?> e = (MapEntry<?, ?>) o;
|
||||
return (key == null ? e.key == null : key.equals(e.key)) &&
|
||||
(value == null ? e.value == null : value.equals(e.value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
}
|
||||
-10
@@ -14344,16 +14344,6 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class HashPMap {
|
||||
@Test
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
-10
@@ -14386,16 +14386,6 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class HashPMap {
|
||||
@Test
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
-13
@@ -12776,19 +12776,6 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class HashPMap extends AbstractIrCodegenBoxWasmTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -2,7 +2,7 @@ description = ''
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
JvmToolchain.configureJvmToolchain(project, JdkMajorVersion.JDK_1_6)
|
||||
JvmToolchain.configureJvmToolchain(project, JdkMajorVersion.JDK_1_8)
|
||||
|
||||
def includeJava9 = BuildPropertiesExtKt.getIncludeJava9(project.kotlinBuildProperties)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
configureJavaOnlyToolchain(JdkMajorVersion.JDK_1_6)
|
||||
configureJavaOnlyToolchain(JdkMajorVersion.JDK_1_8)
|
||||
|
||||
publish()
|
||||
|
||||
@@ -146,7 +146,7 @@ val proguard by task<CacheableProguardTask> {
|
||||
injars(mapOf("filter" to "!META-INF/**,!**/*.kotlin_builtins"), proguardAdditionalInJars)
|
||||
outjars(fileFrom(base.libsDirectory.asFile.get(), "${base.archivesName.get()}-$version-proguard.jar"))
|
||||
|
||||
javaLauncher.set(project.getToolchainLauncherFor(chooseJdk18ForJpsBuild(JdkMajorVersion.JDK_1_6)))
|
||||
javaLauncher.set(project.getToolchainLauncherFor(chooseJdk18ForJpsBuild(JdkMajorVersion.JDK_1_8)))
|
||||
libraryjars(mapOf("filter" to "!META-INF/versions/**"), proguardDeps)
|
||||
libraryjars(
|
||||
project.files(
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
project.updateJvmTarget("1.6")
|
||||
project.updateJvmTarget("1.8")
|
||||
|
||||
dependencies {
|
||||
api(kotlinStdlib())
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
project.updateJvmTarget("1.6")
|
||||
project.updateJvmTarget("1.8")
|
||||
|
||||
dependencies {
|
||||
implementation(kotlinStdlib())
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
project.updateJvmTarget("1.6")
|
||||
project.updateJvmTarget("1.8")
|
||||
|
||||
dependencies {
|
||||
api(kotlinStdlib())
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
project.configureJvmToolchain(JdkMajorVersion.JDK_1_6)
|
||||
project.configureJvmToolchain(JdkMajorVersion.JDK_1_8)
|
||||
|
||||
dependencies {
|
||||
api(project(":kotlin-script-runtime"))
|
||||
|
||||
@@ -35,10 +35,6 @@ the Kotlin IntelliJ IDEA plugin:
|
||||
- License: BSD ([license/third_party/asm_license.txt][asm])
|
||||
- Origin: Derived from ASM: a very small and fast Java bytecode manipulation framework, Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
|
||||
- Path: core/reflection.jvm/src/kotlin.reflect/jvm/internal/pcollections
|
||||
- License: MIT ([license/third_party/pcollections_LICENSE.txt][pcollections])
|
||||
- Origin: Derived from PCollections, A Persistent Java Collections Library (https://pcollections.org/)
|
||||
|
||||
- Path: eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt
|
||||
- License: BSD ([license/third_party/asm_license.txt][asm])
|
||||
- Origin: Derived from ASM: a very small and fast Java bytecode manipulation framework, Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
@@ -284,7 +280,6 @@ any distributions of the compiler, libraries or plugin:
|
||||
[gwt]: third_party/gwt_license.txt
|
||||
[jquery]: third_party/jquery_license.txt
|
||||
[lombok]: third_party/testdata/lombok_license.txt
|
||||
[pcollections]: third_party/pcollections_LICENSE.txt
|
||||
[qunit]: third_party/qunit_license.txt
|
||||
[rhino]: third_party/rhino_LICENSE.txt
|
||||
[rxjava]: third_party/testdata/rxjava_license.txt
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2008 Harold Cooper
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-12
@@ -15440,18 +15440,6 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/hashPMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@Tag("codegen")
|
||||
@UseExtTestCaseGroupProvider()
|
||||
public class HashPMap {
|
||||
@Test
|
||||
public void testAllFilesPresentInHashPMap() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/hashPMap"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/ieee754")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
Reference in New Issue
Block a user