From 038e4da83c37787f8bada4c0bfd43d270deaf60e Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 25 Jan 2017 18:37:05 +0300 Subject: [PATCH] Do not crash JS decompiler on non-existing .jar entries See the comment for clarification --- .../js/KotlinJavaScriptMetaFileDecompiler.kt | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt index 75350ff7414..6403329e306 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.decompiler.js +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.FileViewProvider import com.intellij.psi.PsiManager @@ -34,6 +35,7 @@ import org.jetbrains.kotlin.serialization.js.JsProtoBuf import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol import org.jetbrains.kotlin.utils.JsBinaryVersion import java.io.ByteArrayInputStream +import java.io.IOException class KotlinJavaScriptMetaFileDecompiler : ClassFileDecompilers.Full() { private val stubBuilder = KotlinJavaScriptStubBuilder() @@ -49,6 +51,10 @@ class KotlinJavaScriptMetaFileDecompiler : ClassFileDecompilers.Full() { KtDecompiledFile(provider, ::buildDecompiledTextFromJsMetadata) } } + + companion object { + internal val LOG = Logger.getInstance(KotlinJavaScriptMetaFileDecompiler::class.java) + } } private val decompilerRendererForJS = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() } @@ -60,9 +66,11 @@ fun buildDecompiledTextFromJsMetadata(kjsmFile: VirtualFile): DecompiledText { } val file = KjsmFile.read(kjsmFile) - ?: error("Unexpectedly empty file: $kjsmFile") when (file) { + null -> { + return createIncompatibleAbiVersionDecompiledText(JsBinaryVersion.INSTANCE, JsBinaryVersion.INVALID_VERSION) + } is KjsmFile.Incompatible -> { return createIncompatibleAbiVersionDecompiledText(JsBinaryVersion.INSTANCE, file.version) } @@ -94,11 +102,7 @@ sealed class KjsmFile { companion object { fun read(file: VirtualFile): KjsmFile? { - if (!file.isValid) { - return null - } - - val stream = ByteArrayInputStream(file.contentsToByteArray()) + val stream = ByteArrayInputStream(readFileContentsSafely(file) ?: return null) val version = JsBinaryVersion.readFrom(stream) if (!version.isCompatible()) { @@ -111,5 +115,21 @@ sealed class KjsmFile { return result } + + private fun readFileContentsSafely(file: VirtualFile): ByteArray? { + if (!file.isValid) return null + + return try { + file.contentsToByteArray() + } + catch (e: IOException) { + // This is needed because sometimes we're given VirtualFile instances that point to non-existent .jar entries. + // Such files are valid (isValid() returns true), but an attempt to read their contents results in a FileNotFoundException. + // This looks like a platform issue, but we must still deal with it somehow here. + // Calling "refresh()" instead of catching an exception would be more correct, but is likely to degrade performance + KotlinJavaScriptMetaFileDecompiler.LOG.warn("Attempt to read non-existing file: $file", e) + null + } + } } }