JS: write full version (major.minor.patch) to .meta.js
This commit is contained in:
+1
-1
@@ -1,2 +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 version is $ABI_VERSION$
|
||||
error: file 'compiler/testData/cli/js/wrongAbiVersionLib/wrongAbiLib.meta.js' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 0.0.0, expected version is $ABI_VERSION$
|
||||
COMPILATION_ERROR
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestCaseWithTmpdir;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils;
|
||||
import org.jetbrains.kotlin.utils.JsBinaryVersion;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
import org.jetbrains.kotlin.utils.StringsKt;
|
||||
import org.junit.Assert;
|
||||
@@ -159,18 +159,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
||||
}
|
||||
|
||||
protected void doJsTest(@NotNull String fileName) throws Exception {
|
||||
doTest(fileName, new K2JSCompiler(), new BinaryVersion() {
|
||||
@Override
|
||||
public boolean isCompatible() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(KotlinJavascriptMetadataUtils.ABI_VERSION);
|
||||
}
|
||||
});
|
||||
doTest(fileName, new K2JSCompiler(), JsBinaryVersion.INSTANCE);
|
||||
}
|
||||
|
||||
public static String removePerfOutput(String output) {
|
||||
|
||||
@@ -23,14 +23,14 @@ import java.io.InputStream
|
||||
import javax.xml.bind.DatatypeConverter.parseBase64Binary
|
||||
import javax.xml.bind.DatatypeConverter.printBase64Binary
|
||||
|
||||
class KotlinJavascriptMetadata(val abiVersion: Int, val moduleName: String, val body: ByteArray) {
|
||||
val isAbiVersionCompatible: Boolean = KotlinJavascriptMetadataUtils.isAbiVersionCompatible(abiVersion)
|
||||
}
|
||||
class KotlinJavascriptMetadata(val version: JsBinaryVersion, val moduleName: String, val body: ByteArray)
|
||||
|
||||
// TODO: move to JS modules
|
||||
class JsBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
|
||||
override fun isCompatible() = this.isCompatibleTo(INSTANCE)
|
||||
|
||||
fun toInteger() = (patch shl 16) + (minOf(minor, 255) shl 8) + minOf(major, 255)
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val INSTANCE = JsBinaryVersion(0, 8, 0)
|
||||
@@ -38,6 +38,9 @@ class JsBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
|
||||
@JvmField
|
||||
val INVALID_VERSION = JsBinaryVersion()
|
||||
|
||||
fun fromInteger(version: Int): JsBinaryVersion =
|
||||
JsBinaryVersion(version and 255, (version shr 8) and 255, version shr 16)
|
||||
|
||||
fun readFrom(stream: InputStream): JsBinaryVersion {
|
||||
val dataInput = DataInputStream(stream)
|
||||
val size = dataInput.readInt()
|
||||
@@ -63,37 +66,37 @@ object KotlinJavascriptMetadataUtils {
|
||||
*/
|
||||
private val METADATA_PATTERN = "(?m)\\w+\\.$KOTLIN_JAVASCRIPT_METHOD_NAME\\((\\d+),\\s*(['\"])([^'\"]*)\\2,\\s*(['\"])([^'\"]*)\\4\\)".toPattern()
|
||||
|
||||
@JvmField val ABI_VERSION: Int = JsBinaryVersion.INSTANCE.minor
|
||||
|
||||
fun replaceSuffix(filePath: String): String = filePath.substringBeforeLast(JS_EXT) + META_JS_SUFFIX
|
||||
|
||||
@JvmStatic fun isAbiVersionCompatible(abiVersion: Int): Boolean = abiVersion == ABI_VERSION
|
||||
|
||||
@JvmStatic fun hasMetadata(text: String): Boolean =
|
||||
@JvmStatic
|
||||
fun hasMetadata(text: String): Boolean =
|
||||
KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN.matcher(text).find() && METADATA_PATTERN.matcher(text).find()
|
||||
|
||||
fun formatMetadataAsString(moduleName: String, content: ByteArray): String =
|
||||
"// Kotlin.$KOTLIN_JAVASCRIPT_METHOD_NAME($ABI_VERSION, \"$moduleName\", \"${printBase64Binary(content)}\");\n"
|
||||
"// Kotlin.$KOTLIN_JAVASCRIPT_METHOD_NAME(${JsBinaryVersion.INSTANCE.toInteger()}, \"$moduleName\", \"${printBase64Binary(content)}\");\n"
|
||||
|
||||
@JvmStatic fun loadMetadata(file: File): List<KotlinJavascriptMetadata> {
|
||||
@JvmStatic
|
||||
fun loadMetadata(file: File): List<KotlinJavascriptMetadata> {
|
||||
assert(file.exists()) { "Library $file not found" }
|
||||
val metadataList = arrayListOf<KotlinJavascriptMetadata>()
|
||||
JsLibraryUtils.traverseJsLibrary(file) { content, relativePath ->
|
||||
JsLibraryUtils.traverseJsLibrary(file) { content, _ ->
|
||||
parseMetadata(content, metadataList)
|
||||
}
|
||||
|
||||
return metadataList
|
||||
}
|
||||
|
||||
@JvmStatic fun loadMetadata(path: String): List<KotlinJavascriptMetadata> = loadMetadata(File(path))
|
||||
@JvmStatic
|
||||
fun loadMetadata(path: String): List<KotlinJavascriptMetadata> = loadMetadata(File(path))
|
||||
|
||||
@JvmStatic fun parseMetadata(text: String, metadataList: MutableList<KotlinJavascriptMetadata>) {
|
||||
@JvmStatic
|
||||
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()) {
|
||||
val abiVersion = matcher.group(1).toInt()
|
||||
val abiVersion = JsBinaryVersion.fromInteger(matcher.group(1).toInt())
|
||||
val moduleName = matcher.group(3)
|
||||
val data = matcher.group(5)
|
||||
metadataList.add(KotlinJavascriptMetadata(abiVersion, moduleName, parseBase64Binary(data)))
|
||||
|
||||
@@ -68,7 +68,7 @@ object JsAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
||||
if (moduleInfo is LibraryInfo && KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(moduleInfo.library)) {
|
||||
val providers = moduleInfo.library.getFiles(OrderRootType.CLASSES)
|
||||
.flatMap { KotlinJavascriptMetadataUtils.loadMetadata(PathUtil.getLocalPath(it)!!) }
|
||||
.filter { it.isAbiVersionCompatible }
|
||||
.filter { it.version.isCompatible() }
|
||||
.mapNotNull {
|
||||
KotlinJavascriptSerializationUtil.readModule(
|
||||
it.body, moduleContext.storageManager, moduleDescriptor, container.get<DeserializationConfiguration>()
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||
import java.util.*
|
||||
|
||||
object KotlinJavaScriptAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinJavaScriptAbiVersionIndex, JsBinaryVersion>(
|
||||
KotlinJavaScriptAbiVersionIndex::class.java, { JsBinaryVersion(*it) }
|
||||
KotlinJavaScriptAbiVersionIndex::class.java, ::JsBinaryVersion
|
||||
) {
|
||||
override fun getIndexer() = INDEXER
|
||||
|
||||
@@ -35,7 +35,7 @@ object KotlinJavaScriptAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinJavaScr
|
||||
|
||||
override fun getVersion() = VERSION
|
||||
|
||||
private val VERSION = 2
|
||||
private val VERSION = 3
|
||||
|
||||
private val INDEXER = DataIndexer { inputData: FileContent ->
|
||||
val result = HashMap<JsBinaryVersion, Void?>()
|
||||
@@ -45,13 +45,9 @@ object KotlinJavaScriptAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinJavaScr
|
||||
val metadataList = ArrayList<KotlinJavascriptMetadata>()
|
||||
KotlinJavascriptMetadataUtils.parseMetadata(text, metadataList)
|
||||
for (metadata in metadataList) {
|
||||
val version = if (KotlinJavascriptMetadataUtils.isAbiVersionCompatible(metadata.abiVersion)) {
|
||||
JsBinaryVersion(0, metadata.abiVersion, 0)
|
||||
}
|
||||
else {
|
||||
// Version is set to something weird
|
||||
JsBinaryVersion.INVALID_VERSION
|
||||
}
|
||||
val version = metadata.version.takeIf { it.isCompatible() }
|
||||
// Version is set to something weird
|
||||
?: JsBinaryVersion.INVALID_VERSION
|
||||
result[version] = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,26 +56,17 @@ import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.utils.JsBinaryVersion
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.constant
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
fun getLibraryRootsWithAbiIncompatibleKotlinClasses(module: Module): Collection<BinaryVersionedFile<JvmMetadataVersion>> {
|
||||
return getLibraryRootsWithAbiIncompatibleVersion(
|
||||
module,
|
||||
JvmMetadataVersion.INSTANCE,
|
||||
KotlinMetadataVersionIndex,
|
||||
{ version -> !version.isCompatible() })
|
||||
return getLibraryRootsWithAbiIncompatibleVersion(module, JvmMetadataVersion.INSTANCE, KotlinMetadataVersionIndex)
|
||||
}
|
||||
|
||||
fun getLibraryRootsWithAbiIncompatibleForKotlinJs(module: Module): Collection<BinaryVersionedFile<JsBinaryVersion>> {
|
||||
return getLibraryRootsWithAbiIncompatibleVersion(
|
||||
module,
|
||||
JsBinaryVersion.INSTANCE,
|
||||
KotlinJavaScriptAbiVersionIndex,
|
||||
{ version -> !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(version.minor) }) // TODO: support major.minor.patch version in JS metadata
|
||||
return getLibraryRootsWithAbiIncompatibleVersion(module, JsBinaryVersion.INSTANCE, KotlinJavaScriptAbiVersionIndex)
|
||||
}
|
||||
|
||||
fun updateLibraries(project: Project, libraries: Collection<Library>) {
|
||||
@@ -243,8 +234,8 @@ data class BinaryVersionedFile<out T : BinaryVersion>(val file: VirtualFile, val
|
||||
private fun <T : BinaryVersion> getLibraryRootsWithAbiIncompatibleVersion(
|
||||
module: Module,
|
||||
supportedVersion: T,
|
||||
index: ScalarIndexExtension<T>,
|
||||
checkVersion: (T) -> Boolean): Collection<BinaryVersionedFile<T>> {
|
||||
index: ScalarIndexExtension<T>
|
||||
): Collection<BinaryVersionedFile<T>> {
|
||||
val id = index.name
|
||||
|
||||
val moduleWithAllDependencies = setOf(module) + ModuleUtil.getAllDependentModules(module)
|
||||
@@ -252,7 +243,7 @@ private fun <T : BinaryVersion> getLibraryRootsWithAbiIncompatibleVersion(
|
||||
moduleWithAllDependencies.map { it.moduleWithLibrariesScope }.toTypedArray())
|
||||
|
||||
val allVersions = FileBasedIndex.getInstance().getAllKeys(id, module.project)
|
||||
val badVersions = allVersions.filter(checkVersion).toHashSet()
|
||||
val badVersions = allVersions.filterNot(BinaryVersion::isCompatible).toHashSet()
|
||||
val badRoots = Sets.newHashSet<BinaryVersionedFile<T>>()
|
||||
val fileIndex = ProjectFileIndex.SERVICE.getInstance(module.project)
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor;
|
||||
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil;
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind;
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
||||
import org.jetbrains.kotlin.utils.JsBinaryVersion;
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadata;
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -128,9 +128,8 @@ public abstract class JsConfig {
|
||||
}
|
||||
|
||||
private JsModuleDescriptor<ModuleDescriptorImpl> createModuleDescriptor(KotlinJavascriptMetadata metadata) {
|
||||
assert metadata.isAbiVersionCompatible() :
|
||||
"expected abi version " + KotlinJavascriptMetadataUtils.ABI_VERSION +
|
||||
", but metadata.abiVersion = " + metadata.getAbiVersion();
|
||||
assert metadata.getVersion().isCompatible() :
|
||||
"Expected JS metadata version " + JsBinaryVersion.INSTANCE + ", but actual metadata version is " + metadata.getVersion();
|
||||
|
||||
ModuleDescriptorImpl moduleDescriptor = new ModuleDescriptorImpl(
|
||||
Name.special("<" + metadata.getModuleName() + ">"), storageManager, JsPlatform.INSTANCE.getBuiltIns()
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.utils.JsBinaryVersion;
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadata;
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils;
|
||||
import org.jetbrains.kotlin.utils.LibraryUtils;
|
||||
@@ -144,10 +145,10 @@ public class LibrarySourcesConfig extends JsConfig {
|
||||
}
|
||||
|
||||
for (KotlinJavascriptMetadata metadata : metadataList) {
|
||||
if (!metadata.isAbiVersionCompatible()) {
|
||||
if (!metadata.getVersion().isCompatible()) {
|
||||
report.invoke("File '" + path + "' was compiled with an incompatible version of Kotlin. " +
|
||||
"Its ABI version is " + metadata.getAbiVersion() +
|
||||
", expected version is " + KotlinJavascriptMetadataUtils.ABI_VERSION);
|
||||
"The binary version of its metadata is " + metadata.getVersion() +
|
||||
", expected version is " + JsBinaryVersion.INSTANCE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-10
@@ -158,13 +158,6 @@ object KotlinJavascriptSerializationUtil {
|
||||
packageProto.functionCount == 0 && packageProto.propertyCount == 0 && packageProto.typeAliasCount == 0
|
||||
}) continue
|
||||
|
||||
val packageFqName = fqName.asString()
|
||||
|
||||
val header = JsProtoBuf.Header.newBuilder()
|
||||
// TODO: write pre-release flag if needed
|
||||
// TODO: write JS code binary version
|
||||
header.packageFqName = packageFqName
|
||||
|
||||
val stream = ByteArrayOutputStream()
|
||||
with(DataOutputStream(stream)) {
|
||||
val version = JsBinaryVersion.INSTANCE.toArray()
|
||||
@@ -172,7 +165,7 @@ object KotlinJavascriptSerializationUtil {
|
||||
version.forEach(this::writeInt)
|
||||
}
|
||||
|
||||
header.build().writeDelimitedTo(stream)
|
||||
serializeHeader(fqName).writeDelimitedTo(stream)
|
||||
part.writeTo(stream)
|
||||
|
||||
contentMap[JsSerializerProtocol.getKjsmFilePath(fqName)] = stream.toByteArray()
|
||||
@@ -181,6 +174,19 @@ object KotlinJavascriptSerializationUtil {
|
||||
return contentMap
|
||||
}
|
||||
|
||||
fun serializeHeader(packageFqName: FqName?): JsProtoBuf.Header {
|
||||
val header = JsProtoBuf.Header.newBuilder()
|
||||
|
||||
if (packageFqName != null) {
|
||||
header.packageFqName = packageFqName.asString()
|
||||
}
|
||||
|
||||
// TODO: write pre-release flag if needed
|
||||
// TODO: write JS code binary version
|
||||
|
||||
return header.build()
|
||||
}
|
||||
|
||||
private fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> {
|
||||
return HashSet<FqName>().apply {
|
||||
getSubPackagesFqNames(module.getPackage(FqName.ROOT), this)
|
||||
@@ -202,14 +208,17 @@ object KotlinJavascriptSerializationUtil {
|
||||
}
|
||||
|
||||
private fun JsModuleDescriptor<ModuleDescriptor>.serializeToBinaryMetadata(bindingContext: BindingContext): ByteArray {
|
||||
val proto = serializeMetadata(bindingContext, data, kind, imported)
|
||||
return ByteArrayOutputStream().apply {
|
||||
GZIPOutputStream(this).use(proto::writeTo)
|
||||
GZIPOutputStream(this).use { stream ->
|
||||
serializeHeader(null).writeDelimitedTo(stream)
|
||||
serializeMetadata(bindingContext, data, kind, imported).writeTo(stream)
|
||||
}
|
||||
}.toByteArray()
|
||||
}
|
||||
|
||||
private fun ByteArray.deserializeToLibraryParts(name: String): JsModuleDescriptor<List<JsProtoBuf.Library.Part>> {
|
||||
val content = GZIPInputStream(ByteArrayInputStream(this)).use { stream ->
|
||||
JsProtoBuf.Header.parseDelimitedFrom(stream, JsSerializerProtocol.extensionRegistry)
|
||||
JsProtoBuf.Library.parseFrom(stream, JsSerializerProtocol.extensionRegistry)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user