[Test/AA] Pass project disposable to getOrCreateApplicationEnvironment instead of application disposable

- The disposable passed to `getOrCreateApplicationEnvironment` should
  not actually be the application environment's disposable, which is
  created inside the function. Instead, it should be the project's
  disposable, which is used to track how many projects still rely on the
  shared application environment.
- This issue wasn't apparent before because there is no visible
  consequence when an application isn't disposed after all projects have
  been disposed (during tests). However, the solution for KT-63650
  relies on application environments being disposed after all projects
  are disposed, so that a new application environment with a different
  configuration can be created. (Only one shared application environment
  may be active at the same time.)

^KT-63650
This commit is contained in:
Marco Pennekamp
2023-11-28 19:23:33 +01:00
committed by Space Team
parent 85c4d6b67b
commit bba5447b12
10 changed files with 36 additions and 40 deletions
@@ -69,18 +69,17 @@ import java.nio.file.Paths
object StandaloneProjectFactory { object StandaloneProjectFactory {
fun createProjectEnvironment( fun createProjectEnvironment(
projectDisposable: Disposable, projectDisposable: Disposable,
applicationDisposable: Disposable,
applicationEnvironmentMode: KotlinCoreApplicationEnvironmentMode, applicationEnvironmentMode: KotlinCoreApplicationEnvironmentMode,
compilerConfiguration: CompilerConfiguration = CompilerConfiguration(), compilerConfiguration: CompilerConfiguration = CompilerConfiguration(),
classLoader: ClassLoader = MockProject::class.java.classLoader, classLoader: ClassLoader = MockProject::class.java.classLoader,
): KotlinCoreProjectEnvironment { ): KotlinCoreProjectEnvironment {
val applicationEnvironment = KotlinCoreEnvironment.getOrCreateApplicationEnvironment( val applicationEnvironment = KotlinCoreEnvironment.getOrCreateApplicationEnvironment(
applicationDisposable, projectDisposable = projectDisposable,
compilerConfiguration, compilerConfiguration,
applicationEnvironmentMode, applicationEnvironmentMode,
) )
registerApplicationExtensionPoints(applicationEnvironment, applicationDisposable) registerApplicationExtensionPoints(applicationEnvironment)
registerApplicationServices(applicationEnvironment) registerApplicationServices(applicationEnvironment)
@@ -138,10 +137,7 @@ object StandaloneProjectFactory {
} }
} }
private fun registerApplicationExtensionPoints( private fun registerApplicationExtensionPoints(applicationEnvironment: KotlinCoreApplicationEnvironment) {
applicationEnvironment: KotlinCoreApplicationEnvironment,
applicationDisposable: Disposable,
) {
val applicationArea = applicationEnvironment.application.extensionArea val applicationArea = applicationEnvironment.application.extensionArea
if (!applicationArea.hasExtensionPoint(AdditionalKDocResolutionProvider.EP_NAME)) { if (!applicationArea.hasExtensionPoint(AdditionalKDocResolutionProvider.EP_NAME)) {
@@ -162,7 +158,7 @@ object StandaloneProjectFactory {
ClassTypePointerFactory::class.java ClassTypePointerFactory::class.java
) )
applicationArea.getExtensionPoint(ClassTypePointerFactory.EP_NAME) applicationArea.getExtensionPoint(ClassTypePointerFactory.EP_NAME)
.registerExtension(PsiClassReferenceTypePointerFactory(), applicationDisposable) .registerExtension(PsiClassReferenceTypePointerFactory(), applicationEnvironment.application)
} }
} }
} }
@@ -8,6 +8,7 @@ dependencies {
testImplementation(projectTests(":compiler:tests-common")) testImplementation(projectTests(":compiler:tests-common"))
testImplementation(project(":analysis:analysis-api-standalone")) testImplementation(project(":analysis:analysis-api-standalone"))
testImplementation(projectTests(":analysis:analysis-api-standalone")) testImplementation(projectTests(":analysis:analysis-api-standalone"))
testImplementation(projectTests(":analysis:analysis-test-framework"))
testImplementation(projectTests(":native:native.tests")) testImplementation(projectTests(":native:native.tests"))
testImplementation(project(":native:kotlin-native-utils")) testImplementation(project(":native:kotlin-native-utils"))
testImplementation(project(":kotlin-native:utilities:basic-utils")) testImplementation(project(":kotlin-native:utilities:basic-utils"))
@@ -44,7 +44,6 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
public class StandaloneAnalysisAPISessionBuilder( public class StandaloneAnalysisAPISessionBuilder(
applicationDisposable: Disposable,
projectDisposable: Disposable, projectDisposable: Disposable,
unitTestMode: Boolean, unitTestMode: Boolean,
classLoader: ClassLoader = MockProject::class.java.classLoader classLoader: ClassLoader = MockProject::class.java.classLoader
@@ -61,7 +60,6 @@ public class StandaloneAnalysisAPISessionBuilder(
private val kotlinCoreProjectEnvironment: KotlinCoreProjectEnvironment = private val kotlinCoreProjectEnvironment: KotlinCoreProjectEnvironment =
StandaloneProjectFactory.createProjectEnvironment( StandaloneProjectFactory.createProjectEnvironment(
projectDisposable, projectDisposable,
applicationDisposable,
KotlinCoreApplicationEnvironmentMode.fromUnitTestModeFlag(unitTestMode), KotlinCoreApplicationEnvironmentMode.fromUnitTestModeFlag(unitTestMode),
classLoader = classLoader classLoader = classLoader
) )
@@ -229,7 +227,6 @@ public class StandaloneAnalysisAPISessionBuilder(
@OptIn(ExperimentalContracts::class) @OptIn(ExperimentalContracts::class)
public inline fun buildStandaloneAnalysisAPISession( public inline fun buildStandaloneAnalysisAPISession(
applicationDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.application"),
projectDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.project"), projectDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.project"),
unitTestMode: Boolean = false, unitTestMode: Boolean = false,
withPsiDeclarationFromBinaryModuleProvider: Boolean = false, withPsiDeclarationFromBinaryModuleProvider: Boolean = false,
@@ -240,7 +237,6 @@ public inline fun buildStandaloneAnalysisAPISession(
callsInPlace(init, InvocationKind.EXACTLY_ONCE) callsInPlace(init, InvocationKind.EXACTLY_ONCE)
} }
return StandaloneAnalysisAPISessionBuilder( return StandaloneAnalysisAPISessionBuilder(
applicationDisposable,
projectDisposable, projectDisposable,
unitTestMode, unitTestMode,
classLoader classLoader
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISe
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule
import org.jetbrains.kotlin.analysis.test.framework.TestWithDisposable
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
@@ -28,13 +29,13 @@ import org.junit.jupiter.api.Assertions
import java.nio.file.Path import java.nio.file.Path
@OptIn(KtAnalysisApiInternals::class) @OptIn(KtAnalysisApiInternals::class)
abstract class AbstractStandaloneSessionBuilderAgainstStdlibTest { abstract class AbstractStandaloneSessionBuilderAgainstStdlibTest : TestWithDisposable() {
protected fun doTestKotlinStdLibResolve( protected fun doTestKotlinStdLibResolve(
targetPlatform: TargetPlatform, platformStdlibPath: Path, targetPlatform: TargetPlatform, platformStdlibPath: Path,
additionalStdlibRoots: List<Path> = emptyList(), additionalStdlibRoots: List<Path> = emptyList(),
) { ) {
lateinit var sourceModule: KtSourceModule lateinit var sourceModule: KtSourceModule
val session = buildStandaloneAnalysisAPISession { val session = buildStandaloneAnalysisAPISession(disposable) {
registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider()) registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider())
buildKtModuleProvider { buildKtModuleProvider {
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule
import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule
import org.jetbrains.kotlin.analysis.test.framework.TestWithDisposable
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -35,11 +36,11 @@ import org.junit.jupiter.api.Test
import java.nio.file.Paths import java.nio.file.Paths
@OptIn(KtAnalysisApiInternals::class) @OptIn(KtAnalysisApiInternals::class)
class StandaloneSessionBuilderTest { class StandaloneSessionBuilderTest : TestWithDisposable() {
@Test @Test
fun testJdkSessionBuilder() { fun testJdkSessionBuilder() {
lateinit var sourceModule: KtSourceModule lateinit var sourceModule: KtSourceModule
val session = buildStandaloneAnalysisAPISession { val session = buildStandaloneAnalysisAPISession(disposable) {
registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider()) registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider())
buildKtModuleProvider { buildKtModuleProvider {
@@ -76,7 +77,7 @@ class StandaloneSessionBuilderTest {
@Test @Test
fun testResolveAgainstCommonKlib() { fun testResolveAgainstCommonKlib() {
lateinit var sourceModule: KtSourceModule lateinit var sourceModule: KtSourceModule
val session = buildStandaloneAnalysisAPISession { val session = buildStandaloneAnalysisAPISession(disposable) {
registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider()) registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider())
buildKtModuleProvider { buildKtModuleProvider {
@@ -108,7 +109,7 @@ class StandaloneSessionBuilderTest {
@Test @Test
fun testKotlinSourceModuleSessionBuilder() { fun testKotlinSourceModuleSessionBuilder() {
lateinit var sourceModule: KtSourceModule lateinit var sourceModule: KtSourceModule
val session = buildStandaloneAnalysisAPISession { val session = buildStandaloneAnalysisAPISession(disposable) {
registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider()) registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider())
buildKtModuleProvider { buildKtModuleProvider {
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtSta
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory
import org.jetbrains.kotlin.analysis.project.structure.KtBuiltinsModule import org.jetbrains.kotlin.analysis.project.structure.KtBuiltinsModule
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironmentMode import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironmentMode
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment
@@ -44,7 +43,6 @@ class AnalysisApiEnvironmentManagerImpl(
private val _projectEnvironment: KotlinCoreProjectEnvironment by lazy { private val _projectEnvironment: KotlinCoreProjectEnvironment by lazy {
StandaloneProjectFactory.createProjectEnvironment( StandaloneProjectFactory.createProjectEnvironment(
testRootDisposable, testRootDisposable,
testServices.applicationDisposableProvider.getApplicationRootDisposable(),
KotlinCoreApplicationEnvironmentMode.UnitTest, KotlinCoreApplicationEnvironmentMode.UnitTest,
) )
} }
@@ -443,11 +443,13 @@ class KotlinCoreEnvironment private constructor(
@JvmStatic @JvmStatic
fun createForProduction( fun createForProduction(
parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles projectDisposable: Disposable,
configuration: CompilerConfiguration,
configFiles: EnvironmentConfigFiles,
): KotlinCoreEnvironment { ): KotlinCoreEnvironment {
setupIdeaStandaloneExecution() setupIdeaStandaloneExecution()
val appEnv = getOrCreateApplicationEnvironmentForProduction(parentDisposable, configuration) val appEnv = getOrCreateApplicationEnvironmentForProduction(projectDisposable, configuration)
val projectEnv = ProjectEnvironment(parentDisposable, appEnv, configuration) val projectEnv = ProjectEnvironment(projectDisposable, appEnv, configuration)
val environment = KotlinCoreEnvironment(projectEnv, configuration, configFiles) val environment = KotlinCoreEnvironment(projectEnv, configuration, configFiles)
return environment return environment
@@ -480,11 +482,13 @@ class KotlinCoreEnvironment private constructor(
@TestOnly @TestOnly
@JvmStatic @JvmStatic
fun createForParallelTests( fun createForParallelTests(
parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles projectDisposable: Disposable,
initialConfiguration: CompilerConfiguration,
extensionConfigs: EnvironmentConfigFiles,
): KotlinCoreEnvironment { ): KotlinCoreEnvironment {
val configuration = initialConfiguration.copy() val configuration = initialConfiguration.copy()
val appEnv = getOrCreateApplicationEnvironmentForTests(parentDisposable, configuration) val appEnv = getOrCreateApplicationEnvironmentForTests(projectDisposable, configuration)
val projectEnv = ProjectEnvironment(parentDisposable, appEnv, configuration) val projectEnv = ProjectEnvironment(projectDisposable, appEnv, configuration)
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs) return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
} }
@@ -497,37 +501,38 @@ class KotlinCoreEnvironment private constructor(
} }
@TestOnly @TestOnly
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment { fun createProjectEnvironmentForTests(projectDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
val appEnv = createApplicationEnvironment( val appEnv = createApplicationEnvironment(
parentDisposable, projectDisposable,
configuration, configuration,
KotlinCoreApplicationEnvironmentMode.UnitTest, KotlinCoreApplicationEnvironmentMode.UnitTest,
) )
return ProjectEnvironment(parentDisposable, appEnv, configuration) return ProjectEnvironment(projectDisposable, appEnv, configuration)
} }
// used in the daemon for jar cache cleanup // used in the daemon for jar cache cleanup
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
fun getOrCreateApplicationEnvironmentForProduction( fun getOrCreateApplicationEnvironmentForProduction(
parentDisposable: Disposable, configuration: CompilerConfiguration projectDisposable: Disposable,
configuration: CompilerConfiguration,
): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment( ): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment(
parentDisposable, projectDisposable,
configuration, configuration,
KotlinCoreApplicationEnvironmentMode.Production, KotlinCoreApplicationEnvironmentMode.Production,
) )
fun getOrCreateApplicationEnvironmentForTests( fun getOrCreateApplicationEnvironmentForTests(
parentDisposable: Disposable, projectDisposable: Disposable,
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment( ): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment(
parentDisposable, projectDisposable,
configuration, configuration,
KotlinCoreApplicationEnvironmentMode.UnitTest, KotlinCoreApplicationEnvironmentMode.UnitTest,
) )
fun getOrCreateApplicationEnvironment( fun getOrCreateApplicationEnvironment(
parentDisposable: Disposable, projectDisposable: Disposable,
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
environmentMode: KotlinCoreApplicationEnvironmentMode, environmentMode: KotlinCoreApplicationEnvironmentMode,
): KotlinCoreApplicationEnvironment { ): KotlinCoreApplicationEnvironment {
@@ -553,7 +558,7 @@ class KotlinCoreEnvironment private constructor(
// Disposer uses identity of passed object to deduplicate registered disposables // Disposer uses identity of passed object to deduplicate registered disposables
// We should everytime pass new instance to avoid un-registering from previous one // We should everytime pass new instance to avoid un-registering from previous one
@Suppress("ObjectLiteralToLambda") @Suppress("ObjectLiteralToLambda")
Disposer.register(parentDisposable, object : Disposable { Disposer.register(projectDisposable, object : Disposable {
override fun dispose() { override fun dispose() {
synchronized(APPLICATION_LOCK) { synchronized(APPLICATION_LOCK) {
// Build-systems may run many instances of the compiler in parallel // Build-systems may run many instances of the compiler in parallel
@@ -93,7 +93,7 @@ open class CompilerConfigurationProviderImpl(
val platform = module.targetPlatform val platform = module.targetPlatform
val configFiles = platform.platformToEnvironmentConfigFiles() val configFiles = platform.platformToEnvironmentConfigFiles()
val applicationEnvironment = KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForTests( val applicationEnvironment = KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForTests(
testServices.applicationDisposableProvider.getApplicationRootDisposable(), testRootDisposable,
CompilerConfiguration() CompilerConfiguration()
) )
val configuration = createCompilerConfiguration(module, configurators) val configuration = createCompilerConfiguration(module, configurators)
@@ -860,7 +860,7 @@ private class ExtTestDataFileStructureFactory(parentDisposable: Disposable?) : T
configuration.put(CommonConfigurationKeys.MODULE_NAME, "native-blackbox-test-patching-module") configuration.put(CommonConfigurationKeys.MODULE_NAME, "native-blackbox-test-patching-module")
val environment = KotlinCoreEnvironment.createForProduction( val environment = KotlinCoreEnvironment.createForProduction(
parentDisposable = parentDisposable, projectDisposable = parentDisposable,
configuration = configuration, configuration = configuration,
configFiles = EnvironmentConfigFiles.METADATA_CONFIG_FILES configFiles = EnvironmentConfigFiles.METADATA_CONFIG_FILES
) )
@@ -48,7 +48,6 @@ internal class Kapt4Facade(private val testServices: TestServices) :
val (context, stubMap) = run( val (context, stubMap) = run(
configuration, configuration,
options, options,
testServices.applicationDisposableProvider.getApplicationRootDisposable(),
configurationProvider.testRootDisposable configurationProvider.testRootDisposable
) )
return Kapt4ContextBinaryArtifact(context, stubMap.values.filterNotNull()) return Kapt4ContextBinaryArtifact(context, stubMap.values.filterNotNull())
@@ -67,10 +66,9 @@ internal class Kapt4Facade(private val testServices: TestServices) :
private fun run( private fun run(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
options: KaptOptions, options: KaptOptions,
applicationDisposable: Disposable,
projectDisposable: Disposable, projectDisposable: Disposable,
): Pair<KaptContext, Map<KtLightClass, KaptStub?>> { ): Pair<KaptContext, Map<KtLightClass, KaptStub?>> {
val standaloneAnalysisAPISession = buildStandaloneAnalysisAPISession(applicationDisposable, projectDisposable) { val standaloneAnalysisAPISession = buildStandaloneAnalysisAPISession(projectDisposable) {
(project as MockProject).registerService( (project as MockProject).registerService(
KtLifetimeTokenProvider::class.java, KtLifetimeTokenProvider::class.java,
KtReadActionConfinementLifetimeTokenProvider::class.java KtReadActionConfinementLifetimeTokenProvider::class.java