[FIR IDE] LC Remove difficult caching from FirLightClassBase
This commit is contained in:
+9
-52
@@ -24,60 +24,20 @@ import org.jetbrains.kotlin.utils.SmartList
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
class KotlinClassInnerStuffCache(val myClass: PsiExtensibleClass, externalDependencies: List<Any>) {
|
class KotlinClassInnerStuffCache(
|
||||||
|
private val myClass: PsiExtensibleClass,
|
||||||
|
externalDependencies: List<Any>,
|
||||||
|
private val lazyCreator: LazyCreator,
|
||||||
|
) {
|
||||||
private val myTracker = SimpleModificationTracker()
|
private val myTracker = SimpleModificationTracker()
|
||||||
private val dependencies: List<Any> = externalDependencies + myTracker
|
private val dependencies: List<Any> = externalDependencies + myTracker
|
||||||
|
|
||||||
fun <T : Any> get(initializer: () -> T) = object : Lazy<T> {
|
abstract class LazyCreator {
|
||||||
private val lock = ReentrantLock()
|
abstract fun <T : Any> get(initializer: () -> T, dependencies: List<Any>): Lazy<T>
|
||||||
private val holder = lazyPub {
|
|
||||||
PsiCachedValueImpl(PsiManager.getInstance(myClass.project),
|
|
||||||
CachedValueProvider<T> {
|
|
||||||
val v = initializer()
|
|
||||||
CachedValueProvider.Result.create(v, dependencies)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeValue(): T = holder.value.value ?: error("holder has not null in initializer")
|
|
||||||
|
|
||||||
override val value: T
|
|
||||||
get() {
|
|
||||||
return if (holder.value.hasUpToDateValue()) {
|
|
||||||
computeValue()
|
|
||||||
} else {
|
|
||||||
// the idea behind this locking approach:
|
|
||||||
// Thread T1 starts to calculate value for A it acquires lock for A
|
|
||||||
//
|
|
||||||
// Assumption 1: Lets say A calculation requires another value e.g. B to be calculated
|
|
||||||
// Assumption 2: Thread T2 wants to calculate value for B
|
|
||||||
|
|
||||||
// to avoid dead-lock
|
|
||||||
// - we mark thread as doing calculation and acquire lock only once per thread
|
|
||||||
// as a trade-off to prevent dependent value could be calculated several time
|
|
||||||
// due to CAS (within putUserDataIfAbsent etc) the same instance of calculated value will be used
|
|
||||||
|
|
||||||
// TODO: NOTE: acquire lock for a several seconds to avoid dead-lock via resolve is a WORKAROUND
|
|
||||||
|
|
||||||
if (!initIsRunning.get() && lock.tryLock(5, TimeUnit.SECONDS)) {
|
|
||||||
try {
|
|
||||||
initIsRunning.set(true)
|
|
||||||
try {
|
|
||||||
computeValue()
|
|
||||||
} finally {
|
|
||||||
initIsRunning.set(false)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
computeValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isInitialized() = holder.isInitialized()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <T : Any> get(initializer: () -> T): Lazy<T> = lazyCreator.get(initializer, dependencies)
|
||||||
|
|
||||||
private val _getConstructors: Array<PsiMethod> by get { PsiImplUtil.getConstructors(myClass) }
|
private val _getConstructors: Array<PsiMethod> by get { PsiImplUtil.getConstructors(myClass) }
|
||||||
|
|
||||||
val constructors: Array<PsiMethod>
|
val constructors: Array<PsiMethod>
|
||||||
@@ -235,9 +195,6 @@ class KotlinClassInnerStuffCache(val myClass: PsiExtensibleClass, externalDepend
|
|||||||
private const val VALUES_METHOD = "values"
|
private const val VALUES_METHOD = "values"
|
||||||
private const val VALUE_OF_METHOD = "valueOf"
|
private const val VALUE_OF_METHOD = "valueOf"
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
private val initIsRunning: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
|
||||||
|
|
||||||
// Copy of PsiClassImplUtil.processDeclarationsInEnum for own cache class
|
// Copy of PsiClassImplUtil.processDeclarationsInEnum for own cache class
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun processDeclarationsInEnum(
|
fun processDeclarationsInEnum(
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
|
|||||||
abstract class KtLightClassBase protected constructor(manager: PsiManager)
|
abstract class KtLightClassBase protected constructor(manager: PsiManager)
|
||||||
: AbstractLightClass(manager, KotlinLanguage.INSTANCE), KtLightClass, PsiExtensibleClass {
|
: AbstractLightClass(manager, KotlinLanguage.INSTANCE), KtLightClass, PsiExtensibleClass {
|
||||||
protected open val myInnersCache = KotlinClassInnerStuffCache(
|
protected open val myInnersCache = KotlinClassInnerStuffCache(
|
||||||
this, listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker))
|
myClass = this,
|
||||||
|
externalDependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker),
|
||||||
|
lazyCreator = LightClassesLazyCreator(project)
|
||||||
|
)
|
||||||
|
|
||||||
override fun getDelegate() = clsDelegate
|
override fun getDelegate() = clsDelegate
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -72,8 +72,9 @@ abstract class KtLightClassForSourceDeclaration(
|
|||||||
StubBasedPsiElement<KotlinClassOrObjectStub<out KtClassOrObject>> {
|
StubBasedPsiElement<KotlinClassOrObjectStub<out KtClassOrObject>> {
|
||||||
|
|
||||||
override val myInnersCache: KotlinClassInnerStuffCache = KotlinClassInnerStuffCache(
|
override val myInnersCache: KotlinClassInnerStuffCache = KotlinClassInnerStuffCache(
|
||||||
this,
|
myClass = this,
|
||||||
classOrObject.getExternalDependencies()
|
externalDependencies = classOrObject.getExternalDependencies(),
|
||||||
|
lazyCreator = LightClassesLazyCreator(project)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val lightIdentifier = KtLightIdentifier(this, classOrObject)
|
private val lightIdentifier = KtLightIdentifier(this, classOrObject)
|
||||||
|
|||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.asJava.classes
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.impl.PsiCachedValueImpl
|
||||||
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
|
class LightClassesLazyCreator(private val project: Project) : KotlinClassInnerStuffCache.LazyCreator() {
|
||||||
|
override fun <T : Any> get(initializer: () -> T, dependencies: List<Any>) = object : Lazy<T> {
|
||||||
|
private val lock = ReentrantLock()
|
||||||
|
private val holder = lazyPub {
|
||||||
|
PsiCachedValueImpl(PsiManager.getInstance(project),
|
||||||
|
CachedValueProvider<T> {
|
||||||
|
val v = initializer()
|
||||||
|
CachedValueProvider.Result.create(v, dependencies)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeValue(): T = holder.value.value ?: error("holder has not null in initializer")
|
||||||
|
|
||||||
|
override val value: T
|
||||||
|
get() {
|
||||||
|
return if (holder.value.hasUpToDateValue()) {
|
||||||
|
computeValue()
|
||||||
|
} else {
|
||||||
|
// the idea behind this locking approach:
|
||||||
|
// Thread T1 starts to calculate value for A it acquires lock for A
|
||||||
|
//
|
||||||
|
// Assumption 1: Lets say A calculation requires another value e.g. B to be calculated
|
||||||
|
// Assumption 2: Thread T2 wants to calculate value for B
|
||||||
|
|
||||||
|
// to avoid dead-lock
|
||||||
|
// - we mark thread as doing calculation and acquire lock only once per thread
|
||||||
|
// as a trade-off to prevent dependent value could be calculated several time
|
||||||
|
// due to CAS (within putUserDataIfAbsent etc) the same instance of calculated value will be used
|
||||||
|
|
||||||
|
// TODO: NOTE: acquire lock for a several seconds to avoid dead-lock via resolve is a WORKAROUND
|
||||||
|
|
||||||
|
if (!initIsRunning.get() && lock.tryLock(5, TimeUnit.SECONDS)) {
|
||||||
|
try {
|
||||||
|
initIsRunning.set(true)
|
||||||
|
try {
|
||||||
|
computeValue()
|
||||||
|
} finally {
|
||||||
|
initIsRunning.set(false)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
computeValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isInitialized() = holder.isInitialized()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
private val initIsRunning: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -18,6 +18,7 @@ import org.jetbrains.annotations.NonNls
|
|||||||
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
||||||
import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache
|
import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.LightClassesLazyCreator
|
||||||
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
|
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
|
||||||
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
|
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
|
||||||
@@ -34,7 +35,8 @@ open class KtLightClassForDecompiledDeclaration(
|
|||||||
|
|
||||||
private val myInnersCache = KotlinClassInnerStuffCache(
|
private val myInnersCache = KotlinClassInnerStuffCache(
|
||||||
myClass = this,
|
myClass = this,
|
||||||
externalDependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker)
|
externalDependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker),
|
||||||
|
lazyCreator = LightClassesLazyCreator(project)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun getOwnMethods(): MutableList<PsiMethod> = _methods
|
override fun getOwnMethods(): MutableList<PsiMethod> = _methods
|
||||||
|
|||||||
+18
-3
@@ -18,8 +18,10 @@ package org.jetbrains.kotlin.idea.asJava
|
|||||||
|
|
||||||
import com.intellij.navigation.ItemPresentation
|
import com.intellij.navigation.ItemPresentation
|
||||||
import com.intellij.navigation.ItemPresentationProviders
|
import com.intellij.navigation.ItemPresentationProviders
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Pair
|
import com.intellij.openapi.util.Pair
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.impl.PsiCachedValueImpl
|
||||||
import com.intellij.psi.impl.PsiClassImplUtil
|
import com.intellij.psi.impl.PsiClassImplUtil
|
||||||
import com.intellij.psi.impl.PsiImplUtil
|
import com.intellij.psi.impl.PsiImplUtil
|
||||||
import com.intellij.psi.impl.PsiSuperMethodImplUtil
|
import com.intellij.psi.impl.PsiSuperMethodImplUtil
|
||||||
@@ -27,6 +29,7 @@ import com.intellij.psi.impl.light.LightElement
|
|||||||
import com.intellij.psi.impl.source.PsiExtensibleClass
|
import com.intellij.psi.impl.source.PsiExtensibleClass
|
||||||
import com.intellij.psi.javadoc.PsiDocComment
|
import com.intellij.psi.javadoc.PsiDocComment
|
||||||
import com.intellij.psi.scope.PsiScopeProcessor
|
import com.intellij.psi.scope.PsiScopeProcessor
|
||||||
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.PsiUtil
|
import com.intellij.psi.util.PsiUtil
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
|
||||||
@@ -34,7 +37,10 @@ import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache
|
|||||||
import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache.Companion.processDeclarationsInEnum
|
import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache.Companion.processDeclarationsInEnum
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
import org.jetbrains.kotlin.asJava.classes.cannotModify
|
import org.jetbrains.kotlin.asJava.classes.cannotModify
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import javax.swing.Icon
|
import javax.swing.Icon
|
||||||
|
|
||||||
abstract class FirLightClassBase protected constructor(manager: PsiManager) : LightElement(manager, KotlinLanguage.INSTANCE), PsiClass,
|
abstract class FirLightClassBase protected constructor(manager: PsiManager) : LightElement(manager, KotlinLanguage.INSTANCE), PsiClass,
|
||||||
@@ -43,9 +49,18 @@ abstract class FirLightClassBase protected constructor(manager: PsiManager) : Li
|
|||||||
override val clsDelegate: PsiClass
|
override val clsDelegate: PsiClass
|
||||||
get() = invalidAccess()
|
get() = invalidAccess()
|
||||||
|
|
||||||
protected open val myInnersCache = KotlinClassInnerStuffCache(
|
private class FirLightClassesLazyCreator(private val project: Project) : KotlinClassInnerStuffCache.LazyCreator() {
|
||||||
myClass = this,
|
override fun <T : Any> get(initializer: () -> T, dependencies: List<Any>): Lazy<T> = lazyPub {
|
||||||
externalDependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker)
|
PsiCachedValueImpl(PsiManager.getInstance(project)) {
|
||||||
|
CachedValueProvider.Result.create(initializer(), dependencies)
|
||||||
|
}.value ?: error("initializer cannot return null")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val myInnersCache = KotlinClassInnerStuffCache(
|
||||||
|
myClass = this@FirLightClassBase,
|
||||||
|
externalDependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker),
|
||||||
|
lazyCreator = FirLightClassesLazyCreator(project)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun getFields(): Array<PsiField> = myInnersCache.fields
|
override fun getFields(): Array<PsiField> = myInnersCache.fields
|
||||||
|
|||||||
Reference in New Issue
Block a user