Improve ABI version from one number to "major.minor.patch"

This commit is contained in:
Alexander Udalov
2015-09-08 20:21:19 +03:00
parent 299df05a74
commit 6cecc66d10
37 changed files with 256 additions and 118 deletions
@@ -16,11 +16,16 @@
package org.jetbrains.kotlin.load.java;
public final class AbiVersionUtil {
public static final int INVALID_VERSION = -1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
public static boolean isAbiVersionCompatible(int abiVersion) {
return abiVersion == JvmAbi.VERSION;
public final class AbiVersionUtil {
public static final BinaryVersion INVALID_VERSION = BinaryVersion.create(new int[0]);
public static boolean isAbiVersionCompatible(@NotNull BinaryVersion actual) {
// TODO: compare versions according to Semantic Versioning
return actual.getMajor() == JvmAbi.VERSION.getMajor() &&
actual.getMinor() == JvmAbi.VERSION.getMinor();
}
private AbiVersionUtil() {
@@ -21,13 +21,20 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
public final class JvmAbi {
/**
* This constant is used to identify binary format (class file) versions
* If you change class file metadata format and/or naming conventions, please increase this number
* If you change class file metadata format and/or naming conventions, please change this version.
* - Major version should be increased only when the new binary format is neither forward- nor backward compatible.
* This shouldn't really ever happen at all.
* - Minor version should be increased when the new format is backward compatible,
* i.e. the new compiler can process old class files, but the old compiler will not be able to process new class files.
* - Patch version can be increased freely and is only supposed to be used for debugging. Increase the patch version when you
* make a change to the metadata format or the bytecode which is both forward- and backward compatible.
*/
public static final int VERSION = 24;
public static final BinaryVersion VERSION = BinaryVersion.create(0, 24, 0);
public static final String TRAIT_IMPL_CLASS_NAME = "$TImpl";
public static final String TRAIT_IMPL_SUFFIX = "$" + TRAIT_IMPL_CLASS_NAME;
@@ -38,7 +38,7 @@ public final class JvmAnnotationNames {
public static final FqName KOTLIN_SIGNATURE = new FqName("kotlin.jvm.KotlinSignature");
public static final FqName OLD_KOTLIN_SIGNATURE = new FqName("jet.runtime.typeinfo.KotlinSignature");
public static final String ABI_VERSION_FIELD_NAME = "abiVersion";
public static final String VERSION_FIELD_NAME = "version";
public static final String KIND_FIELD_NAME = "kind";
public static final String FILE_PART_CLASS_NAMES_FIELD_NAME = "filePartClassNames";
public static final String MULTIFILE_CLASS_NAME_FIELD_NAME = "multifileClassName";
@@ -114,6 +114,8 @@ public final class JvmAnnotationNames {
@Deprecated
public static final FqName OLD_KOTLIN_TRAIT_IMPL = new FqName("jet.KotlinTraitImpl");
public static final String OLD_ABI_VERSION_FIELD_NAME = "abiVersion";
// When these annotations appear on a declaration, they are copied to the _type_ of the declaration, becoming type annotations
// See also DescriptorRendererOptions#excludedTypeAnnotationClasses
public static final Set<FqName> ANNOTATIONS_COPIED_TO_TYPES = KotlinPackage.setOf(
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.load.java.AbiVersionUtil
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.jvm.JvmPackageTable
import java.io.ByteArrayInputStream
import java.io.DataInputStream
@@ -28,19 +29,27 @@ public class ModuleMapping private constructor(val packageFqName2Parts: Map<Stri
}
companion object {
public val MAPPING_FILE_EXT: String = "kotlin_module";
public val MAPPING_FILE_EXT: String = "kotlin_module"
public val EMPTY: ModuleMapping = ModuleMapping(emptyMap())
fun create(protoWithAbi: ByteArray? = null): ModuleMapping {
if (protoWithAbi == null) {
fun create(proto: ByteArray? = null): ModuleMapping {
if (proto == null) {
return EMPTY
}
val inputStream = DataInputStream(ByteArrayInputStream(protoWithAbi))
val intCount = inputStream.readInt()
assert(intCount == 1, {"Expected one int value for abi version, but: $intCount"})
val abiVersion = Integer.valueOf(inputStream.readInt());
val inputStream = DataInputStream(ByteArrayInputStream(proto))
val size = inputStream.readInt()
val abiVersion =
if (size == 1) {
// TODO: this is a temporary workaround, drop after M13
BinaryVersion.create(0, inputStream.readInt(), 0)
}
else {
BinaryVersion.create((0..size - 1).map { inputStream.readInt() }.toIntArray())
}
if (AbiVersionUtil.isAbiVersionCompatible(abiVersion)) {
val parseFrom = JvmPackageTable.PackageTable.parseFrom(inputStream)
if (parseFrom != null) {
@@ -55,6 +64,7 @@ public class ModuleMapping private constructor(val packageFqName2Parts: Map<Stri
return ModuleMapping(packageFqNameParts)
}
}
return EMPTY
}
}
@@ -16,13 +16,14 @@
package org.jetbrains.kotlin.load.kotlin.header
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.load.java.AbiVersionUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass
public class KotlinClassHeader(
public val kind: KotlinClassHeader.Kind,
public val version: Int,
public val version: BinaryVersion,
public val annotationData: Array<String>?,
public val classKind: KotlinClass.Kind?,
public val syntheticClassKind: KotlinSyntheticClass.Kind?,
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.load.java.AbiVersionUtil;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
import java.util.ArrayList;
import java.util.HashMap;
@@ -59,7 +60,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
OLD_DEPRECATED_ANNOTATIONS_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(OLD_KOTLIN_TRAIT_IMPL), SYNTHETIC_CLASS);
}
private int version = AbiVersionUtil.INVALID_VERSION;
private BinaryVersion version = AbiVersionUtil.INVALID_VERSION;
private String multifileClassName = null;
private String[] filePartClassNames = null;
private String[] annotationData = null;
@@ -151,12 +152,18 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
if (name != null) {
if (name.asString().equals(ABI_VERSION_FIELD_NAME)) {
version = value instanceof Integer ? (Integer) value : AbiVersionUtil.INVALID_VERSION;
}
else if (name.asString().equals(MULTIFILE_CLASS_NAME_FIELD_NAME)) {
multifileClassName = value instanceof String ? (String) value : null;
if (name == null) return;
String string = name.asString();
if (VERSION_FIELD_NAME.equals(string)) {
version = value instanceof int[] ? BinaryVersion.create((int[]) value) : AbiVersionUtil.INVALID_VERSION;
}
else if (MULTIFILE_CLASS_NAME_FIELD_NAME.equals(string)) {
multifileClassName = value instanceof String ? (String) value : null;
}
else if (OLD_ABI_VERSION_FIELD_NAME.equals(string)) {
if (version == AbiVersionUtil.INVALID_VERSION && value instanceof Integer && (Integer) value > 0) {
version = BinaryVersion.create(0, (Integer) value, 0);
}
}
}
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.load.java.components
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
public object RuntimeErrorReporter : ErrorReporter {
@@ -29,8 +29,8 @@ public object RuntimeErrorReporter : ErrorReporter {
throw IllegalStateException("Incomplete hierarchy for class ${descriptor.getName()}, unresolved classes $unresolvedSuperClasses")
}
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: Int) {
throw IllegalStateException("Incompatible ABI version of ${classId}: $actualVersion " +
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: BinaryVersion) {
throw IllegalStateException("Incompatible ABI version of $classId: $actualVersion " +
"(expected version is ${JvmAbi.VERSION})")
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.serialization.deserialization
data class BinaryVersion private constructor(
val major: Int,
val minor: Int,
val patch: Int,
val rest: List<Int> = listOf()
) {
fun toArray(): IntArray =
intArrayOf(major, minor, patch, *rest.toIntArray())
override fun toString(): String {
val versions = toArray().takeWhile { it != UNKNOWN }
return if (versions.isEmpty()) "unknown" else versions.joinToString(".")
}
companion object {
private val UNKNOWN = -1
@JvmStatic
fun create(version: IntArray): BinaryVersion {
return BinaryVersion(
major = version.getOrNull(0) ?: UNKNOWN,
minor = version.getOrNull(1) ?: UNKNOWN,
patch = version.getOrNull(2) ?: UNKNOWN,
rest = if (version.size() > 3) version.asList().subList(3, version.size()).toList() else listOf()
)
}
@JvmStatic
fun create(major: Int, minor: Int, patch: Int) = BinaryVersion(major, minor, patch)
}
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.name.ClassId;
import java.util.List;
public interface ErrorReporter {
void reportIncompatibleAbiVersion(@NotNull ClassId classId, @NotNull String filePath, int actualVersion);
void reportIncompatibleAbiVersion(@NotNull ClassId classId, @NotNull String filePath, @NotNull BinaryVersion actualVersion);
void reportIncompleteHierarchy(@NotNull ClassDescriptor descriptor, @NotNull List<String> unresolvedSuperClasses);
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinCallable {
@Deprecated
int abiVersion();
int[] version() default {};
String[] data();
}
@@ -21,8 +21,11 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinClass {
@Deprecated
int abiVersion();
int[] version() default {};
Kind kind() default Kind.CLASS;
String[] data();
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinFileFacade {
@Deprecated
int abiVersion();
int[] version() default {};
String[] data();
}
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinMultifileClass {
@Deprecated
int abiVersion();
int[] version() default {};
String[] filePartClassNames();
}
@@ -21,8 +21,12 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinMultifileClassPart {
@Deprecated
int abiVersion();
int[] version() default {};
String multifileClassName();
String[] data();
}
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinPackage {
@Deprecated
int abiVersion();
int[] version() default {};
String[] data();
}
@@ -21,8 +21,11 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface KotlinSyntheticClass {
@Deprecated
int abiVersion();
int[] version() default {};
Kind kind();
enum Kind {