[AA] forbid analyze from write action
Analysis is not supposed to be called from write action. Such actions can lead to IDE freezes and incorrect behavior ^KT-60586 Fixed
This commit is contained in:
committed by
Space Team
parent
c624bba8cd
commit
88da053408
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -11,3 +11,6 @@ public annotation class KtAnalysisApiInternals
|
|||||||
@RequiresOptIn("Analysis should not be allowed to be ran from EDT thread, otherwise it may cause IDE freezes")
|
@RequiresOptIn("Analysis should not be allowed to be ran from EDT thread, otherwise it may cause IDE freezes")
|
||||||
public annotation class KtAllowAnalysisOnEdt
|
public annotation class KtAllowAnalysisOnEdt
|
||||||
|
|
||||||
|
@RequiresOptIn("Analysis should not be allowed to be ran from write action, otherwise it may cause IDE freezes and incorrect behavior in some cases")
|
||||||
|
public annotation class KtAllowAnalysisFromWriteAction
|
||||||
|
|
||||||
|
|||||||
+31
-4
@@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:OptIn(KtAnalysisApiInternals::class)
|
@file:OptIn(KtAnalysisApiInternals::class, KtAllowProhibitedAnalyzeFromWriteAction::class)
|
||||||
|
|
||||||
package org.jetbrains.kotlin.analysis.api.lifetime
|
package org.jetbrains.kotlin.analysis.api.lifetime
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ public class KtReadActionConfinementLifetimeToken(project: Project) : KtLifetime
|
|||||||
override fun isAccessible(): Boolean {
|
override fun isAccessible(): Boolean {
|
||||||
val application = ApplicationManager.getApplication()
|
val application = ApplicationManager.getApplication()
|
||||||
if (application.isDispatchThread && !allowOnEdt.get()) return false
|
if (application.isDispatchThread && !allowOnEdt.get()) return false
|
||||||
|
if (application.isWriteAccessAllowed && !allowFromWriteAction.get()) return false
|
||||||
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
|
||||||
@@ -41,6 +42,7 @@ public class KtReadActionConfinementLifetimeToken(project: Project) : KtLifetime
|
|||||||
override fun getInaccessibilityReason(): String {
|
override fun getInaccessibilityReason(): String {
|
||||||
val application = ApplicationManager.getApplication()
|
val application = ApplicationManager.getApplication()
|
||||||
if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread"
|
if (application.isDispatchThread && !allowOnEdt.get()) return "Called in EDT thread"
|
||||||
|
if (application.isWriteAccessAllowed && !allowFromWriteAction.get()) return "Called from write action"
|
||||||
if (!application.isReadAccessAllowed) return "Called outside read action"
|
if (!application.isReadAccessAllowed) return "Called outside read action"
|
||||||
KtAnalysisAllowanceManager.resolveIsForbiddenInActionWithName.get()?.let { actionName ->
|
KtAnalysisAllowanceManager.resolveIsForbiddenInActionWithName.get()?.let { actionName ->
|
||||||
return "Resolve is forbidden in $actionName"
|
return "Resolve is forbidden in $actionName"
|
||||||
@@ -55,6 +57,10 @@ public class KtReadActionConfinementLifetimeToken(project: Project) : KtLifetime
|
|||||||
public companion object {
|
public companion object {
|
||||||
@KtAnalysisApiInternals
|
@KtAnalysisApiInternals
|
||||||
public val allowOnEdt: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
public val allowOnEdt: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
||||||
|
|
||||||
|
@KtAnalysisApiInternals
|
||||||
|
@KtAllowProhibitedAnalyzeFromWriteAction
|
||||||
|
public val allowFromWriteAction: ThreadLocal<Boolean> = ThreadLocal.withInitial { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override val factory: KtLifetimeTokenFactory = KtReadActionConfinementLifetimeTokenFactory
|
public override val factory: KtLifetimeTokenFactory = KtReadActionConfinementLifetimeTokenFactory
|
||||||
@@ -78,13 +84,15 @@ public object KtReadActionConfinementLifetimeTokenFactory : KtLifetimeTokenFacto
|
|||||||
|
|
||||||
private val lifetimeOwnersStack = ThreadLocal.withInitial<PersistentList<KtLifetimeToken>> { persistentListOf() }
|
private val lifetimeOwnersStack = ThreadLocal.withInitial<PersistentList<KtLifetimeToken>> { persistentListOf() }
|
||||||
|
|
||||||
internal fun isInsideAnalysisContext() = lifetimeOwnersStack.get().size > 0
|
internal fun isInsideAnalysisContext() = lifetimeOwnersStack.get().isNotEmpty()
|
||||||
|
|
||||||
internal fun currentToken() = lifetimeOwnersStack.get().last()
|
internal fun currentToken() = lifetimeOwnersStack.get().last()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresOptIn("Analysis should be prohibited to be ran from write action, otherwise it may cause IDE freezes and incorrect behavior in some cases")
|
||||||
|
private annotation class KtAllowProhibitedAnalyzeFromWriteAction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @see KtAnalysisSession
|
* @see KtAnalysisSession
|
||||||
* @see KtReadActionConfinementLifetimeToken
|
* @see KtReadActionConfinementLifetimeToken
|
||||||
*/
|
*/
|
||||||
@@ -98,3 +106,22 @@ public inline fun <T> allowAnalysisOnEdt(action: () -> T): T {
|
|||||||
KtReadActionConfinementLifetimeToken.allowOnEdt.set(false)
|
KtReadActionConfinementLifetimeToken.allowOnEdt.set(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analysis is not supposed to be called from write action.
|
||||||
|
* Such actions can lead to IDE freezes and incorrect behavior in some cases.
|
||||||
|
*
|
||||||
|
* @see KtAnalysisSession
|
||||||
|
* @see KtReadActionConfinementLifetimeToken
|
||||||
|
*/
|
||||||
|
@KtAllowAnalysisFromWriteAction
|
||||||
|
@KtAllowProhibitedAnalyzeFromWriteAction
|
||||||
|
public inline fun <T> allowAnalysisFromWriteAction(action: () -> T): T {
|
||||||
|
if (KtReadActionConfinementLifetimeToken.allowFromWriteAction.get()) return action()
|
||||||
|
KtReadActionConfinementLifetimeToken.allowFromWriteAction.set(true)
|
||||||
|
try {
|
||||||
|
return action()
|
||||||
|
} finally {
|
||||||
|
KtReadActionConfinementLifetimeToken.allowFromWriteAction.set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ projectTest(jUnitMode = JUnitMode.JUnit5) {
|
|||||||
|
|
||||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||||
kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
|
kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
|
||||||
|
kotlinOptions.freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.analysis.api.lifetime.KtAllowProhibitedAnalyzeFromWriteAction"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -8,9 +8,11 @@ package org.jetbrains.kotlin.light.classes.symbol
|
|||||||
import com.intellij.psi.JavaPsiFacade
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiType
|
import com.intellij.psi.PsiType
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisFromWriteAction
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisOnEdt
|
import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisOnEdt
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.analyze
|
import org.jetbrains.kotlin.analysis.api.analyze
|
||||||
|
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisFromWriteAction
|
||||||
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
|
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
@@ -19,8 +21,10 @@ import org.jetbrains.kotlin.psi.KtElement
|
|||||||
internal fun PsiElement.nonExistentType(): PsiType =
|
internal fun PsiElement.nonExistentType(): PsiType =
|
||||||
JavaPsiFacade.getElementFactory(project).createTypeFromText(StandardNames.NON_EXISTENT_CLASS.asString(), this)
|
JavaPsiFacade.getElementFactory(project).createTypeFromText(StandardNames.NON_EXISTENT_CLASS.asString(), this)
|
||||||
|
|
||||||
@OptIn(KtAllowAnalysisOnEdt::class)
|
@OptIn(KtAllowAnalysisOnEdt::class, KtAllowAnalysisFromWriteAction::class)
|
||||||
private inline fun <E> allowLightClassesOnEdt(crossinline action: () -> E): E = allowAnalysisOnEdt(action)
|
private inline fun <E> allowLightClassesOnEdt(crossinline action: () -> E): E = allowAnalysisFromWriteAction {
|
||||||
|
allowAnalysisOnEdt(action)
|
||||||
|
}
|
||||||
|
|
||||||
internal inline fun <R> analyzeForLightClasses(context: KtElement, crossinline action: KtAnalysisSession.() -> R): R =
|
internal inline fun <R> analyzeForLightClasses(context: KtElement, crossinline action: KtAnalysisSession.() -> R): R =
|
||||||
allowLightClassesOnEdt {
|
allowLightClassesOnEdt {
|
||||||
|
|||||||
+21
-6
@@ -1,24 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* 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.cli.jvm.compiler
|
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||||
|
|
||||||
import com.intellij.DynamicBundle
|
import com.intellij.DynamicBundle
|
||||||
import com.intellij.codeInsight.ContainerProvider
|
import com.intellij.codeInsight.ContainerProvider
|
||||||
import com.intellij.codeInsight.folding.JavaCodeFoldingSettings
|
|
||||||
import com.intellij.codeInsight.folding.impl.JavaCodeFoldingSettingsBase
|
|
||||||
import com.intellij.codeInsight.runner.JavaMainMethodProvider
|
import com.intellij.codeInsight.runner.JavaMainMethodProvider
|
||||||
import com.intellij.core.JavaCoreApplicationEnvironment
|
import com.intellij.core.JavaCoreApplicationEnvironment
|
||||||
import com.intellij.ide.highlighter.JavaClassFileType
|
import com.intellij.ide.highlighter.JavaClassFileType
|
||||||
import com.intellij.lang.MetaLanguage
|
import com.intellij.lang.MetaLanguage
|
||||||
|
import com.intellij.mock.MockApplication
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||||
import com.intellij.psi.FileContextProvider
|
import com.intellij.psi.FileContextProvider
|
||||||
import com.intellij.psi.augment.PsiAugmentProvider
|
import com.intellij.psi.augment.PsiAugmentProvider
|
||||||
import com.intellij.psi.codeStyle.JavaFileCodeStyleFacade
|
|
||||||
import com.intellij.psi.codeStyle.JavaFileCodeStyleFacadeFactory
|
import com.intellij.psi.codeStyle.JavaFileCodeStyleFacadeFactory
|
||||||
import com.intellij.psi.impl.smartPointers.SmartPointerAnchorProvider
|
import com.intellij.psi.impl.smartPointers.SmartPointerAnchorProvider
|
||||||
import com.intellij.psi.meta.MetaDataContributor
|
import com.intellij.psi.meta.MetaDataContributor
|
||||||
@@ -28,8 +26,7 @@ import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
|||||||
|
|
||||||
class KotlinCoreApplicationEnvironment private constructor(
|
class KotlinCoreApplicationEnvironment private constructor(
|
||||||
parentDisposable: Disposable, unitTestMode: Boolean
|
parentDisposable: Disposable, unitTestMode: Boolean
|
||||||
) :
|
) : JavaCoreApplicationEnvironment(parentDisposable, unitTestMode) {
|
||||||
JavaCoreApplicationEnvironment(parentDisposable, unitTestMode) {
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
registerApplicationService(JavaFileCodeStyleFacadeFactory::class.java, DummyJavaFileCodeStyleFacadeFactory())
|
registerApplicationService(JavaFileCodeStyleFacadeFactory::class.java, DummyJavaFileCodeStyleFacadeFactory())
|
||||||
@@ -40,6 +37,24 @@ class KotlinCoreApplicationEnvironment private constructor(
|
|||||||
return CoreJrtFileSystem()
|
return CoreJrtFileSystem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createApplication(parentDisposable: Disposable): MockApplication {
|
||||||
|
val mock = super.createApplication(parentDisposable)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We can't use just [unitTestMode] from constructor, because at this moment
|
||||||
|
* the corresponding property is not yet initialized, so [unitTestMode] is effectively always false,
|
||||||
|
* because this function called from super class constructor
|
||||||
|
*/
|
||||||
|
return if (mock.isUnitTestMode) {
|
||||||
|
object : MockApplication(parentDisposable) {
|
||||||
|
override fun isUnitTestMode(): Boolean = true
|
||||||
|
override fun isWriteAccessAllowed(): Boolean = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var fastJarFileSystemField: FastJarFileSystem? = null
|
private var fastJarFileSystemField: FastJarFileSystem? = null
|
||||||
private var fastJarFileSystemFieldInitialized = false
|
private var fastJarFileSystemFieldInitialized = false
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user