JS: write full version (major.minor.patch) to .meta.js

This commit is contained in:
Alexander Udalov
2017-01-25 20:13:33 +03:00
parent 038e4da83c
commit ef42e3e4aa
9 changed files with 57 additions and 69 deletions
+1 -1
View File
@@ -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;
}
}
@@ -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)
}