From 324ec7aff85d5d5545bcedeccdca29b6f6432d7b Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Tue, 11 Sep 2018 10:48:36 +0300 Subject: [PATCH] Detect K/N library kind if library is a ZIP file (native-specific part) Issue #KT-26730 Fixed --- .../ide/konan/KotlinNativePluginUtil.kt | 27 ++++++++-- .../KotlinNativeLoadingMetadataCache.kt | 24 +++++++-- .../konan/library/KonanLibraryConstants.kt | 2 + .../konan/library/KonanLibraryLayout.kt | 49 +++++++++-------- .../library/impl/KonanLibraryLayoutImpl.kt | 52 +++++++++++-------- 5 files changed, 98 insertions(+), 56 deletions(-) diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt index 7d20657aee2..78fa41a5aba 100644 --- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.ide.konan import com.intellij.openapi.diagnostic.Logger 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.psi.PsiFileFactory 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.library.KonanLibrary 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.createKonanLibrary import org.jetbrains.kotlin.konan.util.KonanFactories.DefaultPackageFragmentsFactory @@ -98,16 +100,31 @@ private fun KonanLibrary.createPackageFragmentProvider( internal object CachingIdeMetadataReaderImpl : MetadataReader { override fun loadSerializedModule(libraryLayout: KonanLibraryLayout): KonanProtoBuf.LinkDataLibrary = - cache.getCachedModuleHeader(libraryLayout.moduleHeaderFile.virtualFile) + cache.getCachedModuleHeader(libraryLayout.getVirtualFile(libraryLayout.moduleHeaderFile)) override fun loadSerializedPackageFragment( libraryLayout: KonanLibraryLayout, packageFqName: String ): KonanProtoBuf.LinkDataPackageFragment = - cache.getCachedPackageFragment(libraryLayout.packageFragmentFile(packageFqName).virtualFile) + cache.getCachedPackageFragment(libraryLayout.getVirtualFile(libraryLayout.packageFragmentFile(packageFqName))) - private val File.virtualFile: VirtualFile - get() = LocalFileSystem.getInstance().refreshAndFindFileByPath(path) ?: error("File not found: $path") + private fun KonanLibraryLayout.getVirtualFile(file: File): VirtualFile { + 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 get() = KotlinNativeLoadingMetadataCache.getInstance() diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt index d20b985eed8..7faccd83f37 100644 --- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt @@ -9,6 +9,8 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.ApplicationComponent import com.intellij.openapi.vfs.* 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.serialization.konan.parseModuleHeader import org.jetbrains.kotlin.serialization.konan.parsePackageFragment @@ -25,12 +27,12 @@ class KotlinNativeLoadingMetadataCache : ApplicationComponent { private val moduleHeaderCache = createConcurrentSoftValueMap() fun getCachedPackageFragment(virtualFile: VirtualFile): KonanProtoBuf.LinkDataPackageFragment = - packageFragmentCache.computeIfAbsent(virtualFile) { + packageFragmentCache.computeIfAbsent(virtualFile.surePackageMetadataFile) { parsePackageFragment(virtualFile.contentsToByteArray(false)) } fun getCachedModuleHeader(virtualFile: VirtualFile): KonanProtoBuf.LinkDataLibrary = - moduleHeaderCache.computeIfAbsent(virtualFile) { + moduleHeaderCache.computeIfAbsent(virtualFile.sureModuleHeaderFile) { parseModuleHeader(virtualFile.contentsToByteArray(false)) } @@ -45,7 +47,21 @@ class KotlinNativeLoadingMetadataCache : ApplicationComponent { } private fun invalidateCaches(virtualFile: VirtualFile) { - packageFragmentCache.remove(virtualFile) - moduleHeaderCache.remove(virtualFile) + when { + 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 } diff --git a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt index a8dd040badb..e83594a373d 100644 --- a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt +++ b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt @@ -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_WITH_DOT = ".$KLIB_METADATA_FILE_EXTENSION" +const val KLIB_MODULE_METADATA_FILE_NAME = "module" + const val KONAN_STDLIB_NAME = "stdlib" val KONAN_COMMON_LIBS_PATH: Path diff --git a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt index 93c26edfec8..311f078eecd 100644 --- a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt +++ b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt @@ -14,36 +14,35 @@ import org.jetbrains.kotlin.konan.target.KonanTarget interface KonanLibraryLayout { val libraryName: String - val libDir: File - val target: KonanTarget? - // This is a default implementation. Can't make it an assignment. - get() = null - val manifestFile - get() = File(libDir, "manifest") - val resourcesDir - get() = File(libDir, "resources") + val source: KonanLibrarySource - val targetsDir - get() = File(libDir, "targets") - val targetDir - get() = File(targetsDir, target!!.visibleName) + // This is a default implementation. Can't make it an assignment. + val target: KonanTarget? get() = null - val kotlinDir - get() = File(targetDir, "kotlin") - val nativeDir - get() = File(targetDir, "native") - val includedDir - get() = File(targetDir, "included") + val manifestFile get() = File(libDir, "manifest") + val resourcesDir get() = File(libDir, "resources") - val linkdataDir - get() = File(libDir, "linkdata") - val moduleHeaderFile - get() = File(linkdataDir, "module") - val dataFlowGraphFile - get() = File(linkdataDir, "module_data_flow_graph") + val targetsDir get() = File(libDir, "targets") + val targetDir get() = File(targetsDir, target!!.visibleName) + + val kotlinDir get() = File(targetDir, "kotlin") + val nativeDir get() = File(targetDir, "native") + 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) = - 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() } diff --git a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt index 06e3cf6b5d3..7f9a7111bbc 100644 --- a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt +++ b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt @@ -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_WITH_DOT 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 java.nio.file.FileSystem @@ -17,24 +19,23 @@ interface KonanLibraryLayoutImpl : KonanLibraryLayout { fun 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 { zippedKonanLibraryChecks(klibFile) } override val libraryName = klibFile.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT) - override val libDir: File = File("/") + override val source = KonanLibraryFile(klibFile) - override fun realFiles(action: (KonanLibraryLayout) -> T): T { - return action(FileExtractor(this))!! - } + override fun realFiles(action: (KonanLibraryLayout) -> T) = action(FileExtractor(this)) - override fun inPlace(action: (KonanLibraryLayout) -> T): T { - return klibFile.withZipFileSystem { zipFileSystem -> - action(DirectFromZip(this, zipFileSystem)) - } + override fun inPlace(action: (KonanLibraryLayout) -> T) = source.klibFile.withZipFileSystem { 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" } } -private class UnzippedKonanLibraryLayout(override val libDir: File, override val target: KonanTarget?) : KonanLibraryLayoutImpl { - override val libraryName = libDir.path +private class UnzippedKonanLibraryLayout( + override val libDir: File, + override val target: KonanTarget? +) : KonanLibraryLayoutImpl { - override fun inPlace(action: (KonanLibraryLayout) -> T): T = action(this) - override fun realFiles(action: (KonanLibraryLayout) -> T): T = inPlace(action) + override val libraryName = libDir.path + override val source = KonanLibraryDir + + override fun inPlace(action: (KonanLibraryLayout) -> T) = action(this) + override fun 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 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 * 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 resourcesDir: File by lazy { extractDir(super.resourcesDir) } - override val includedDir: File by lazy { extractDir(super.includedDir) } - override val kotlinDir: File by lazy { extractDir(super.kotlinDir) } - override val nativeDir: File by lazy { extractDir(super.nativeDir) } - 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) zipFileSystem.file(file).copyTo(temporary) temporary.deleteOnExit() 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) zipFileSystem.file(directory).recursiveCopyTo(temporary) temporary.deleteOnExitRecursively()