From 71fac797eef62ffc9ea5a9414b0299d5b42b5201 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Fri, 16 Dec 2022 15:14:27 +0100 Subject: [PATCH] [kotlin-tooling-core] Implement generic Interner ^KT-55475 Verification Pending --- .../api/kotlin-tooling-core.api | 11 ++++ .../jetbrains/kotlin/tooling/core/Interner.kt | 56 +++++++++++++++++++ .../kotlin/tooling/core/InternerTest.kt | 40 +++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Interner.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/InternerTest.kt diff --git a/libraries/tools/kotlin-tooling-core/api/kotlin-tooling-core.api b/libraries/tools/kotlin-tooling-core/api/kotlin-tooling-core.api index e11fe3dfb36..1ee309f46e3 100644 --- a/libraries/tools/kotlin-tooling-core/api/kotlin-tooling-core.api +++ b/libraries/tools/kotlin-tooling-core/api/kotlin-tooling-core.api @@ -142,6 +142,17 @@ public abstract interface class org/jetbrains/kotlin/tooling/core/HasMutableExtr public abstract fun getExtras ()Lorg/jetbrains/kotlin/tooling/core/MutableExtras; } +public abstract interface class org/jetbrains/kotlin/tooling/core/Interner { + public abstract fun getOrPut (Ljava/lang/Object;)Ljava/lang/Object; +} + +public final class org/jetbrains/kotlin/tooling/core/InternerKt { + public static final fun Interner (Ljava/util/concurrent/locks/Lock;)Lorg/jetbrains/kotlin/tooling/core/Interner; + public static synthetic fun Interner$default (Ljava/util/concurrent/locks/Lock;ILjava/lang/Object;)Lorg/jetbrains/kotlin/tooling/core/Interner; + public static final fun WeakInterner (Ljava/util/concurrent/locks/Lock;)Lorg/jetbrains/kotlin/tooling/core/Interner; + public static synthetic fun WeakInterner$default (Ljava/util/concurrent/locks/Lock;ILjava/lang/Object;)Lorg/jetbrains/kotlin/tooling/core/Interner; +} + public final class org/jetbrains/kotlin/tooling/core/KotlinToolingVersion : java/io/Serializable, java/lang/Comparable { public fun (IIILjava/lang/String;)V public synthetic fun compareTo (Ljava/lang/Object;)I diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Interner.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Interner.kt new file mode 100644 index 00000000000..7de3b50645f --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Interner.kt @@ -0,0 +1,56 @@ +/* + * 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.tooling.core + +import org.jetbrains.kotlin.tooling.core.InternerImpl.Store.Strong +import org.jetbrains.kotlin.tooling.core.InternerImpl.Store.Weak +import java.lang.ref.WeakReference +import java.util.* +import java.util.concurrent.locks.Lock +import kotlin.concurrent.withLock + +fun WeakInterner(lock: Lock? = null): Interner = InternerImpl(Weak(), lock) + +fun Interner(lock: Lock? = null): Interner = InternerImpl(Strong(), lock) + +interface Interner { + fun getOrPut(value: T): T +} + +private class InternerImpl( + private val store: Store, + private val lock: Lock? = null +) : Interner { + + interface Store { + fun getOrPut(value: T): T + + class Weak : Store { + private val references = WeakHashMap>() + override fun getOrPut(value: T): T { + @Suppress("unchecked_cast") + return (references.getOrPut(value) { WeakReference(value) }.get() ?: run { + references[value] = WeakReference(value) + value + }) as T + } + } + + @Suppress("UNCHECKED_CAST") + class Strong : Store { + private val references = hashMapOf() + override fun getOrPut(value: T): T { + return references.getOrPut(value) { value } as T + } + } + } + + override fun getOrPut(value: T): T { + return lock?.withLock { store.getOrPut(value) } ?: store.getOrPut(value) + } +} diff --git a/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/InternerTest.kt b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/InternerTest.kt new file mode 100644 index 00000000000..70671a5be72 --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/InternerTest.kt @@ -0,0 +1,40 @@ +/* + * 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. + */ + +@file:Suppress("UNUSED_VALUE") + +package org.jetbrains.kotlin.tooling.core + +import org.junit.Test +import kotlin.test.assertSame + +class InternerTest { + + data class Sample(val value: Int) + + @Test + fun `test - weak interner`() { + doTest(WeakInterner()) + } + + @Test + fun `test - strong interner`() { + doTest(Interner()) + } + + private fun doTest(interner: Interner) { + val sample0A = Sample(0) + val sample0B = Sample(0) + val sample1A = Sample(1) + val sample1B = Sample(1) + + assertSame(sample0A, interner.getOrPut(sample0A)) + assertSame(sample0A, interner.getOrPut(sample0B)) + assertSame(sample1A, interner.getOrPut(sample1A)) + assertSame(sample1A, interner.getOrPut(sample1B)) + assertSame(sample0A, interner.getOrPut(sample0A)) + assertSame(sample0A, interner.getOrPut(sample0B)) + } +} \ No newline at end of file