diff options
Diffstat (limited to 'kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis')
-rw-r--r-- | kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt | 192 |
1 files changed, 161 insertions, 31 deletions
diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt index c39621f9..c8b324a0 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt @@ -3,8 +3,10 @@ package org.jetbrains.dokka.analysis import com.google.common.collect.ImmutableMap import com.intellij.core.CoreApplicationEnvironment import com.intellij.core.CoreModuleManager +import com.intellij.mock.MockApplication import com.intellij.mock.MockComponentManager import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager @@ -28,6 +30,7 @@ import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns +import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.caches.resolve.* import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.config.ContentRoot @@ -41,21 +44,34 @@ import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM import org.jetbrains.kotlin.cli.jvm.config.* import org.jetbrains.kotlin.cli.jvm.index.JavaRoot import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.container.get import org.jetbrains.kotlin.container.getService import org.jetbrains.kotlin.container.tryGetService +import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.context.withModule import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin import org.jetbrains.kotlin.extensions.ApplicationExtensionDescriptor +import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve +import org.jetbrains.kotlin.ide.konan.NativeKlibLibraryInfo import org.jetbrains.kotlin.ide.konan.NativePlatformKindResolution import org.jetbrains.kotlin.ide.konan.analyzer.NativeResolverForModuleFactory +import org.jetbrains.kotlin.idea.klib.KlibLoadingMetadataCache +import org.jetbrains.kotlin.idea.klib.createKlibPackageFragmentProvider +import org.jetbrains.kotlin.idea.klib.getCompatibilityInfo +import org.jetbrains.kotlin.idea.klib.safeRead import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices -import org.jetbrains.kotlin.library.impl.createKotlinLibrary +import org.jetbrains.kotlin.konan.util.KlibMetadataFactories +import org.jetbrains.kotlin.library.* +import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass import org.jetbrains.kotlin.name.Name @@ -71,10 +87,7 @@ import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms.unspecifiedJvmPlatform import org.jetbrains.kotlin.platform.konan.NativePlatforms import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.CompilerEnvironment -import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices +import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory @@ -82,13 +95,14 @@ import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.ResolveSession +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService.Companion.createDeclarationProviderFactory import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice import org.jetbrains.kotlin.util.slicedMap.WritableSlice import java.io.File +import org.jetbrains.kotlin.konan.file.File as KFile const val JAR_SEPARATOR = "!/" -const val KLIB_EXTENSION = "klib" /** * Kotlin as a service entry point @@ -146,6 +160,13 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl CustomJavadocTagProvider.EP_NAME, CustomJavadocTagProvider::class.java ) + // TODO: figure out why compilation fails with unresolved `CoreApplicationEnvironment.registerApplicationService(...)` + // call, fix it appropriately + with (ApplicationManager.getApplication() as MockApplication) { + if (getService(KlibLoadingMetadataCache::class.java) == null) + registerService(KlibLoadingMetadataCache::class.java, KlibLoadingMetadataCache()) + } + projectComponentManager.registerService( ProjectFileIndex::class.java, projectFileIndex @@ -202,7 +223,6 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl val projectContext = ProjectContext(environment.project, "Dokka") val sourceFiles = environment.getSourceFiles() - val targetPlatform = when (analysisPlatform) { Platform.js -> JsPlatforms.defaultJsPlatform Platform.common -> CommonPlatforms.defaultCommonPlatform @@ -210,8 +230,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl Platform.jvm -> JvmPlatforms.defaultJvmPlatform } - val nativeLibraries = classpath.filter { it.extension == KLIB_EXTENSION } - .map { createNativeLibraryModuleInfo(it) } + val nativeLibraries: Map</* absolute path */String, LibraryModuleInfo> = loadNativeLibraries() val library = object : LibraryModuleInfo { override val analyzerServices: PlatformDependentAnalyzerServices = @@ -219,8 +238,9 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl override val name: Name = Name.special("<library>") override val platform: TargetPlatform = targetPlatform override fun dependencies(): List<ModuleInfo> = listOf(this) - override fun getLibraryRoots(): Collection<String> = - classpath.filterNot { it.extension == KLIB_EXTENSION }.map { it.absolutePath } + override fun getLibraryRoots(): Collection<String> = classpath.mapNotNull { libraryFile -> + libraryFile.absolutePath.takeIf { it !in nativeLibraries } + } } val module = object : ModuleInfo { @@ -228,7 +248,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl analysisPlatform.analyzerServices() override val name: Name = Name.special("<module>") override val platform: TargetPlatform = targetPlatform - override fun dependencies(): List<ModuleInfo> = listOf(this, library) + nativeLibraries + override fun dependencies(): List<ModuleInfo> = listOf(this, library) + nativeLibraries.values } val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles) @@ -236,9 +256,13 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl when (it) { library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope)) module -> ModuleContent(it, emptyList(), GlobalSearchScope.allScope(environment.project)) - in nativeLibraries -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope)) - else -> throw IllegalArgumentException("Unexpected module info") - } + is DokkaNativeKlibLibraryInfo -> { + if (it.libraryRoot in nativeLibraries) + ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope)) + else null + } + else -> null + } ?: throw IllegalArgumentException("Unexpected module info") } var builtIns: JvmBuiltIns? = null @@ -303,18 +327,29 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl Platform.jvm -> JvmPlatformAnalyzerServices } - private fun createNativeLibraryModuleInfo(libraryFile: File): LibraryModuleInfo { - val kotlinLibrary = createKotlinLibrary(org.jetbrains.kotlin.konan.file.File(libraryFile.absolutePath), "",false) - return object : LibraryModuleInfo { - override val analyzerServices: PlatformDependentAnalyzerServices = - analysisPlatform.analyzerServices() - override val name: Name = Name.special("<klib>") - override val platform: TargetPlatform = NativePlatforms.unspecifiedNativePlatform - override fun dependencies(): List<ModuleInfo> = listOf(this) - override fun getLibraryRoots(): Collection<String> = listOf(libraryFile.absolutePath) - override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?> - get() = super.capabilities + (KlibModuleOrigin.CAPABILITY to kotlinLibrary) + private fun loadNativeLibraries(): Map<String, LibraryModuleInfo> { + if (analysisPlatform != Platform.native) return emptyMap() + + val dependencyResolver = DokkaNativeKlibLibraryDependencyResolver() + val analyzerServices = analysisPlatform.analyzerServices() + + val result = mutableMapOf<String, LibraryModuleInfo>() + + classpath.forEach { libraryFile -> + val libraryRoot = libraryFile.absolutePath + + val kotlinLibrary: KotlinLibrary = resolveSingleFileKlib( + libraryFile = KFile(libraryRoot), + strategy = ToolingSingleFileKlibResolveStrategy + ) + + if (kotlinLibrary.getCompatibilityInfo().isCompatible) { + // exists, is KLIB, has compatible format + result[libraryRoot] = DokkaNativeKlibLibraryInfo(kotlinLibrary, analyzerServices, dependencyResolver) + } } + + return result } private fun createCommonResolverForProject( @@ -404,11 +439,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl moduleInfo: ModuleInfo ): ResolverForModule { - return NativeResolverForModuleFactory( - PlatformAnalysisParameters.Empty, - CompilerEnvironment, - NativePlatforms.unspecifiedNativePlatform - ).createResolverForModule( + return DokkaNativeResolverForModuleFactory(CompilerEnvironment).createResolverForModule( descriptor as ModuleDescriptorImpl, projectContext.withModule(descriptor), modulesContent(moduleInfo), @@ -569,6 +600,105 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl } } +/** TODO: replace by [NativeKlibLibraryInfo] after fix of KT-40734 */ +internal class DokkaNativeKlibLibraryDependencyResolver { + private val cachedDependencies = mutableMapOf</* libraryName */String, DokkaNativeKlibLibraryInfo>() + + fun registerLibrary(libraryInfo: DokkaNativeKlibLibraryInfo) { + cachedDependencies[libraryInfo.kotlinLibrary.uniqueName] = libraryInfo + } + + fun resolveDependencies(libraryInfo: DokkaNativeKlibLibraryInfo): List<DokkaNativeKlibLibraryInfo> { + return libraryInfo.kotlinLibrary.unresolvedDependencies.mapNotNull { cachedDependencies[it.path] } + } +} + +/** TODO: replace by [NativeKlibLibraryInfo] after fix of KT-40734 */ +internal class DokkaNativeKlibLibraryInfo( + val kotlinLibrary: KotlinLibrary, + override val analyzerServices: PlatformDependentAnalyzerServices, + private val dependencyResolver: DokkaNativeKlibLibraryDependencyResolver +) : LibraryModuleInfo { + init { + dependencyResolver.registerLibrary(this) + } + + internal val libraryRoot: String + get() = kotlinLibrary.libraryFile.path + + override val name: Name by lazy { + val libraryName = kotlinLibrary.shortName ?: kotlinLibrary.uniqueName + Name.special("<$libraryName>") + } + + override val platform: TargetPlatform = NativePlatforms.unspecifiedNativePlatform + override fun dependencies(): List<ModuleInfo> = listOf(this) + dependencyResolver.resolveDependencies(this) + override fun getLibraryRoots(): Collection<String> = listOf(libraryRoot) + + override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?> + get() { + val capabilities = super.capabilities.toMutableMap() + capabilities += KlibModuleOrigin.CAPABILITY to DeserializedKlibModuleOrigin(kotlinLibrary) + capabilities += ImplicitIntegerCoercion.MODULE_CAPABILITY to kotlinLibrary.safeRead(false) { isInterop } + return capabilities + } +} + +/** TODO: replace by [NativeResolverForModuleFactory] after fix of KT-40734 */ +internal class DokkaNativeResolverForModuleFactory( + private val targetEnvironment: TargetEnvironment +) : ResolverForModuleFactory() { + companion object { + private val metadataFactories = KlibMetadataFactories(::KonanBuiltIns, NullFlexibleTypeDeserializer) + } + + override fun <M : ModuleInfo> createResolverForModule( + moduleDescriptor: ModuleDescriptorImpl, + moduleContext: ModuleContext, + moduleContent: ModuleContent<M>, + resolverForProject: ResolverForProject<M>, + languageVersionSettings: LanguageVersionSettings + ): ResolverForModule { + + val declarationProviderFactory = createDeclarationProviderFactory( + moduleContext.project, + moduleContext.storageManager, + moduleContent.syntheticFiles, + moduleContent.moduleContentScope, + moduleContent.moduleInfo + ) + + val container = createContainerForLazyResolve( + moduleContext, + declarationProviderFactory, + CodeAnalyzerInitializer.getInstance(moduleContext.project).createTrace(), + moduleDescriptor.platform!!, + NativePlatformAnalyzerServices, + targetEnvironment, + languageVersionSettings + ) + + var packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider + + val klibPackageFragmentProvider = (moduleContent.moduleInfo as? DokkaNativeKlibLibraryInfo) + ?.kotlinLibrary + ?.createKlibPackageFragmentProvider( + storageManager = moduleContext.storageManager, + metadataModuleDescriptorFactory = metadataFactories.DefaultDeserializedDescriptorFactory, + languageVersionSettings = languageVersionSettings, + moduleDescriptor = moduleDescriptor, + lookupTracker = LookupTracker.DO_NOTHING + ) + + if (klibPackageFragmentProvider != null) { + packageFragmentProvider = + CompositePackageFragmentProvider(listOf(packageFragmentProvider, klibPackageFragmentProvider)) + } + + return ResolverForModule(packageFragmentProvider, container) + } +} + fun contentRootFromPath(path: String): ContentRoot { val file = File(path) return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path, false) |