Detect K/N library kind if library is a ZIP file (native-specific part)
Issue #KT-26730 Fixed
This commit is contained in:
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.ide.konan
|
|||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.vfs.LocalFileSystem
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiFileFactory
|
import com.intellij.psi.PsiFileFactory
|
||||||
import com.intellij.psi.SingleRootFileViewProvider
|
import com.intellij.psi.SingleRootFileViewProvider
|
||||||
@@ -25,6 +25,8 @@ import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
|
|||||||
import org.jetbrains.kotlin.konan.file.File
|
import org.jetbrains.kotlin.konan.file.File
|
||||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||||
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
||||||
|
import org.jetbrains.kotlin.konan.library.KonanLibrarySource.KonanLibraryDir
|
||||||
|
import org.jetbrains.kotlin.konan.library.KonanLibrarySource.KonanLibraryFile
|
||||||
import org.jetbrains.kotlin.konan.library.MetadataReader
|
import org.jetbrains.kotlin.konan.library.MetadataReader
|
||||||
import org.jetbrains.kotlin.konan.library.createKonanLibrary
|
import org.jetbrains.kotlin.konan.library.createKonanLibrary
|
||||||
import org.jetbrains.kotlin.konan.util.KonanFactories.DefaultPackageFragmentsFactory
|
import org.jetbrains.kotlin.konan.util.KonanFactories.DefaultPackageFragmentsFactory
|
||||||
@@ -98,16 +100,31 @@ private fun KonanLibrary.createPackageFragmentProvider(
|
|||||||
internal object CachingIdeMetadataReaderImpl : MetadataReader {
|
internal object CachingIdeMetadataReaderImpl : MetadataReader {
|
||||||
|
|
||||||
override fun loadSerializedModule(libraryLayout: KonanLibraryLayout): KonanProtoBuf.LinkDataLibrary =
|
override fun loadSerializedModule(libraryLayout: KonanLibraryLayout): KonanProtoBuf.LinkDataLibrary =
|
||||||
cache.getCachedModuleHeader(libraryLayout.moduleHeaderFile.virtualFile)
|
cache.getCachedModuleHeader(libraryLayout.getVirtualFile(libraryLayout.moduleHeaderFile))
|
||||||
|
|
||||||
override fun loadSerializedPackageFragment(
|
override fun loadSerializedPackageFragment(
|
||||||
libraryLayout: KonanLibraryLayout,
|
libraryLayout: KonanLibraryLayout,
|
||||||
packageFqName: String
|
packageFqName: String
|
||||||
): KonanProtoBuf.LinkDataPackageFragment =
|
): KonanProtoBuf.LinkDataPackageFragment =
|
||||||
cache.getCachedPackageFragment(libraryLayout.packageFragmentFile(packageFqName).virtualFile)
|
cache.getCachedPackageFragment(libraryLayout.getVirtualFile(libraryLayout.packageFragmentFile(packageFqName)))
|
||||||
|
|
||||||
private val File.virtualFile: VirtualFile
|
private fun KonanLibraryLayout.getVirtualFile(file: File): VirtualFile {
|
||||||
get() = LocalFileSystem.getInstance().refreshAndFindFileByPath(path) ?: error("File not found: $path")
|
val source = this.source
|
||||||
|
return when (source) {
|
||||||
|
is KonanLibraryFile -> asJarFileSystemFile(source.klibFile, file)
|
||||||
|
is KonanLibraryDir -> asLocalFile(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun asJarFileSystemFile(jarFile: File, localFile: File): VirtualFile {
|
||||||
|
val fullPath = jarFile.absolutePath + "!" + localFile.absolutePath
|
||||||
|
return StandardFileSystems.jar().findFileByPath(fullPath) ?: error("File not found: $fullPath")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun asLocalFile(localFile: File): VirtualFile {
|
||||||
|
val fullPath = localFile.absolutePath
|
||||||
|
return StandardFileSystems.local().findFileByPath(fullPath) ?: error("File not found: $fullPath")
|
||||||
|
}
|
||||||
|
|
||||||
private val cache
|
private val cache
|
||||||
get() = KotlinNativeLoadingMetadataCache.getInstance()
|
get() = KotlinNativeLoadingMetadataCache.getInstance()
|
||||||
|
|||||||
+20
-4
@@ -9,6 +9,8 @@ import com.intellij.openapi.application.ApplicationManager
|
|||||||
import com.intellij.openapi.components.ApplicationComponent
|
import com.intellij.openapi.components.ApplicationComponent
|
||||||
import com.intellij.openapi.vfs.*
|
import com.intellij.openapi.vfs.*
|
||||||
import com.intellij.util.containers.ContainerUtil.createConcurrentSoftValueMap
|
import com.intellij.util.containers.ContainerUtil.createConcurrentSoftValueMap
|
||||||
|
import org.jetbrains.kotlin.konan.library.KLIB_METADATA_FILE_EXTENSION
|
||||||
|
import org.jetbrains.kotlin.konan.library.KLIB_MODULE_METADATA_FILE_NAME
|
||||||
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
|
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
|
||||||
import org.jetbrains.kotlin.serialization.konan.parseModuleHeader
|
import org.jetbrains.kotlin.serialization.konan.parseModuleHeader
|
||||||
import org.jetbrains.kotlin.serialization.konan.parsePackageFragment
|
import org.jetbrains.kotlin.serialization.konan.parsePackageFragment
|
||||||
@@ -25,12 +27,12 @@ class KotlinNativeLoadingMetadataCache : ApplicationComponent {
|
|||||||
private val moduleHeaderCache = createConcurrentSoftValueMap<VirtualFile, KonanProtoBuf.LinkDataLibrary>()
|
private val moduleHeaderCache = createConcurrentSoftValueMap<VirtualFile, KonanProtoBuf.LinkDataLibrary>()
|
||||||
|
|
||||||
fun getCachedPackageFragment(virtualFile: VirtualFile): KonanProtoBuf.LinkDataPackageFragment =
|
fun getCachedPackageFragment(virtualFile: VirtualFile): KonanProtoBuf.LinkDataPackageFragment =
|
||||||
packageFragmentCache.computeIfAbsent(virtualFile) {
|
packageFragmentCache.computeIfAbsent(virtualFile.surePackageMetadataFile) {
|
||||||
parsePackageFragment(virtualFile.contentsToByteArray(false))
|
parsePackageFragment(virtualFile.contentsToByteArray(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCachedModuleHeader(virtualFile: VirtualFile): KonanProtoBuf.LinkDataLibrary =
|
fun getCachedModuleHeader(virtualFile: VirtualFile): KonanProtoBuf.LinkDataLibrary =
|
||||||
moduleHeaderCache.computeIfAbsent(virtualFile) {
|
moduleHeaderCache.computeIfAbsent(virtualFile.sureModuleHeaderFile) {
|
||||||
parseModuleHeader(virtualFile.contentsToByteArray(false))
|
parseModuleHeader(virtualFile.contentsToByteArray(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +47,21 @@ class KotlinNativeLoadingMetadataCache : ApplicationComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun invalidateCaches(virtualFile: VirtualFile) {
|
private fun invalidateCaches(virtualFile: VirtualFile) {
|
||||||
packageFragmentCache.remove(virtualFile)
|
when {
|
||||||
moduleHeaderCache.remove(virtualFile)
|
virtualFile.isPackageMetadataFile -> packageFragmentCache.remove(virtualFile)
|
||||||
|
virtualFile.isModuleHeaderFile -> moduleHeaderCache.remove(virtualFile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val VirtualFile.surePackageMetadataFile
|
||||||
|
get() = if (isPackageMetadataFile) this else error("Not a package metadata file: $this")
|
||||||
|
|
||||||
|
private val VirtualFile.isPackageMetadataFile
|
||||||
|
get() = extension == KLIB_METADATA_FILE_EXTENSION
|
||||||
|
|
||||||
|
private val VirtualFile.sureModuleHeaderFile
|
||||||
|
get() = if (isModuleHeaderFile) this else error("Not a module header file: $this")
|
||||||
|
|
||||||
|
private val VirtualFile.isModuleHeaderFile
|
||||||
|
get() = name == KLIB_MODULE_METADATA_FILE_NAME
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION"
|
|||||||
const val KLIB_METADATA_FILE_EXTENSION = "knm"
|
const val KLIB_METADATA_FILE_EXTENSION = "knm"
|
||||||
const val KLIB_METADATA_FILE_EXTENSION_WITH_DOT = ".$KLIB_METADATA_FILE_EXTENSION"
|
const val KLIB_METADATA_FILE_EXTENSION_WITH_DOT = ".$KLIB_METADATA_FILE_EXTENSION"
|
||||||
|
|
||||||
|
const val KLIB_MODULE_METADATA_FILE_NAME = "module"
|
||||||
|
|
||||||
const val KONAN_STDLIB_NAME = "stdlib"
|
const val KONAN_STDLIB_NAME = "stdlib"
|
||||||
|
|
||||||
val KONAN_COMMON_LIBS_PATH: Path
|
val KONAN_COMMON_LIBS_PATH: Path
|
||||||
|
|||||||
@@ -14,36 +14,35 @@ import org.jetbrains.kotlin.konan.target.KonanTarget
|
|||||||
interface KonanLibraryLayout {
|
interface KonanLibraryLayout {
|
||||||
|
|
||||||
val libraryName: String
|
val libraryName: String
|
||||||
|
|
||||||
val libDir: File
|
val libDir: File
|
||||||
val target: KonanTarget?
|
|
||||||
// This is a default implementation. Can't make it an assignment.
|
|
||||||
get() = null
|
|
||||||
|
|
||||||
val manifestFile
|
val source: KonanLibrarySource
|
||||||
get() = File(libDir, "manifest")
|
|
||||||
val resourcesDir
|
|
||||||
get() = File(libDir, "resources")
|
|
||||||
|
|
||||||
val targetsDir
|
// This is a default implementation. Can't make it an assignment.
|
||||||
get() = File(libDir, "targets")
|
val target: KonanTarget? get() = null
|
||||||
val targetDir
|
|
||||||
get() = File(targetsDir, target!!.visibleName)
|
|
||||||
|
|
||||||
val kotlinDir
|
val manifestFile get() = File(libDir, "manifest")
|
||||||
get() = File(targetDir, "kotlin")
|
val resourcesDir get() = File(libDir, "resources")
|
||||||
val nativeDir
|
|
||||||
get() = File(targetDir, "native")
|
|
||||||
val includedDir
|
|
||||||
get() = File(targetDir, "included")
|
|
||||||
|
|
||||||
val linkdataDir
|
val targetsDir get() = File(libDir, "targets")
|
||||||
get() = File(libDir, "linkdata")
|
val targetDir get() = File(targetsDir, target!!.visibleName)
|
||||||
val moduleHeaderFile
|
|
||||||
get() = File(linkdataDir, "module")
|
val kotlinDir get() = File(targetDir, "kotlin")
|
||||||
val dataFlowGraphFile
|
val nativeDir get() = File(targetDir, "native")
|
||||||
get() = File(linkdataDir, "module_data_flow_graph")
|
val includedDir get() = File(targetDir, "included")
|
||||||
|
|
||||||
|
val linkdataDir get() = File(libDir, "linkdata")
|
||||||
|
val moduleHeaderFile get() = File(linkdataDir, KLIB_MODULE_METADATA_FILE_NAME)
|
||||||
|
val dataFlowGraphFile get() = File(linkdataDir, "module_data_flow_graph")
|
||||||
|
|
||||||
fun packageFragmentFile(packageFqName: String) =
|
fun packageFragmentFile(packageFqName: String) =
|
||||||
File(linkdataDir, if (packageFqName == "") "root_package.knm" else "package_$packageFqName.knm")
|
File(
|
||||||
|
linkdataDir,
|
||||||
|
if (packageFqName == "") "root_package.$KLIB_METADATA_FILE_EXTENSION" else "package_$packageFqName.$KLIB_METADATA_FILE_EXTENSION"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class KonanLibrarySource {
|
||||||
|
object KonanLibraryDir : KonanLibrarySource()
|
||||||
|
class KonanLibraryFile(val klibFile: File) : KonanLibrarySource()
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-22
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.konan.file.*
|
|||||||
import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION
|
import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION
|
||||||
import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION_WITH_DOT
|
import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION_WITH_DOT
|
||||||
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
||||||
|
import org.jetbrains.kotlin.konan.library.KonanLibrarySource.KonanLibraryDir
|
||||||
|
import org.jetbrains.kotlin.konan.library.KonanLibrarySource.KonanLibraryFile
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import java.nio.file.FileSystem
|
import java.nio.file.FileSystem
|
||||||
|
|
||||||
@@ -17,24 +19,23 @@ interface KonanLibraryLayoutImpl : KonanLibraryLayout {
|
|||||||
fun <T> realFiles(action: (KonanLibraryLayout) -> T): T
|
fun <T> realFiles(action: (KonanLibraryLayout) -> T): T
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ZippedKonanLibraryLayout(val klibFile: File, override val target: KonanTarget?) : KonanLibraryLayoutImpl {
|
private class ZippedKonanLibraryLayout(
|
||||||
|
klibFile: File,
|
||||||
|
override val target: KonanTarget?
|
||||||
|
) : KonanLibraryLayoutImpl {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
zippedKonanLibraryChecks(klibFile)
|
zippedKonanLibraryChecks(klibFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val libraryName = klibFile.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT)
|
override val libraryName = klibFile.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT)
|
||||||
|
|
||||||
override val libDir: File = File("/")
|
override val libDir: File = File("/")
|
||||||
|
override val source = KonanLibraryFile(klibFile)
|
||||||
|
|
||||||
override fun <T> realFiles(action: (KonanLibraryLayout) -> T): T {
|
override fun <T> realFiles(action: (KonanLibraryLayout) -> T) = action(FileExtractor(this))
|
||||||
return action(FileExtractor(this))!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T> inPlace(action: (KonanLibraryLayout) -> T): T {
|
override fun <T> inPlace(action: (KonanLibraryLayout) -> T) = source.klibFile.withZipFileSystem { zipFileSystem ->
|
||||||
return klibFile.withZipFileSystem { zipFileSystem ->
|
action(DirectFromZip(this, zipFileSystem))
|
||||||
action(DirectFromZip(this, zipFileSystem))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,44 +47,51 @@ internal fun zippedKonanLibraryChecks(klibFile: File) {
|
|||||||
check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) { "Unexpected file extension: $extension" }
|
check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) { "Unexpected file extension: $extension" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UnzippedKonanLibraryLayout(override val libDir: File, override val target: KonanTarget?) : KonanLibraryLayoutImpl {
|
private class UnzippedKonanLibraryLayout(
|
||||||
override val libraryName = libDir.path
|
override val libDir: File,
|
||||||
|
override val target: KonanTarget?
|
||||||
|
) : KonanLibraryLayoutImpl {
|
||||||
|
|
||||||
override fun <T> inPlace(action: (KonanLibraryLayout) -> T): T = action(this)
|
override val libraryName = libDir.path
|
||||||
override fun <T> realFiles(action: (KonanLibraryLayout) -> T): T = inPlace(action)
|
override val source = KonanLibraryDir
|
||||||
|
|
||||||
|
override fun <T> inPlace(action: (KonanLibraryLayout) -> T) = action(this)
|
||||||
|
override fun <T> realFiles(action: (KonanLibraryLayout) -> T) = inPlace(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DirectFromZip(zippedLayout: ZippedKonanLibraryLayout, val zipFileSystem: FileSystem) : KonanLibraryLayout {
|
private class DirectFromZip(
|
||||||
|
zippedLayout: ZippedKonanLibraryLayout,
|
||||||
|
zipFileSystem: FileSystem
|
||||||
|
) : KonanLibraryLayout {
|
||||||
|
|
||||||
override val libraryName = zippedLayout.libraryName
|
override val libraryName = zippedLayout.libraryName
|
||||||
override val libDir = zipFileSystem.file(zippedLayout.libDir)
|
override val libDir = zipFileSystem.file(zippedLayout.libDir)
|
||||||
|
override val source = zippedLayout.source
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class automatically extracts pieces of the library on first access. Use it if you need
|
* This class automatically extracts pieces of the library on first access. Use it if you need
|
||||||
* to pass extracted files to an external tool. Otherwise, stick to [DirectFromZip].
|
* to pass extracted files to an external tool. Otherwise, stick to [DirectFromZip].
|
||||||
*/
|
*/
|
||||||
private class FileExtractor(val zippedLibraryLayout: ZippedKonanLibraryLayout) : KonanLibraryLayout by zippedLibraryLayout {
|
private class FileExtractor(
|
||||||
|
val zippedLayout: ZippedKonanLibraryLayout
|
||||||
|
) : KonanLibraryLayout by zippedLayout {
|
||||||
|
|
||||||
override val manifestFile: File by lazy { extract(super.manifestFile) }
|
override val manifestFile: File by lazy { extract(super.manifestFile) }
|
||||||
|
|
||||||
override val resourcesDir: File by lazy { extractDir(super.resourcesDir) }
|
override val resourcesDir: File by lazy { extractDir(super.resourcesDir) }
|
||||||
|
|
||||||
override val includedDir: File by lazy { extractDir(super.includedDir) }
|
override val includedDir: File by lazy { extractDir(super.includedDir) }
|
||||||
|
|
||||||
override val kotlinDir: File by lazy { extractDir(super.kotlinDir) }
|
override val kotlinDir: File by lazy { extractDir(super.kotlinDir) }
|
||||||
|
|
||||||
override val nativeDir: File by lazy { extractDir(super.nativeDir) }
|
override val nativeDir: File by lazy { extractDir(super.nativeDir) }
|
||||||
|
|
||||||
override val linkdataDir: File by lazy { extractDir(super.linkdataDir) }
|
override val linkdataDir: File by lazy { extractDir(super.linkdataDir) }
|
||||||
|
|
||||||
fun extract(file: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem ->
|
fun extract(file: File): File = zippedLayout.source.klibFile.withZipFileSystem { zipFileSystem ->
|
||||||
val temporary = createTempFile(file.name)
|
val temporary = createTempFile(file.name)
|
||||||
zipFileSystem.file(file).copyTo(temporary)
|
zipFileSystem.file(file).copyTo(temporary)
|
||||||
temporary.deleteOnExit()
|
temporary.deleteOnExit()
|
||||||
temporary
|
temporary
|
||||||
}
|
}
|
||||||
|
|
||||||
fun extractDir(directory: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem ->
|
fun extractDir(directory: File): File = zippedLayout.source.klibFile.withZipFileSystem { zipFileSystem ->
|
||||||
val temporary = createTempDir(directory.name)
|
val temporary = createTempDir(directory.name)
|
||||||
zipFileSystem.file(directory).recursiveCopyTo(temporary)
|
zipFileSystem.file(directory).recursiveCopyTo(temporary)
|
||||||
temporary.deleteOnExitRecursively()
|
temporary.deleteOnExitRecursively()
|
||||||
|
|||||||
Reference in New Issue
Block a user