[IDE KJS] Recognize and load correctly Kotlin/JS klibs in IDE
#KT-36276 Fixed
This commit is contained in:
+3
-13
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* 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:Suppress("Duplicates")
|
@file:Suppress("Duplicates")
|
||||||
@@ -186,17 +186,7 @@ class CompositeResolverForModuleFactory(
|
|||||||
): List<PackageFragmentProvider> {
|
): List<PackageFragmentProvider> {
|
||||||
if (moduleInfo !is LibraryModuleInfo || !moduleInfo.platform.isJs()) return emptyList()
|
if (moduleInfo !is LibraryModuleInfo || !moduleInfo.platform.isJs()) return emptyList()
|
||||||
|
|
||||||
return moduleInfo.getLibraryRoots()
|
return createPackageFragmentProvider(moduleInfo, container, moduleContext, moduleDescriptor)
|
||||||
.flatMap { KotlinJavascriptMetadataUtils.loadMetadata(it) }
|
|
||||||
.filter { it.version.isCompatible() }
|
|
||||||
.map { metadata ->
|
|
||||||
val (header, packageFragmentProtos) =
|
|
||||||
KotlinJavascriptSerializationUtil.readModuleAsProto(metadata.body, metadata.version)
|
|
||||||
createKotlinJavascriptPackageFragmentProvider(
|
|
||||||
moduleContext.storageManager, moduleDescriptor, header, packageFragmentProtos, metadata.version,
|
|
||||||
container.get(), LookupTracker.DO_NOTHING
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createContainerForCompositePlatform(
|
fun createContainerForCompositePlatform(
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.caches.resolve
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
|
||||||
|
import org.jetbrains.kotlin.konan.file.File
|
||||||
|
import org.jetbrains.kotlin.library.resolveSingleFileKlib
|
||||||
|
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||||
|
import org.jetbrains.kotlin.platform.js.JsPlatforms
|
||||||
|
|
||||||
|
internal class JsKlibLibraryInfo(
|
||||||
|
project: Project,
|
||||||
|
library: Library,
|
||||||
|
private val libraryRoot: String
|
||||||
|
) : LibraryInfo(project, library) {
|
||||||
|
|
||||||
|
val kotlinLibrary = resolveSingleFileKlib(File(libraryRoot))
|
||||||
|
|
||||||
|
override fun getLibraryRoots() = listOf(libraryRoot)
|
||||||
|
|
||||||
|
override val platform: TargetPlatform
|
||||||
|
get() = JsPlatforms.defaultJsPlatform
|
||||||
|
|
||||||
|
override fun toString() = "JsKlib" + super.toString()
|
||||||
|
}
|
||||||
+53
-2
@@ -5,24 +5,35 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.caches.resolve
|
package org.jetbrains.kotlin.caches.resolve
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.OrderRootType
|
||||||
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.util.PathUtil
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
|
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
|
||||||
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
|
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
|
||||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.context.ProjectContext
|
import org.jetbrains.kotlin.context.ProjectContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
|
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
|
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
|
||||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||||
|
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
|
||||||
|
import org.jetbrains.kotlin.library.KLIB_MANIFEST_FILE_NAME
|
||||||
|
import org.jetbrains.kotlin.library.KLIB_PROPERTY_BUILTINS_PLATFORM
|
||||||
|
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
|
||||||
|
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||||
import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind
|
import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind
|
||||||
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
||||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class JsPlatformKindResolution : IdePlatformKindResolution {
|
class JsPlatformKindResolution : IdePlatformKindResolution {
|
||||||
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean {
|
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean {
|
||||||
return virtualFile.extension == "js" || virtualFile.extension == "kjsm"
|
return virtualFile.extension == "js" || virtualFile.extension == "kjsm" || virtualFile.isJsKlibRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
override val libraryKind: PersistentLibraryKind<*>?
|
override val libraryKind: PersistentLibraryKind<*>?
|
||||||
@@ -43,4 +54,44 @@ class JsPlatformKindResolution : IdePlatformKindResolution {
|
|||||||
environment: TargetEnvironment,
|
environment: TargetEnvironment,
|
||||||
platform: TargetPlatform
|
platform: TargetPlatform
|
||||||
): ResolverForModuleFactory = JsResolverForModuleFactory(environment)
|
): ResolverForModuleFactory = JsResolverForModuleFactory(environment)
|
||||||
|
|
||||||
|
override fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> {
|
||||||
|
val klibFiles = library.getFiles(OrderRootType.CLASSES).filter { it.isJsKlibRoot }
|
||||||
|
|
||||||
|
return if (klibFiles.isNotEmpty()) {
|
||||||
|
klibFiles.mapNotNull {
|
||||||
|
// TODO report error?
|
||||||
|
val path = PathUtil.getLocalPath(it) ?: return@mapNotNull null
|
||||||
|
JsKlibLibraryInfo(project, library, path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.createLibraryInfo(project, library)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO unify the solution between Native and Common
|
||||||
|
private val VirtualFile.isJsKlibRoot: Boolean
|
||||||
|
get() {
|
||||||
|
// The virtual file for a library packed in a ZIP file will have path like "/some/path/to/the/file.klib!/",
|
||||||
|
// and therefore will be recognized by VFS as a directory (isDirectory == true).
|
||||||
|
// So, first, let's check the extension.
|
||||||
|
val extension = extension
|
||||||
|
if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false
|
||||||
|
|
||||||
|
fun checkComponent(componentFile: VirtualFile): Boolean {
|
||||||
|
val manifestFile = componentFile.findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false
|
||||||
|
|
||||||
|
val manifestProperties = try {
|
||||||
|
manifestFile.inputStream.use { Properties().apply { load(it) } }
|
||||||
|
} catch (_: IOException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifestProperties.getProperty(KLIB_PROPERTY_BUILTINS_PLATFORM) == BuiltInsPlatform.JS.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// run check for library root too
|
||||||
|
// this is necessary to recognize old style KLIBs that do not have components, and report tem to user appropriately
|
||||||
|
return checkComponent(this) || children?.any(::checkComponent) == true
|
||||||
|
}
|
||||||
|
|||||||
+59
-8
@@ -6,20 +6,29 @@
|
|||||||
package org.jetbrains.kotlin.caches.resolve
|
package org.jetbrains.kotlin.caches.resolve
|
||||||
|
|
||||||
import org.jetbrains.kotlin.analyzer.*
|
import org.jetbrains.kotlin.analyzer.*
|
||||||
|
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||||
import org.jetbrains.kotlin.container.get
|
import org.jetbrains.kotlin.container.get
|
||||||
import org.jetbrains.kotlin.context.ModuleContext
|
import org.jetbrains.kotlin.context.ModuleContext
|
||||||
|
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||||
import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve
|
import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve
|
||||||
|
import org.jetbrains.kotlin.idea.klib.getCompatibilityInfo
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
|
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
|
||||||
|
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
|
||||||
|
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
|
||||||
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||||
|
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
|
||||||
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
||||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||||
|
import org.jetbrains.kotlin.serialization.js.DynamicTypeDeserializer
|
||||||
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
|
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
|
||||||
import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider
|
import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider
|
||||||
|
import org.jetbrains.kotlin.serialization.konan.impl.KlibMetadataModuleDescriptorFactoryImpl
|
||||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||||
|
|
||||||
class JsResolverForModuleFactory(
|
class JsResolverForModuleFactory(
|
||||||
@@ -53,7 +62,54 @@ class JsResolverForModuleFactory(
|
|||||||
)
|
)
|
||||||
var packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
|
var packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
|
||||||
|
|
||||||
val libraryProviders = (moduleInfo as? LibraryModuleInfo)?.getLibraryRoots().orEmpty()
|
val libraryProviders = createPackageFragmentProvider(moduleInfo, container, moduleContext, moduleDescriptor)
|
||||||
|
|
||||||
|
if (libraryProviders.isNotEmpty()) {
|
||||||
|
packageFragmentProvider = CompositePackageFragmentProvider(listOf(packageFragmentProvider) + libraryProviders)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolverForModule(packageFragmentProvider, container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun <M : ModuleInfo> createPackageFragmentProvider(
|
||||||
|
moduleInfo: M,
|
||||||
|
container: StorageComponentContainer,
|
||||||
|
moduleContext: ModuleContext,
|
||||||
|
moduleDescriptor: ModuleDescriptorImpl
|
||||||
|
): List<PackageFragmentProvider> = when (moduleInfo) {
|
||||||
|
is JsKlibLibraryInfo -> {
|
||||||
|
val library = moduleInfo.kotlinLibrary
|
||||||
|
|
||||||
|
if (library.getCompatibilityInfo().isCompatible) {
|
||||||
|
val metadataFactories = KlibMetadataFactories({ DefaultBuiltIns.Instance }, DynamicTypeDeserializer)
|
||||||
|
|
||||||
|
val klibMetadataModuleDescriptorFactory = KlibMetadataModuleDescriptorFactoryImpl(
|
||||||
|
metadataFactories.DefaultDescriptorFactory,
|
||||||
|
metadataFactories.DefaultPackageFragmentsFactory,
|
||||||
|
metadataFactories.flexibleTypeDeserializer,
|
||||||
|
metadataFactories.platformDependentTypeTransformer
|
||||||
|
)
|
||||||
|
|
||||||
|
val packageFragmentNames = parseModuleHeader(library.moduleHeaderData).packageFragmentNameList
|
||||||
|
|
||||||
|
val klibBasedPackageFragmentProvider = klibMetadataModuleDescriptorFactory.createPackageFragmentProvider(
|
||||||
|
library,
|
||||||
|
packageAccessHandler = null,
|
||||||
|
packageFragmentNames = packageFragmentNames,
|
||||||
|
storageManager = moduleContext.storageManager,
|
||||||
|
moduleDescriptor = moduleDescriptor,
|
||||||
|
configuration = CompilerDeserializationConfiguration(container.get()),
|
||||||
|
compositePackageFragmentAddend = null
|
||||||
|
)
|
||||||
|
|
||||||
|
listOf(klibBasedPackageFragmentProvider)
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is LibraryModuleInfo -> {
|
||||||
|
moduleInfo.getLibraryRoots()
|
||||||
.flatMap { KotlinJavascriptMetadataUtils.loadMetadata(it) }
|
.flatMap { KotlinJavascriptMetadataUtils.loadMetadata(it) }
|
||||||
.filter { it.version.isCompatible() }
|
.filter { it.version.isCompatible() }
|
||||||
.map { metadata ->
|
.map { metadata ->
|
||||||
@@ -64,11 +120,6 @@ class JsResolverForModuleFactory(
|
|||||||
container.get(), LookupTracker.DO_NOTHING
|
container.get(), LookupTracker.DO_NOTHING
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libraryProviders.isNotEmpty()) {
|
|
||||||
packageFragmentProvider = CompositePackageFragmentProvider(listOf(packageFragmentProvider) + libraryProviders)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResolverForModule(packageFragmentProvider, container)
|
|
||||||
}
|
}
|
||||||
}
|
else -> emptyList()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user