FIR IDE: allow PCE between resolve phases, forbid in other cases

This commit is contained in:
Ilya Kirillov
2020-08-24 19:22:24 +03:00
parent 8363c711e6
commit bc3e98b116
6 changed files with 115 additions and 31 deletions
@@ -83,7 +83,7 @@ internal open class FirModuleResolveStateImpl(
}
override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
elementBuilder.lazyResolveDeclaration(declaration, fileCache, toPhase)
elementBuilder.lazyResolveDeclarationWithPCECheck(declaration, fileCache, toPhase)
return declaration
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.createTransformerBasedProcessorByPhase
import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeWithoutPCE
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
@@ -30,6 +31,6 @@ internal class FirPhaseRunner {
private fun runPhaseWithoutLock(firFile: FirFile, phase: FirResolvePhase, scopeSession: ScopeSession) {
val phaseProcessor = phase.createTransformerBasedProcessorByPhase(firFile.session, scopeSession)
phaseProcessor.processFile(firFile)
executeWithoutPCE { phaseProcessor.processFile(firFile) }
}
}
@@ -16,6 +16,6 @@ internal class IdePhasedFirFileResolver(
private val cache: ModuleFileCache
) : PhasedFirFileResolver() {
override fun resolveFile(firFile: FirFile, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
firFileBuilder.runResolve(firFile, cache, fromPhase, toPhase)
firFileBuilder.runResolveWithLock(firFile, cache, fromPhase, toPhase)
}
}
@@ -5,8 +5,6 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
import com.intellij.psi.util.parentOfType
import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirStatement
@@ -15,7 +13,6 @@ import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
@@ -28,7 +25,6 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.ThreadSafe
import org.jetbrains.kotlin.idea.fir.low.level.api.util.getContainingFile
import org.jetbrains.kotlin.idea.util.classIdIfNonLocal
import org.jetbrains.kotlin.psi.psiUtil.parents
/**
* Maps [KtElement] to [FirElement]
@@ -56,47 +52,33 @@ internal class FirElementBuilder(
else -> error("Unsupported: ${container.text}")
}
firFileBuilder.runCustomResolve(firFile, moduleFileCache) {
runLazyResolveWithoutLock(containerFir, firFile, firFile.session.firIdeProvider, toPhase)
}
runLazyResolveWithPCECheck(containerFir, moduleFileCache, firFile, firFile.session.firIdeProvider, toPhase)
return psiToFirCache.getFir(element, containerFir, firFile)
}
fun lazyResolveDeclaration(
fun lazyResolveDeclarationWithPCECheck(
declaration: FirDeclaration,
moduleFileCache: ModuleFileCache,
toPhase: FirResolvePhase
) {
if (declaration.resolvePhase < toPhase) {
val firFile = declaration.getContainingFile() ?: error("FirFile was not found for\n${declaration.render()}")
firFileBuilder.runCustomResolve(firFile, moduleFileCache) {
runLazyResolveWithoutLock(declaration, firFile, declaration.session.firProvider, toPhase)
}
runLazyResolveWithPCECheck(declaration, moduleFileCache, firFile, firFile.session.firIdeProvider, toPhase)
}
}
/**
* Should be invoked under lock
*/
private fun runLazyResolveWithoutLock(
private fun runLazyResolvePhase(
firDeclarationToResolve: FirDeclaration,
containerFirFile: FirFile,
provider: FirProvider,
toPhase: FirResolvePhase,
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null,
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>?,
) {
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
if (firDeclarationToResolve.resolvePhase < nonLazyPhase) {
firFileBuilder.runResolveWithoutLock(containerFirFile, fromPhase = firDeclarationToResolve.resolvePhase, toPhase = nonLazyPhase)
}
if (toPhase <= nonLazyPhase) return
val nonLocalDeclarationToResolve = firDeclarationToResolve.getNonLocalDeclarationToResolve(provider)
val designation = mutableListOf<FirDeclaration>(containerFirFile)
if (nonLocalDeclarationToResolve !is FirFile) {
val id = when (nonLocalDeclarationToResolve) {
is FirCallableDeclaration<*> -> {
nonLocalDeclarationToResolve.symbol.callableId.classId
@@ -127,6 +109,51 @@ internal class FirElementBuilder(
containerFirFile.transform<FirFile, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
}
/**
* Should be invoked under lock
*/
private fun runLazyResolveWithoutLock(
firDeclarationToResolve: FirDeclaration,
containerFirFile: FirFile,
provider: FirProvider,
toPhase: FirResolvePhase,
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null,
) {
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
if (firDeclarationToResolve.resolvePhase < nonLazyPhase) {
firFileBuilder.runResolveWithoutLockNoPCECheck(
containerFirFile,
fromPhase = firDeclarationToResolve.resolvePhase,
toPhase = nonLazyPhase
)
}
if (toPhase <= nonLazyPhase) return
runLazyResolvePhase(firDeclarationToResolve, containerFirFile, provider, toPhase, towerDataContextForStatement)
}
fun runLazyResolveWithPCECheck(
firDeclarationToResolve: FirDeclaration,
cache: ModuleFileCache,
containerFirFile: FirFile,
provider: FirProvider,
toPhase: FirResolvePhase,
towerDataContextForStatement: MutableMap<FirStatement, FirTowerDataContext>? = null,
) {
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
if (firDeclarationToResolve.resolvePhase < nonLazyPhase) {
firFileBuilder.runResolveWithPCECheck(
containerFirFile,
fromPhase = firDeclarationToResolve.resolvePhase,
toPhase = nonLazyPhase,
cache = cache,
)
}
if (toPhase <= nonLazyPhase) return
firFileBuilder.runCustomResolveWithPCECheck(containerFirFile, cache) {
runLazyResolvePhase(firDeclarationToResolve, containerFirFile, provider, toPhase, towerDataContextForStatement)
}
}
//TODO for completion only
fun runLazyResolveForCompletion(
firFunction: FirFunction<*>,
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.FirPhaseRunner
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.ThreadSafe
import org.jetbrains.kotlin.idea.fir.low.level.api.util.checkCanceled
import org.jetbrains.kotlin.idea.fir.low.level.api.util.lockWithPCECheck
/**
* Responsible for building [FirFile] by [KtFile]
@@ -43,7 +45,7 @@ internal class FirFileBuilder(
if (toPhase > FirResolvePhase.RAW_FIR) {
cache.firFileLockProvider.withLock(firFile) {
//add lock for implit type resolve phase & super type
runResolveWithoutLock(firFile, fromPhase = firFile.resolvePhase, toPhase = toPhase)
runResolveWithoutLockNoPCECheck(firFile, fromPhase = firFile.resolvePhase, toPhase = toPhase)
}
}
return firFile
@@ -52,16 +54,35 @@ internal class FirFileBuilder(
/**
* Runs [resolve] function (which is considered to do some resolve on [firFile]) under a lock for [firFile]
*/
inline fun <R> runCustomResolve(firFile: FirFile, cache: ModuleFileCache, resolve: () -> R): R =
inline fun <R> runCustomResolveUnderLock(firFile: FirFile, cache: ModuleFileCache, resolve: () -> R): R =
cache.firFileLockProvider.withLock(firFile) { resolve() }
fun runResolve(firFile: FirFile, cache: ModuleFileCache, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
inline fun <R : Any> runCustomResolveWithPCECheck(firFile: FirFile, cache: ModuleFileCache, resolve: () -> R): R {
val lock = cache.firFileLockProvider.getLockFor(firFile)
return lock.lockWithPCECheck(LOCKING_INTERVAL_MS) { resolve() }
}
fun runResolveWithLock(firFile: FirFile, cache: ModuleFileCache, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
cache.firFileLockProvider.withLock(firFile) {
runResolveWithoutLock(firFile, fromPhase, toPhase)
runResolveWithoutLockNoPCECheck(firFile, fromPhase, toPhase)
}
}
fun runResolveWithoutLock(firFile: FirFile, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
fun runResolveWithPCECheck(firFile: FirFile, cache: ModuleFileCache, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
val lock = cache.firFileLockProvider.getLockFor(firFile)
lock.lockWithPCECheck(LOCKING_INTERVAL_MS) {
val scopeSession = ScopeSession()
var currentPhase = fromPhase
while (currentPhase < toPhase) {
checkCanceled()
currentPhase = currentPhase.next
firPhaseRunner.runPhase(firFile, currentPhase, scopeSession)
}
}
}
fun runResolveWithoutLockNoPCECheck(firFile: FirFile, fromPhase: FirResolvePhase, toPhase: FirResolvePhase) {
assert(fromPhase <= toPhase) {
"Trying to resolve file ${firFile.name} from $fromPhase to $toPhase"
}
@@ -72,6 +93,10 @@ internal class FirFileBuilder(
firPhaseRunner.runPhase(firFile, currentPhase, scopeSession)
}
}
companion object {
private const val LOCKING_INTERVAL_MS = 500L
}
}
@@ -6,9 +6,12 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.util
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressManager
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
internal inline fun <T> executeOrReturnDefaultValueOnPCE(defaultValue: T, action: () -> T): T =
@@ -18,6 +21,34 @@ internal inline fun <T> executeOrReturnDefaultValueOnPCE(defaultValue: T, action
defaultValue
}
internal inline fun <T : Any> executeWithoutPCE(crossinline action: () -> T): T {
var result: T? = null
ProgressManager.getInstance().executeNonCancelableSection { result = action() }
return result!!
}
internal inline fun <T : Any> ReentrantLock.lockWithPCECheck(lockingIntervalMs: Long, action: () -> T): T {
var needToRun = true
var result: T? = null
while (needToRun) {
checkCanceled()
if (tryLock(lockingIntervalMs, TimeUnit.MILLISECONDS)) {
try {
needToRun = false
result = action()
} finally {
unlock()
}
}
}
return result!!
}
@Suppress("NOTHING_TO_INLINE")
internal inline fun checkCanceled() {
ProgressManager.checkCanceled()
}
internal val FirElement.isErrorElement
get() = this is FirDiagnosticHolder