From 472fe3ea66866b344144ebd08e00db6cd31f0250 Mon Sep 17 00:00:00 2001 From: vmishenev Date: Thu, 1 Dec 2022 18:24:35 +0200 Subject: [PATCH] Do not use `finalize` Refactor --- .../dokka/analysis/EnvironmentAndFacade.kt | 59 ++++++++++++++----- .../dokka/analysis/KotlinAnalysis.kt | 49 ++++++++++++--- plugins/base/api/base.api | 1 + .../src/main/kotlin/testRunner/baseTestApi.kt | 2 + plugins/base/src/main/kotlin/DokkaBase.kt | 7 ++- .../pages/samples/SamplesTransformer.kt | 54 ++++++----------- 6 files changed, 112 insertions(+), 60 deletions(-) diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt index 229adc8da2..92bde8e8a8 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt @@ -8,30 +8,48 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import java.io.Closeable +import java.io.File internal fun createEnvironmentAndFacade( logger: DokkaLogger, sourceSets: List, sourceSet: DokkaConfiguration.DokkaSourceSet, analysisConfiguration: DokkaAnalysisConfiguration -): EnvironmentAndFacade = - AnalysisEnvironment(DokkaMessageCollector(logger), sourceSet.analysisPlatform).run { - if (analysisPlatform == Platform.jvm) { - configureJdkClasspathRoots() - } +): EnvironmentAndFacade { + val parentSourceSets = sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets } + val classpath = sourceSet.classpath + parentSourceSets.flatMap { it.classpath } + val sources = sourceSet.sourceRoots + parentSourceSets.flatMap { it.sourceRoots } - val parentSourceSets = sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets } - addClasspath(sourceSet.classpath + parentSourceSets.flatMap { it.classpath }) + return createEnvironmentAndFacade( + logger = logger, + classpath = classpath, + sourceRoots = sources, + sourceSet = sourceSet, + analysisConfiguration = analysisConfiguration + ) +} - addSources(sourceSet.sourceRoots + parentSourceSets.flatMap { it.sourceRoots }) +internal fun createEnvironmentAndFacade( + logger: DokkaLogger, + classpath: List, + sourceRoots: Set, + sourceSet: DokkaConfiguration.DokkaSourceSet, + analysisConfiguration: DokkaAnalysisConfiguration +) = AnalysisEnvironment(DokkaMessageCollector(logger), sourceSet.analysisPlatform).run { + if (analysisPlatform == Platform.jvm) { + configureJdkClasspathRoots() + } + addClasspath(classpath) + addSources(sourceRoots) - loadLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion) + loadLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion) - val environment = createCoreEnvironment() + val environment = createCoreEnvironment() - val (facade, _) = createResolutionFacade(environment, analysisConfiguration.ignoreCommonBuiltIns) - EnvironmentAndFacade(environment, facade, this) - } + val (facade, _) = createResolutionFacade(environment, analysisConfiguration.ignoreCommonBuiltIns) + EnvironmentAndFacade(environment, facade, this) +} class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector { override fun clear() { @@ -51,10 +69,21 @@ class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector } // It is not data class due to ill-defined equals -class EnvironmentAndFacade(val environment: KotlinCoreEnvironment, val facade: DokkaResolutionFacade, private val analysisEnvironment: AnalysisEnvironment) { +class EnvironmentAndFacade( + environment: KotlinCoreEnvironment, + facade: DokkaResolutionFacade, + private val analysisEnvironment: AnalysisEnvironment +) : Closeable { + private var isClosed: Boolean = false + val environment: KotlinCoreEnvironment = environment + get() = field.takeUnless { isClosed } ?: throw IllegalStateException("AnalysisEnvironment is already closed") + val facade: DokkaResolutionFacade = facade + get() = field.takeUnless { isClosed } ?: throw IllegalStateException("AnalysisEnvironment is already closed") + operator fun component1() = environment operator fun component2() = facade - protected fun finalize() { + override fun close() { + isClosed = true analysisEnvironment.dispose() } } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt index a188e3f9e1..d9fffc4d5f 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt @@ -7,8 +7,13 @@ import org.jetbrains.dokka.DokkaSourceSetID import org.jetbrains.dokka.model.SourceSetDependent import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.DokkaLogger +import java.io.Closeable -fun KotlinAnalysis(sourceSets: List, logger: DokkaLogger, analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration()): KotlinAnalysis { +fun KotlinAnalysis( + sourceSets: List, + logger: DokkaLogger, + analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() +): KotlinAnalysis { val environments = sourceSets.associateWith { sourceSet -> createEnvironmentAndFacade( logger = logger, @@ -17,10 +22,30 @@ fun KotlinAnalysis(sourceSets: List, logger: DokkaLogger, analys analysisConfiguration = analysisConfiguration ) } - return KotlinAnalysisImpl(environments) } +fun KotlinAnalysisForSamples( + sourceSets: List, + logger: DokkaLogger, + defaultKotlinAnalysis: KotlinAnalysis, + analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() +): KotlinAnalysis { + val environments = sourceSets + .filter { it.samples.isNotEmpty() } + .associateWith { sourceSet -> + createEnvironmentAndFacade( + logger = logger, + classpath = sourceSet.classpath, + sourceRoots = sourceSet.samples, + sourceSet = sourceSet, + analysisConfiguration = analysisConfiguration + ) + } + + return KotlinAnalysisImpl(environments, defaultKotlinAnalysis) +} + class DokkaAnalysisConfiguration( /** * Only for common platform ignore BuiltIns for StdLib since it can cause a conflict @@ -34,20 +59,30 @@ class DokkaAnalysisConfiguration( ) fun KotlinAnalysis(context: DokkaContext): KotlinAnalysis = KotlinAnalysis(context.configuration.sourceSets, context.logger) -interface KotlinAnalysis : SourceSetDependent { +interface KotlinAnalysis : SourceSetDependent, Closeable { override fun get(key: DokkaSourceSet): EnvironmentAndFacade operator fun get(sourceSetID: DokkaSourceSetID): EnvironmentAndFacade } +/** + * It does not close [defaultKotlinAnalysis]. + */ internal class KotlinAnalysisImpl( - private val environments: SourceSetDependent + private val environments: SourceSetDependent, + private val defaultKotlinAnalysis: KotlinAnalysis? = null ) : KotlinAnalysis, SourceSetDependent by environments { override fun get(key: DokkaSourceSet): EnvironmentAndFacade { - return environments[key] ?: throw IllegalStateException("Missing EnvironmentAndFacade for sourceSet $key") + return environments[key] ?: defaultKotlinAnalysis?.get(key) + ?: throw IllegalStateException("Missing EnvironmentAndFacade for sourceSet $key") } override fun get(sourceSetID: DokkaSourceSetID): EnvironmentAndFacade { - return environments.entries.first { (sourceSet, _) -> sourceSet.sourceSetID == sourceSetID }.value + return environments.entries.firstOrNull { (sourceSet, _) -> sourceSet.sourceSetID == sourceSetID }?.value + ?: defaultKotlinAnalysis?.get(sourceSetID) + ?: throw IllegalStateException("Missing EnvironmentAndFacade for sourceSetID $sourceSetID") } -} + override fun close() { + environments.values.forEach(EnvironmentAndFacade::close) + } +} \ No newline at end of file diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index c299d547ed..4b87d62589 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -13,6 +13,7 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug public final fun getDefaultTabSortingStrategy ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDeprecatedDocumentableFilter ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDescriptorToDocumentableTranslator ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getDisposeKotlinAnalysisPostAction ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDocTagToContentConverter ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDocumentableMerger ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDocumentableToPageTranslator ()Lorg/jetbrains/dokka/plugability/Extension; diff --git a/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt b/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt index 25f6656e23..a11ddb84fe 100644 --- a/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt +++ b/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt @@ -53,6 +53,8 @@ class BaseDokkaTestGenerator( singleModuleGeneration.render(transformedPages) renderingStage(transformedPages, context) + singleModuleGeneration.runPostActions() + singleModuleGeneration.reportAfterRendering() } } diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 86160b4a7f..e839da0ccf 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -36,7 +36,8 @@ import org.jetbrains.dokka.base.translators.descriptors.ExternalClasslikesTransl import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider import org.jetbrains.dokka.base.utils.NoopIntellijLoggerFactory import org.jetbrains.dokka.plugability.DokkaPlugin -import org.jetbrains.dokka.plugability.configuration +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.renderers.PostAction import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -280,6 +281,10 @@ class DokkaBase : DokkaPlugin() { externalClasslikesTranslator providing ::DefaultDescriptorToDocumentableTranslator } + val disposeKotlinAnalysisPostAction by extending { + CoreExtensions.postActions with PostAction { this@DokkaBase.querySingle { kotlinAnalysis }.close() } + } + private companion object { init { // Suppress messages emitted by the IntelliJ logger since diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt index a1a55a8429..cc252b45f3 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt @@ -4,11 +4,7 @@ import com.intellij.psi.PsiElement import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.analysis.AnalysisEnvironment -import org.jetbrains.dokka.analysis.DokkaMessageCollector -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.EnvironmentAndFacade +import org.jetbrains.dokka.analysis.* import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.links.DRI @@ -38,42 +34,26 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { * Currently, all `ThreadLocal`s are in a compiler/IDE codebase. */ runBlocking(Dispatchers.Default) { - val analysis = setUpAnalysis(context) - - input.transformContentPagesTree { page -> - val samples = (page as? WithDocumentables)?.documentables?.flatMap { - it.documentation.entries.flatMap { entry -> - entry.value.children.filterIsInstance().map { entry.key to it } + val analysis = KotlinAnalysisForSamples(sourceSets = context.configuration.sourceSets, + logger = context.logger, + defaultKotlinAnalysis = context.plugin().querySingle { kotlinAnalysis }) + analysis.use { + input.transformContentPagesTree { page -> + val samples = (page as? WithDocumentables)?.documentables?.flatMap { + it.documentation.entries.flatMap { entry -> + entry.value.children.filterIsInstance().map { entry.key to it } + } } - } - - samples?.fold(page as ContentPage) { acc, (sampleSourceSet, sample) -> - acc.modified( - content = acc.content.addSample(page, sampleSourceSet, sample.name, analysis), - embeddedResources = acc.embeddedResources + KOTLIN_PLAYGROUND_SCRIPT - ) - } ?: page - } - } - private fun setUpAnalysis(context: DokkaContext) = context.configuration.sourceSets.associateWith { sourceSet -> - if (sourceSet.samples.isEmpty()) context.plugin() - .querySingle { kotlinAnalysis }[sourceSet] // from sourceSet.sourceRoots - else AnalysisEnvironment(DokkaMessageCollector(context.logger), sourceSet.analysisPlatform).run { - if (analysisPlatform == Platform.jvm) { - configureJdkClasspathRoots() + samples?.fold(page as ContentPage) { acc, (sampleSourceSet, sample) -> + acc.modified( + content = acc.content.addSample(page, sampleSourceSet, sample.name, it), + embeddedResources = acc.embeddedResources + KOTLIN_PLAYGROUND_SCRIPT + ) + } ?: page + } } - sourceSet.classpath.forEach(::addClasspath) - - addSources(sourceSet.samples.toList()) - - loadLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion) - - val environment = createCoreEnvironment() - val (facade, _) = createResolutionFacade(environment) - EnvironmentAndFacade(environment, facade, this) } - } private fun ContentNode.addSample( contentPage: ContentPage,