[FIR] Introduce different implementations of ArrayMapOwner

- ComponentArrayOwner for services
- AttributeArrayOwner for attributes like userdata or type attributes
This commit is contained in:
Dmitriy Novozhilov
2020-05-07 13:39:05 +03:00
parent edd7d5b0b0
commit b348ae689a
8 changed files with 202 additions and 78 deletions
@@ -38,7 +38,7 @@ class FirRegisteredExtension<P : FirExtension>(
class FirExtensionsService( class FirExtensionsService(
val session: FirSession val session: FirSession
) : ComponentArrayOwner<FirExtension, FirRegisteredExtension<*>>(), FirSessionComponent { ) : ComponentArrayOwner<FirExtension, FirRegisteredExtension<*>>(), FirSessionComponent {
companion object : ComponentTypeRegistry<FirExtension, FirRegisteredExtension<*>>() { companion object : TypeRegistry<FirExtension, FirRegisteredExtension<*>>() {
inline fun <reified P : FirExtension, V : FirRegisteredExtension<P>> registeredExtensions(): ReadOnlyProperty<FirExtensionsService, ExtensionsAccessor<P>> { inline fun <reified P : FirExtension, V : FirRegisteredExtension<P>> registeredExtensions(): ReadOnlyProperty<FirExtensionsService, ExtensionsAccessor<P>> {
val accessor = generateAccessor<V, P>(P::class) val accessor = generateAccessor<V, P>(P::class)
return object : ReadOnlyProperty<FirExtensionsService, ExtensionsAccessor<P>> { return object : ReadOnlyProperty<FirExtensionsService, ExtensionsAccessor<P>> {
@@ -51,7 +51,7 @@ class FirExtensionsService(
private fun <K, V> createMultimap(): Multimap<K, V> = LinkedHashMultimap.create() private fun <K, V> createMultimap(): Multimap<K, V> = LinkedHashMultimap.create()
} }
override val typeRegistry: ComponentTypeRegistry<FirExtension, FirRegisteredExtension<*>> override val typeRegistry: TypeRegistry<FirExtension, FirRegisteredExtension<*>>
get() = Companion get() = Companion
fun <P : FirExtension> registerExtensions(extensionClass: KClass<P>, extensionFactories: List<FirExtension.Factory<P>>) { fun <P : FirExtension> registerExtensions(extensionClass: KClass<P>, extensionFactories: List<FirExtension.Factory<P>>) {
@@ -8,16 +8,16 @@ package org.jetbrains.kotlin.fir
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.fir.types.impl.* import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.fir.utils.ComponentArrayAccessor import org.jetbrains.kotlin.fir.utils.ArrayMapAccessor
import org.jetbrains.kotlin.fir.utils.ComponentArrayOwner import org.jetbrains.kotlin.fir.utils.ComponentArrayOwner
import org.jetbrains.kotlin.fir.utils.ComponentTypeRegistry import org.jetbrains.kotlin.fir.utils.TypeRegistry
import org.jetbrains.kotlin.utils.Jsr305State import org.jetbrains.kotlin.utils.Jsr305State
interface FirSessionComponent interface FirSessionComponent
abstract class FirSession(val sessionProvider: FirSessionProvider?) : ComponentArrayOwner<FirSessionComponent, FirSessionComponent>() { abstract class FirSession(val sessionProvider: FirSessionProvider?) : ComponentArrayOwner<FirSessionComponent, FirSessionComponent>() {
companion object : ComponentTypeRegistry<FirSessionComponent, FirSessionComponent>() { companion object : TypeRegistry<FirSessionComponent, FirSessionComponent>() {
inline fun <reified T : FirSessionComponent> sessionComponentAccessor(): ComponentArrayAccessor<FirSessionComponent, FirSessionComponent, T> { inline fun <reified T : FirSessionComponent> sessionComponentAccessor(): ArrayMapAccessor<FirSessionComponent, FirSessionComponent, T> {
return generateAccessor(T::class) return generateAccessor(T::class)
} }
} }
@@ -28,7 +28,7 @@ abstract class FirSession(val sessionProvider: FirSessionProvider?) : ComponentA
val builtinTypes: BuiltinTypes = BuiltinTypes() val builtinTypes: BuiltinTypes = BuiltinTypes()
final override val typeRegistry: ComponentTypeRegistry<FirSessionComponent, FirSessionComponent> = Companion final override val typeRegistry: TypeRegistry<FirSessionComponent, FirSessionComponent> = Companion
} }
interface FirSessionProvider { interface FirSessionProvider {
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2020 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 org.jetbrains.kotlin.fir.utils
sealed class ArrayMap<T : Any> {
abstract val size: Int
abstract operator fun set(index: Int, value: T)
abstract operator fun get(index: Int): T?
}
internal object EmptyArrayMap : ArrayMap<Nothing>() {
override val size: Int
get() = 0
override fun set(index: Int, value: Nothing) {
throw IllegalStateException()
}
override fun get(index: Int): Nothing? {
return null
}
}
internal class OneElementArrayMap<T : Any>(val value: T, val index: Int) : ArrayMap<T>() {
override val size: Int
get() = 1
override fun set(index: Int, value: T) {
throw IllegalStateException()
}
override fun get(index: Int): T? {
return if (index == this.index) value else null
}
}
internal class ArrayMapImpl<T : Any> : ArrayMap<T>() {
companion object {
private const val DEFAULT_SIZE = 20
private const val INCREASE_K = 2
}
override var size: Int = 0
private set
private var data = arrayOfNulls<Any>(DEFAULT_SIZE)
private fun ensureCapacity(index: Int) {
if (data.size < index) {
data = data.copyOf(data.size * INCREASE_K)
}
}
override operator fun set(index: Int, value: T) {
ensureCapacity(index)
data[index] = value
size++
}
override operator fun get(index: Int): T? {
@Suppress("UNCHECKED_CAST")
return data.getOrNull(index) as T?
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2020 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 org.jetbrains.kotlin.fir.utils
import java.util.*
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
abstract class AbstractArrayMapOwner<K : Any, V : Any> {
protected abstract val arrayMap: ArrayMap<V>
protected abstract val typeRegistry: TypeRegistry<K, V>
abstract class AbstractArrayMapAccessor<K : Any, V : Any, T : V>(
protected val key: KClass<out K>,
protected val id: Int
) {
protected fun extractValue(thisRef: AbstractArrayMapOwner<K, V>): T? {
@Suppress("UNCHECKED_CAST")
return thisRef.arrayMap[id] as T?
}
}
protected abstract fun registerComponent(tClass: KClass<out K>, value: V)
}
class ArrayMapAccessor<K : Any, V : Any, T : V>(
key: KClass<out K>,
id: Int
) : AbstractArrayMapOwner.AbstractArrayMapAccessor<K, V, T>(key, id), ReadOnlyProperty<AbstractArrayMapOwner<K, V>, V> {
override fun getValue(thisRef: AbstractArrayMapOwner<K, V>, property: KProperty<*>): T {
return extractValue(thisRef) ?: error("No '$key'($id) in array owner: $thisRef")
}
}
class NullableArrayMapAccessor<K : Any, V : Any, T : V>(
key: KClass<out K>,
id: Int
) : AbstractArrayMapOwner.AbstractArrayMapAccessor<K, V, T>(key, id), ReadOnlyProperty<AbstractArrayMapOwner<K, V>, V?> {
override fun getValue(thisRef: AbstractArrayMapOwner<K, V>, property: KProperty<*>): T? {
return extractValue(thisRef)
}
}
abstract class TypeRegistry<K : Any, V : Any> {
private val idPerType = HashMap<KClass<out K>, Int>()
fun <T : V, KK : K> generateAccessor(kClass: KClass<KK>): ArrayMapAccessor<K, V, T> {
return ArrayMapAccessor(kClass, getId(kClass))
}
fun <T : V, KK : K> generateNullableAccessor(kClass: KClass<KK>): NullableArrayMapAccessor<K, V, T> {
return NullableArrayMapAccessor(kClass, getId(kClass))
}
fun <T : K> getId(kClass: KClass<T>): Int {
return idPerType.getOrPut(kClass) { idPerType.size }
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2020 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 org.jetbrains.kotlin.fir.utils
import kotlin.reflect.KClass
/**
* [AttributeArrayOwner] based on different implementations of [ArrayMap] and switches them
* depending on array map fullness
* [AttributeArrayOwner] can be used in classes with many instances,
* like user data for Fir elements or attributes for cone types
*/
abstract class AttributeArrayOwner<K : Any, T : Any> : AbstractArrayMapOwner<K, T>() {
@Suppress("UNCHECKED_CAST")
final override var arrayMap: ArrayMap<T> = EmptyArrayMap as ArrayMap<T>
private set
final override fun registerComponent(tClass: KClass<out K>, value: T) {
val id = typeRegistry.getId(tClass)
when (arrayMap.size) {
0 -> {
arrayMap = OneElementArrayMap(value, id)
return
}
1 -> {
arrayMap = ArrayMapImpl<T>().apply {
val map = arrayMap as OneElementArrayMap<T>
this[map.index] = map.value
}
}
}
arrayMap[id] = value
}
}
@@ -1,71 +0,0 @@
/*
* Copyright 2010-2020 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 org.jetbrains.kotlin.fir.utils
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
abstract class ComponentArrayOwner<K : Any, V : Any> {
internal val componentArray: ComponentArray<V> = ComponentArray()
protected abstract val typeRegistry: ComponentTypeRegistry<K, V>
protected fun registerComponent(tClass: KClass<out K>, value: V) {
componentArray[(typeRegistry.getId(tClass))] = value
}
protected operator fun get(tClass: KClass<out K>): V {
return componentArray[typeRegistry.getId(tClass)]
}
}
abstract class ComponentTypeRegistry<K : Any, V : Any> {
private val idPerType = mutableMapOf<KClass<out K>, Int>()
fun <T : V, KK : K> generateAccessor(kClass: KClass<KK>): ComponentArrayAccessor<K, V, T> {
return ComponentArrayAccessor(kClass, getId(kClass))
}
fun <T : K> getId(kClass: KClass<T>): Int {
return idPerType.getOrPut(kClass) { idPerType.size }
}
}
class ComponentArrayAccessor<K : Any, V : Any, T : V>(
private val key: KClass<out K>,
private val id: Int
) : ReadOnlyProperty<ComponentArrayOwner<K, V>, V> {
override fun getValue(thisRef: ComponentArrayOwner<K, V>, property: KProperty<*>): T {
@Suppress("UNCHECKED_CAST")
return thisRef.componentArray[id] as T? ?: error("No '$key'($id) component in session: $thisRef")
}
}
class ComponentArray<T : Any> {
companion object {
private const val DEFAULT_SIZE = 20
private const val INCREASE_K = 2
}
private var data = arrayOfNulls<Any>(DEFAULT_SIZE)
private fun ensureCapacity(index: Int) {
if (data.size < index) {
data = data.copyOf(data.size * INCREASE_K)
}
}
operator fun set(index: Int, value: T) {
ensureCapacity(index)
data[index] = value
}
operator fun get(index: Int): T {
@Suppress("UNCHECKED_CAST")
return data.getOrNull(index) as T
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2020 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 org.jetbrains.kotlin.fir.utils
import kotlin.reflect.KClass
/**
* [ComponentArrayOwner] based on [ArrayMap] with flexible size and should be used for
* storing services in entities with limited number of instances, like FirSession
*/
abstract class ComponentArrayOwner<K : Any, V : Any> : AbstractArrayMapOwner<K, V>() {
final override val arrayMap: ArrayMap<V> =
ArrayMapImpl()
final override fun registerComponent(tClass: KClass<out K>, value: V) {
arrayMap[typeRegistry.getId(tClass)] = value
}
protected operator fun get(key: KClass<out K>): V {
val id = typeRegistry.getId(key)
return arrayMap[id] ?: error("No '$key'($id) component in array: $this")
}
}