[Analysis API] add checks that KtLifetimeOwners are not leaked to other analyse scope
This commit is contained in:
@@ -8,6 +8,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(kotlinxCollectionsImmutable())
|
||||||
compileOnly(project(":kotlin-reflect-api"))
|
compileOnly(project(":kotlin-reflect-api"))
|
||||||
|
|
||||||
compileOnly(project(":compiler:psi"))
|
compileOnly(project(":compiler:psi"))
|
||||||
|
|||||||
+2
-2
@@ -22,8 +22,8 @@ public abstract class KtLifetimeTokenFactory {
|
|||||||
public abstract val identifier: KClass<out KtLifetimeToken>
|
public abstract val identifier: KClass<out KtLifetimeToken>
|
||||||
public abstract fun create(project: Project): KtLifetimeToken
|
public abstract fun create(project: Project): KtLifetimeToken
|
||||||
|
|
||||||
public open fun beforeEnteringAnalysisContext() {}
|
public open fun beforeEnteringAnalysisContext(token: KtLifetimeToken) {}
|
||||||
public open fun afterLeavingAnalysisContext() {}
|
public open fun afterLeavingAnalysisContext(token: KtLifetimeToken) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+17
-9
@@ -9,10 +9,10 @@ package org.jetbrains.kotlin.analysis.api.lifetime
|
|||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker
|
import kotlinx.collections.immutable.PersistentList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import org.jetbrains.kotlin.analysis.api.*
|
import org.jetbrains.kotlin.analysis.api.*
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisAllowanceManager
|
import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker
|
||||||
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@OptIn(KtAllowAnalysisOnEdt::class)
|
@OptIn(KtAllowAnalysisOnEdt::class)
|
||||||
@@ -35,6 +35,7 @@ public class KtReadActionConfinementLifetimeToken(project: Project) : KtLifetime
|
|||||||
if (KtAnalysisAllowanceManager.resolveIsForbiddenInActionWithName.get() != null) return false
|
if (KtAnalysisAllowanceManager.resolveIsForbiddenInActionWithName.get() != null) return false
|
||||||
if (!application.isReadAccessAllowed) return false
|
if (!application.isReadAccessAllowed) return false
|
||||||
if (!KtReadActionConfinementLifetimeTokenFactory.isInsideAnalysisContext()) return false
|
if (!KtReadActionConfinementLifetimeTokenFactory.isInsideAnalysisContext()) return false
|
||||||
|
if (KtReadActionConfinementLifetimeTokenFactory.currentToken() != this) return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +47,8 @@ public class KtReadActionConfinementLifetimeToken(project: Project) : KtLifetime
|
|||||||
return "Resolve is forbidden in $actionName"
|
return "Resolve is forbidden in $actionName"
|
||||||
}
|
}
|
||||||
if (!KtReadActionConfinementLifetimeTokenFactory.isInsideAnalysisContext()) return "Called outside analyse method"
|
if (!KtReadActionConfinementLifetimeTokenFactory.isInsideAnalysisContext()) return "Called outside analyse method"
|
||||||
|
if (KtReadActionConfinementLifetimeTokenFactory.currentToken() != this) return "Using KtLifetimeOwner from previous analysis"
|
||||||
|
|
||||||
error("Getting inaccessibility reason for validity token when it is accessible")
|
error("Getting inaccessibility reason for validity token when it is accessible")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,19 +66,24 @@ public object KtReadActionConfinementLifetimeTokenFactory : KtLifetimeTokenFacto
|
|||||||
|
|
||||||
override fun create(project: Project): KtLifetimeToken = KtReadActionConfinementLifetimeToken(project)
|
override fun create(project: Project): KtLifetimeToken = KtReadActionConfinementLifetimeToken(project)
|
||||||
|
|
||||||
override fun beforeEnteringAnalysisContext() {
|
override fun beforeEnteringAnalysisContext(token: KtLifetimeToken) {
|
||||||
currentAnalysisContextEnteringCount.set(currentAnalysisContextEnteringCount.get() + 1)
|
lifetimeOwnersStack.set(lifetimeOwnersStack.get().add(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun afterLeavingAnalysisContext() {
|
override fun afterLeavingAnalysisContext(token: KtLifetimeToken) {
|
||||||
currentAnalysisContextEnteringCount.set(currentAnalysisContextEnteringCount.get() - 1)
|
val stack = lifetimeOwnersStack.get()
|
||||||
|
val last = stack.last()
|
||||||
|
check(last == token)
|
||||||
|
lifetimeOwnersStack.set(stack.removeAt(stack.lastIndex))
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentAnalysisContextEnteringCount = ThreadLocal.withInitial { 0 }
|
private val lifetimeOwnersStack = ThreadLocal.withInitial<PersistentList<KtLifetimeToken>> { persistentListOf() }
|
||||||
|
|
||||||
internal fun isInsideAnalysisContext() = currentAnalysisContextEnteringCount.get() > 0
|
internal fun isInsideAnalysisContext() = lifetimeOwnersStack.get().size > 0
|
||||||
|
|
||||||
|
internal fun currentToken() = lifetimeOwnersStack.get().last()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @see KtAnalysisSession
|
* @see KtAnalysisSession
|
||||||
|
|||||||
+2
-2
@@ -75,11 +75,11 @@ public abstract class KtAnalysisSessionProvider(public val project: Project) : D
|
|||||||
action: KtAnalysisSession.() -> R
|
action: KtAnalysisSession.() -> R
|
||||||
): R {
|
): R {
|
||||||
noWriteActionInAnalyseCallChecker.beforeEnteringAnalysisContext()
|
noWriteActionInAnalyseCallChecker.beforeEnteringAnalysisContext()
|
||||||
factory.beforeEnteringAnalysisContext()
|
factory.beforeEnteringAnalysisContext(analysisSession.token)
|
||||||
return try {
|
return try {
|
||||||
analysisSession.action()
|
analysisSession.action()
|
||||||
} finally {
|
} finally {
|
||||||
factory.afterLeavingAnalysisContext()
|
factory.afterLeavingAnalysisContext(analysisSession.token)
|
||||||
noWriteActionInAnalyseCallChecker.afterLeavingAnalysisContext()
|
noWriteActionInAnalyseCallChecker.afterLeavingAnalysisContext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user