JS: error message (not exception) for incompatible abi version during compilation
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
$TESTDATA_DIR$/simple2js.kt
|
||||
-no-stdlib
|
||||
-library-files
|
||||
$TESTDATA_DIR$/wrongAbiVersionLib/wrongAbiLib.meta.js
|
||||
-output
|
||||
$TEMP_DIR$/out.js
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
// Kotlin.kotlin_module_metadata(0, "wrongAbiLib", "");
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<KotlinJavascriptMetadataUtils>())
|
||||
|
||||
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 <name>.kotlin_module_metadata(<abi version>, <module name>, <base64 data>)
|
||||
*/
|
||||
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<KotlinJavascriptMetadata> = loadMetadata(File(path))
|
||||
|
||||
private fun parseMetadata(text: String, path: String, metadataList: MutableList<KotlinJavascriptMetadata>) {
|
||||
platformStatic
|
||||
public fun parseMetadata(text: String, metadataList: MutableList<KotlinJavascriptMetadata>) {
|
||||
// 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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public object JsAnalyzerFacade : AnalyzerFacade<JsResolverForModule, PlatformAna
|
||||
if (moduleInfo is LibraryInfo && KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(moduleInfo.library)) {
|
||||
val providers = moduleInfo.library.getFiles(OrderRootType.CLASSES)
|
||||
.flatMap { KotlinJavascriptMetadataUtils.loadMetadata(PathUtil.getLocalPath(it)!!) }
|
||||
.filter { it.isAbiVersionCompatible }
|
||||
.map { KotlinJavascriptSerializationUtil.createPackageFragmentProvider(moduleDescriptor, it.body, moduleContext.storageManager) }
|
||||
.filterNotNull()
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ import com.intellij.util.PathUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil
|
||||
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil.isJsKotlinModule
|
||||
import org.jetbrains.kotlin.utils.LibraryUtils
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.platform.platformStatic
|
||||
|
||||
@@ -103,11 +102,12 @@ public class KotlinJavaScriptLibraryManager private constructor(private var myPr
|
||||
val path = PathUtil.getLocalPath(clsRootFile)
|
||||
assert(path != null, "expected not-null path for ${clsRootFile.getName()}")
|
||||
|
||||
if (LibraryUtils.isKotlinJavascriptLibraryWithMetadata(File(path))) {
|
||||
val classRoot = KotlinJavaScriptMetaFileSystem.getInstance().refreshAndFindFileByPath(path!! + "!/")
|
||||
clsRootFiles.add(classRoot)
|
||||
addSources = true
|
||||
}
|
||||
val metadataList = KotlinJavascriptMetadataUtils.loadMetadata(path!!)
|
||||
if (metadataList.filter { !it.isAbiVersionCompatible }.isNotEmpty()) continue
|
||||
|
||||
val classRoot = KotlinJavaScriptMetaFileSystem.getInstance().refreshAndFindFileByPath(path + "!/")
|
||||
clsRootFiles.add(classRoot)
|
||||
addSources = true
|
||||
}
|
||||
if (addSources) {
|
||||
srcRootFiles.addAll(arrayListOf(*library.getFiles(OrderRootType.SOURCES)))
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.JetFile;
|
||||
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil;
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadata;
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -146,6 +147,9 @@ public abstract class Config {
|
||||
}
|
||||
|
||||
private ModuleDescriptorImpl createModuleDescriptor(KotlinJavascriptMetadata metadata) {
|
||||
assert metadata.getIsAbiVersionCompatible() :
|
||||
"expected abi version " + KotlinJavascriptMetadataUtils.ABI_VERSION + ", but metadata.abiVersion = " + metadata.getAbiVersion();
|
||||
|
||||
ModuleDescriptorImpl moduleDescriptor = new ModuleDescriptorImpl(
|
||||
Name.special("<" + metadata.getModuleName() + ">"), storageManager,
|
||||
TopDownAnalyzerFacadeForJS.JS_MODULE_PARAMETERS
|
||||
|
||||
@@ -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<KotlinJavascriptMetadata> 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) {
|
||||
|
||||
Reference in New Issue
Block a user