From 999c83240cd4ddd4cd464326e5019f02d96eeb91 Mon Sep 17 00:00:00 2001 From: Michael Nedzelsky Date: Fri, 5 Jun 2015 13:29:20 +0300 Subject: [PATCH] JS: error message (not exception) for incompatible abi version during compilation --- compiler/testData/cli/js/wrongAbiVersion.args | 6 +++ compiler/testData/cli/js/wrongAbiVersion.out | 2 + .../js/wrongAbiVersionLib/wrongAbiLib.meta.js | 1 + .../cli/KotlincExecutableTestGenerated.java | 6 +++ .../jetbrains/kotlin/cli/js/K2JsCliTest.java | 7 ++++ .../utils/KotlinJavascriptMetadataUtils.kt | 40 ++++++++++++------- .../idea/caches/resolve/JsAnalyzerFacade.kt | 1 + .../idea/js/KotlinJavaScriptLibraryManager.kt | 14 +++---- .../jetbrains/kotlin/js/config/Config.java | 4 ++ .../js/config/LibrarySourcesConfig.java | 20 +++++++--- 10 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 compiler/testData/cli/js/wrongAbiVersion.args create mode 100644 compiler/testData/cli/js/wrongAbiVersion.out create mode 100644 compiler/testData/cli/js/wrongAbiVersionLib/wrongAbiLib.meta.js diff --git a/compiler/testData/cli/js/wrongAbiVersion.args b/compiler/testData/cli/js/wrongAbiVersion.args new file mode 100644 index 00000000000..06ce726a676 --- /dev/null +++ b/compiler/testData/cli/js/wrongAbiVersion.args @@ -0,0 +1,6 @@ +$TESTDATA_DIR$/simple2js.kt +-no-stdlib +-library-files +$TESTDATA_DIR$/wrongAbiVersionLib/wrongAbiLib.meta.js +-output +$TEMP_DIR$/out.js diff --git a/compiler/testData/cli/js/wrongAbiVersion.out b/compiler/testData/cli/js/wrongAbiVersion.out new file mode 100644 index 00000000000..69177e1e8b1 --- /dev/null +++ b/compiler/testData/cli/js/wrongAbiVersion.out @@ -0,0 +1,2 @@ +ERROR: File 'compiler/testData/cli/js/wrongAbiVersionLib/wrongAbiLib.meta.js' was compiled with an incompatible version of Kotlin. Its ABI version is 0, expected ABI version is 3 +COMPILATION_ERROR \ No newline at end of file diff --git a/compiler/testData/cli/js/wrongAbiVersionLib/wrongAbiLib.meta.js b/compiler/testData/cli/js/wrongAbiVersionLib/wrongAbiLib.meta.js new file mode 100644 index 00000000000..c0422c16041 --- /dev/null +++ b/compiler/testData/cli/js/wrongAbiVersionLib/wrongAbiLib.meta.js @@ -0,0 +1 @@ +// Kotlin.kotlin_module_metadata(0, "wrongAbiLib", ""); diff --git a/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java index 4f6a3226dd0..435ff1185ff 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java @@ -285,5 +285,11 @@ public class KotlincExecutableTestGenerated extends AbstractKotlincExecutableTes String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/js/withLib.args"); doJsTest(fileName); } + + @TestMetadata("wrongAbiVersion.args") + public void testWrongAbiVersion() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/js/wrongAbiVersion.args"); + doJsTest(fileName); + } } } diff --git a/compiler/tests/org/jetbrains/kotlin/cli/js/K2JsCliTest.java b/compiler/tests/org/jetbrains/kotlin/cli/js/K2JsCliTest.java index 5d99e5c85b4..2f173963277 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/js/K2JsCliTest.java +++ b/compiler/tests/org/jetbrains/kotlin/cli/js/K2JsCliTest.java @@ -51,6 +51,13 @@ public class K2JsCliTest extends CliBaseTest { Assert.assertFalse(new File(tmpdir.getTmpDir(), "out.js").exists()); } + @Test + public void wrongAbiVersion() throws Exception { + executeCompilerCompareOutputJS(); + + Assert.assertFalse(new File(tmpdir.getTmpDir(), "out.js").exists()); + } + @Test public void jsHelp() throws Exception { executeCompilerCompareOutputJS(); diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt index f0d21f100e9..2ef57fd74ff 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt +++ b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt @@ -16,35 +16,46 @@ package org.jetbrains.kotlin.utils -import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.util.io.FileUtil import java.io.File -import java.io.IOException import javax.xml.bind.DatatypeConverter.parseBase64Binary import javax.xml.bind.DatatypeConverter.printBase64Binary import kotlin.platform.platformStatic -public class KotlinJavascriptMetadata(public val moduleName: String, public val body: ByteArray) +public class KotlinJavascriptMetadata(public val abiVersion: Int, public val moduleName: String, public val body: ByteArray) { + public val isAbiVersionCompatible: Boolean = KotlinJavascriptMetadataUtils.isAbiVersionCompatible(abiVersion) +} public object KotlinJavascriptMetadataUtils { - private val LOG = Logger.getInstance(javaClass()) - public val JS_EXT: String = ".js" public val META_JS_SUFFIX: String = ".meta.js" public val VFS_PROTOCOL: String = "kotlin-js-meta" private val KOTLIN_JAVASCRIPT_METHOD_NAME = "kotlin_module_metadata" - private val KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN = "\\.kotlin_module_metadata\\(".toRegex() + private val KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN = "\\.kotlin_module_metadata\\(".toPattern() /** * Matches string like .kotlin_module_metadata(, , ) */ - private val METADATA_PATTERN = "(?m)\\w+\\.$KOTLIN_JAVASCRIPT_METHOD_NAME\\((\\d+),\\s*(['\"])([^'\"]*)\\2,\\s*(['\"])([^'\"]*)\\4\\)".toRegex() - private val ABI_VERSION = 3 + private val METADATA_PATTERN = "(?m)\\w+\\.$KOTLIN_JAVASCRIPT_METHOD_NAME\\((\\d+),\\s*(['\"])([^'\"]*)\\2,\\s*(['\"])([^'\"]*)\\4\\)".toPattern() + + platformStatic + public val ABI_VERSION: Int = 3 public fun replaceSuffix(filePath: String): String = filePath.substringBeforeLast(JS_EXT) + META_JS_SUFFIX + platformStatic + public fun isAbiVersionCompatible(abiVersion: Int): Boolean = abiVersion == ABI_VERSION + platformStatic public fun hasMetadata(text: String): Boolean = METADATA_PATTERN.matcher(text).find() + public fun hasMetadataWithIncompatibleAbiVersion(text: String): Boolean { + val matcher = METADATA_PATTERN.matcher(text) + while (matcher.find()) { + var abiVersion = matcher.group(1).toInt() + if (abiVersion != ABI_VERSION) return true + } + return false + } + public fun formatMetadataAsString(moduleName: String, content: ByteArray): String = "// Kotlin.$KOTLIN_JAVASCRIPT_METHOD_NAME($ABI_VERSION, \"$moduleName\", \"${printBase64Binary(content)}\");\n" @@ -59,7 +70,7 @@ public object KotlinJavascriptMetadataUtils { path += "/$relativePath" } - parseMetadata(content, path, metadataList) + parseMetadata(content, metadataList) } return metadataList @@ -68,18 +79,17 @@ public object KotlinJavascriptMetadataUtils { platformStatic public fun loadMetadata(path: String): List = loadMetadata(File(path)) - private fun parseMetadata(text: String, path: String, metadataList: MutableList) { + platformStatic + public fun parseMetadata(text: String, metadataList: MutableList) { // Check for literal pattern first in order to reduce time for large files without metadata if (!KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN.matcher(text).find()) return val matcher = METADATA_PATTERN.matcher(text) while (matcher.find()) { - var abiVersion = Integer.parseInt(matcher.group(1)); - if (abiVersion != ABI_VERSION) LOG.error("Unsupported abi version in $path, expected $ABI_VERSION, but $abiVersion") - + var abiVersion = matcher.group(1).toInt() var moduleName = matcher.group(3) val data = matcher.group(5) - metadataList.add(KotlinJavascriptMetadata(moduleName, parseBase64Binary(data))) + metadataList.add(KotlinJavascriptMetadata(abiVersion, moduleName, parseBase64Binary(data))) } } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/JsAnalyzerFacade.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/JsAnalyzerFacade.kt index 9d1b18cee76..d35a5d1671f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/JsAnalyzerFacade.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/JsAnalyzerFacade.kt @@ -63,6 +63,7 @@ public object JsAnalyzerFacade : AnalyzerFacade"), storageManager, TopDownAnalyzerFacadeForJS.JS_MODULE_PARAMETERS diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/LibrarySourcesConfig.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/LibrarySourcesConfig.java index 5347b37bb2d..3fb7f12385d 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/LibrarySourcesConfig.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/LibrarySourcesConfig.java @@ -40,7 +40,6 @@ import java.io.File; import java.util.Collections; import java.util.List; -import static org.jetbrains.kotlin.utils.LibraryUtils.isKotlinJavascriptLibraryWithMetadata; import static org.jetbrains.kotlin.utils.LibraryUtils.isOldKotlinJavascriptLibrary; import static org.jetbrains.kotlin.utils.PathUtil.getKotlinPathsForDistDirectory; @@ -161,12 +160,21 @@ public class LibrarySourcesConfig extends Config { if (isOldKotlinJavascriptLibrary(filePath)) { moduleName = LibraryUtils.getKotlinJsModuleName(filePath); } - else if (isKotlinJavascriptLibraryWithMetadata(filePath)) { - moduleName = null; - } else { - report.invoke("'" + path + "' is not a valid Kotlin Javascript library"); - return true; + List metadataList = KotlinJavascriptMetadataUtils.loadMetadata(filePath); + if (metadataList.isEmpty()) { + report.invoke("'" + path + "' is not a valid Kotlin Javascript library"); + return true; + } + + for (KotlinJavascriptMetadata metadata : metadataList) { + if (!metadata.getIsAbiVersionCompatible()) { + report.invoke("File '" + path + "' was compiled with an incompatible version of Kotlin. Its ABI version is " + metadata.getAbiVersion() + ", expected ABI version is " + KotlinJavascriptMetadataUtils.ABI_VERSION); + return true; + } + } + + moduleName = null; } if (action != null) {