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
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.load.java.JavaVisibilities;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
import org.jetbrains.kotlin.name.FqName;
@@ -61,7 +60,6 @@ import java.util.Set;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveClass;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isInterface;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
@@ -823,7 +821,7 @@ public class AsmUtil {
public static void writeKotlinSyntheticClassAnnotation(@NotNull ClassBuilder v, @NotNull KotlinSyntheticClass.Kind kind) {
AnnotationVisitor av = v.newAnnotation(Type.getObjectType(KotlinSyntheticClass.CLASS_NAME.getInternalName()).getDescriptor(), true);
av.visit(ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
JvmCodegenUtil.writeAbiVersion(av);
av.visitEnum(
JvmAnnotationNames.KIND_FIELD_NAME,
Type.getObjectType(KotlinSyntheticClass.KIND_INTERNAL_NAME).getDescriptor(),
@@ -120,10 +120,13 @@ public class ClassFileFactory implements OutputFileCollection {
@Override
public byte[] asBytes(ClassBuilderFactory factory) {
try {
ByteArrayOutputStream moduleMapping = new ByteArrayOutputStream(4*1024);
ByteArrayOutputStream moduleMapping = new ByteArrayOutputStream(4096);
DataOutputStream dataOutStream = new DataOutputStream(moduleMapping);
dataOutStream.writeInt(1);
dataOutStream.writeInt(JvmAbi.VERSION);
int[] version = JvmAbi.VERSION.toArray();
dataOutStream.writeInt(version.length);
for (int number : version) {
dataOutStream.writeInt(number);
}
builder.build().writeTo(dataOutStream);
dataOutStream.flush();
return moduleMapping.toByteArray();
@@ -237,7 +237,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
NameResolver nameResolver = new NameResolver(strings.serializeSimpleNames(), strings.serializeQualifiedNames());
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CALLABLE), true);
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
JvmCodegenUtil.writeAbiVersion(av);
AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(SerializationUtil.serializeCallableData(nameResolver, callableProto))) {
array.visit(null, string);
@@ -264,7 +264,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
ClassData data = new ClassData(nameResolver, classProto);
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CLASS), true);
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
JvmCodegenUtil.writeAbiVersion(av);
//noinspection ConstantConditions
if (kind != null) {
av.visitEnum(
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.codegen.context.PackageContext;
import org.jetbrains.kotlin.codegen.context.RootContext;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment;
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
@@ -46,6 +48,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import java.io.File;
@@ -257,4 +260,11 @@ public class JvmCodegenUtil {
public static String getMappingFileName(@NotNull String moduleName) {
return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
}
public static void writeAbiVersion(@NotNull AnnotationVisitor av) {
av.visit(JvmAnnotationNames.VERSION_FIELD_NAME, JvmAbi.VERSION.toArray());
// TODO: drop after some time
av.visit(JvmAnnotationNames.OLD_ABI_VERSION_FIELD_NAME, JvmAbi.VERSION.getMinor());
}
}
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider
import org.jetbrains.kotlin.name.FqName
@@ -184,7 +183,7 @@ public class MultifileClassCodegen(
if (files.any { it.isScript }) return
val av = classBuilder.newAnnotation(AsmUtil.asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_MULTIFILE_CLASS), true)
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION)
JvmCodegenUtil.writeAbiVersion(av)
val shortNames = partFqNames.map { it.shortName().asString() }.sorted()
val filePartClassNamesArray = av.visitArray(JvmAnnotationNames.FILE_PART_CLASS_NAMES_FIELD_NAME)
@@ -20,7 +20,6 @@ import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile
@@ -36,7 +35,6 @@ import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import java.util.*
public class MultifileClassPartCodegen(
v: ClassBuilder,
file: JetFile,
@@ -60,12 +58,12 @@ public class MultifileClassPartCodegen(
override fun generateBody() {
for (declaration in element.declarations) {
if (declaration is JetNamedFunction || declaration is JetProperty) {
genFunctionOrProperty(declaration);
genFunctionOrProperty(declaration)
}
}
if (state.classBuilderMode == ClassBuilderMode.FULL) {
generateInitializers({ createOrGetClInitCodegen(); });
generateInitializers { createOrGetClInitCodegen() }
}
}
@@ -100,7 +98,7 @@ public class MultifileClassPartCodegen(
val data = PackageData(nameResolver, packageProto)
val av = v.newAnnotation(AsmUtil.asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_MULTIFILE_CLASS_PART), true)
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION)
JvmCodegenUtil.writeAbiVersion(av)
av.visit(JvmAnnotationNames.MULTIFILE_CLASS_NAME_FIELD_NAME, multifileClassFqName.shortName().asString())
val dataArray = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME)
for (string in BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) {
@@ -301,7 +301,7 @@ public class PackageCodegen {
PackageData data = new PackageData(nameResolver, packageProto);
AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true);
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
JvmCodegenUtil.writeAbiVersion(av);
AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) {
array.visit(null, string);
@@ -16,18 +16,14 @@
package org.jetbrains.kotlin.codegen;
import com.google.common.collect.Lists;
import com.intellij.util.ArrayUtil;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
import org.jetbrains.kotlin.psi.JetDeclaration;
import org.jetbrains.kotlin.psi.JetFile;
@@ -41,12 +37,9 @@ import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.kotlin.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class PackagePartCodegen extends MemberCodegen<JetFile> {
@@ -124,7 +117,7 @@ public class PackagePartCodegen extends MemberCodegen<JetFile> {
PackageData data = new PackageData(nameResolver, packageProto);
AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_FILE_FACADE), true);
av.visit(ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
JvmCodegenUtil.writeAbiVersion(av);
AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) {
array.visit(null, string);
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.OverrideResolver
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
import org.jetbrains.kotlin.util.slicedMap.Slices
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
@@ -29,7 +30,7 @@ import org.jetbrains.kotlin.util.slicedMap.WritableSlice
public class TraceBasedErrorReporter(private val trace: BindingTrace) : ErrorReporter {
companion object {
private val LOG = Logger.getInstance(javaClass<TraceBasedErrorReporter>())
private val LOG = Logger.getInstance(TraceBasedErrorReporter::class.java)
public val ABI_VERSION_ERRORS: WritableSlice<String, AbiVersionErrorData> = Slices.createCollectiveSlice()
// TODO: MutableList is a workaround for KT-5792 Covariant types in Kotlin translated to wildcard types in Java
@@ -37,11 +38,11 @@ public class TraceBasedErrorReporter(private val trace: BindingTrace) : ErrorRep
}
public data class AbiVersionErrorData(
public val actualVersion: Int,
public val actualVersion: BinaryVersion,
public val classId: ClassId
)
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: Int) {
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: BinaryVersion) {
trace.record(ABI_VERSION_ERRORS, filePath, AbiVersionErrorData(actualVersion, classId))
}
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.psi.stubs.impl.KotlinFileStubImpl;
import java.io.IOException;
public class JetFileElementType extends IStubFileElementType<KotlinFileStub> {
public static final int STUB_VERSION = 55;
public static final int STUB_VERSION = 56;
private static final String NAME = "kotlin.FILE";
+2 -2
View File
@@ -1,5 +1,5 @@
compiler/testData/cli/jvm/wrongAbiVersionLib/bin/ClassWithWrongAbiVersion.class: error: class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is $ABI_VERSION$
compiler/testData/cli/jvm/wrongAbiVersionLib/bin/wrong/Wrong_packageKt.class: error: class 'wrong/Wrong_packageKt' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is $ABI_VERSION$
compiler/testData/cli/jvm/wrongAbiVersionLib/bin/ClassWithWrongAbiVersion.class: error: class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. Its ABI version is unknown, expected ABI version is $ABI_VERSION$
compiler/testData/cli/jvm/wrongAbiVersionLib/bin/wrong/Wrong_packageKt.class: error: class 'wrong/Wrong_packageKt' was compiled with an incompatible version of Kotlin. Its ABI version is unknown, expected ABI version is $ABI_VERSION$
compiler/testData/cli/jvm/wrongAbiVersion.kt:4:3: error: unresolved reference: bar
bar()
^
@@ -71,7 +71,7 @@ public class CliBaseTest {
public static String getNormalizedCompilerOutput(@NotNull String pureOutput, @NotNull ExitCode exitCode, @NotNull String testDataDir) {
String normalizedOutputWithoutExitCode = pureOutput
.replace(new File(testDataDir).getAbsolutePath(), "$TESTDATA_DIR$")
.replace("expected ABI version is " + Integer.toString(JvmAbi.VERSION), "expected ABI version is $ABI_VERSION$")
.replace("expected ABI version is " + JvmAbi.VERSION, "expected ABI version is $ABI_VERSION$")
.replace("\\", "/");
return removePerfOutput(normalizedOutputWithoutExitCode) + exitCode;
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.codegen;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.backend.common.output.OutputFile;
import org.jetbrains.kotlin.load.java.AbiVersionUtil;
@@ -27,17 +26,18 @@ import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
import org.jetbrains.kotlin.test.ConfigurationKind;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KIND_FIELD_NAME;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass.Kind.ANONYMOUS_OBJECT;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass.Kind.LOCAL_CLASS;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass.Kind.*;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.VERSION_FIELD_NAME;
public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
public static final FqName PACKAGE_NAME = new FqName("test");
@@ -194,10 +194,10 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
Annotation annotation = aClass.getAnnotation(annotationClass);
Integer version = (Integer) CodegenTestUtil.getAnnotationAttribute(annotation, ABI_VERSION_FIELD_NAME);
int[] version = (int[]) CodegenTestUtil.getAnnotationAttribute(annotation, VERSION_FIELD_NAME);
assertNotNull(version);
assertTrue("Annotation " + annotationFqName + " is written with an unsupported format",
AbiVersionUtil.isAbiVersionCompatible(version));
AbiVersionUtil.isAbiVersionCompatible(BinaryVersion.create(version)));
Object actualKind = CodegenTestUtil.getAnnotationAttribute(annotation, KIND_FIELD_NAME);
assertNotNull(actualKind);
@@ -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 {
@@ -26,15 +26,18 @@ import org.jetbrains.org.objectweb.asm.*
* Some additional information is printed to stderr
*/
fun loadAbiVersionOfClass(bytes: ByteArray): Int? {
var result: Int? = null
fun loadAbiVersionOfClass(bytes: ByteArray): IntArray? {
var result: IntArray? = null
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
if (desc == "Lkotlin/jvm/internal/KotlinClass;") {
return object : AnnotationVisitor(Opcodes.ASM5) {
override fun visit(name: String, value: Any) {
if (name == "abiVersion") {
result = value as Int
if (name == "version") {
result = value as IntArray
}
else if (name == "abiVersion") {
result = intArrayOf(0, value as Int, 0)
}
}
}
@@ -45,7 +48,7 @@ fun loadAbiVersionOfClass(bytes: ByteArray): Int? {
return result
}
fun loadVersion(library: File): Int {
fun loadVersion(library: File): IntArray {
val jarFile = JarFile(library)
try {
for (entry in jarFile.entries()) {
@@ -71,8 +74,8 @@ fun main(args: Array<String>) {
val library1 = File(args[0])
val library2 = File(args[1])
val v1 = loadVersion(library1)
val v2 = loadVersion(library2)
val v1 = loadVersion(library1).joinToString(".")
val v2 = loadVersion(library2).joinToString(".")
if (v1 != v2) {
System.err.println("ABI versions differ:")
@@ -16,11 +16,12 @@
package org.jetbrains.kotlin.idea.decompiler.textBuilder
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
class LoggingErrorReporter(private val log: Logger) : ErrorReporter {
override fun reportLoadingError(message: String, exception: Exception?) {
@@ -35,7 +36,7 @@ class LoggingErrorReporter(private val log: Logger) : ErrorReporter {
log.error("Could not infer visibility for $descriptor")
}
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: Int) {
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: BinaryVersion) {
log.error("Incompatible ABI version for class $classId, actual version: $actualVersion")
}
}
@@ -23,21 +23,23 @@ import com.intellij.util.indexing.FileContent
import org.jetbrains.kotlin.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses
import org.jetbrains.kotlin.load.java.AbiVersionUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.*
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersionIndex>(javaClass<KotlinAbiVersionIndex>()) {
public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersionIndex>(KotlinAbiVersionIndex::class.java) {
override fun getIndexer() = INDEXER
override fun getInputFilter() = FileBasedIndex.InputFilter() { file -> file.getFileType() == StdFileTypes.CLASS }
override fun getInputFilter() = FileBasedIndex.InputFilter() { file -> file.fileType == StdFileTypes.CLASS }
override fun getVersion() = VERSION
private val VERSION = 1
private val VERSION = 2
@Suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
private val kotlinAnnotationsDesc = setOf(
OLD_JET_CLASS_ANNOTATION,
OLD_JET_PACKAGE_CLASS_ANNOTATION,
@@ -48,12 +50,12 @@ public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersion
KOTLIN_FILE_FACADE)
.map { asmDescByFqNameWithoutInnerClasses(it) }
private val INDEXER = DataIndexer<Int, Void, FileContent>() { inputData: FileContent ->
var version: Int? = null
private val INDEXER = DataIndexer<BinaryVersion, Void, FileContent>() { inputData: FileContent ->
var version: BinaryVersion? = null
var annotationPresent = false
tryBlock(inputData) {
val classReader = ClassReader(inputData.getContent())
val classReader = ClassReader(inputData.content)
classReader.accept(object : ClassVisitor(Opcodes.ASM5) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
if (!kotlinAnnotationsDesc.contains(desc)) {
@@ -62,13 +64,12 @@ public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersion
annotationPresent = true
return object : AnnotationVisitor(Opcodes.ASM5) {
override fun visit(name: String, value: Any) {
if (ABI_VERSION_FIELD_NAME == name) {
if (value is Int) {
version = value
when (name) {
VERSION_FIELD_NAME -> if (value is IntArray) {
version = BinaryVersion.create(value)
}
else {
// Version is set to something weird
version = AbiVersionUtil.INVALID_VERSION
OLD_ABI_VERSION_FIELD_NAME -> if (version == null && value is Int) {
version = BinaryVersion.create(0, value, 0)
}
}
}
@@ -78,10 +79,10 @@ public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersion
}
if (annotationPresent && version == null) {
// No version at all: the class is too old
// No version at all because the class is too old, or version is set to something weird
version = AbiVersionUtil.INVALID_VERSION
}
if (version != null) mapOf<Int, Void?>(version!! to null) else mapOf()
if (version != null) mapOf(version!! to null) else mapOf()
}
}
@@ -20,16 +20,37 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.util.indexing.FileContent
import com.intellij.util.indexing.ID
import com.intellij.util.indexing.ScalarIndexExtension
import com.intellij.util.io.ExternalIntegerKeyDescriptor
import com.intellij.util.io.DataInputOutputUtil
import com.intellij.util.io.KeyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import java.io.DataInput
import java.io.DataOutput
/**
* Important! This is not a stub-based index. And it has its own version
*/
abstract class KotlinAbiVersionIndexBase<T>(private val classOfIndex: Class<T>) : ScalarIndexExtension<Int>() {
abstract class KotlinAbiVersionIndexBase<T>(private val classOfIndex: Class<T>) : ScalarIndexExtension<BinaryVersion>() {
override fun getName() = ID.create<Int, Void>(classOfIndex.getCanonicalName())
override fun getName() = ID.create<BinaryVersion, Void>(classOfIndex.canonicalName)
override fun getKeyDescriptor() = ExternalIntegerKeyDescriptor()
override fun getKeyDescriptor(): KeyDescriptor<BinaryVersion> = object : KeyDescriptor<BinaryVersion> {
override fun isEqual(val1: BinaryVersion, val2: BinaryVersion): Boolean = val1 == val2
override fun getHashCode(value: BinaryVersion): Int = value.hashCode()
override fun read(input: DataInput): BinaryVersion {
val size = DataInputOutputUtil.readINT(input)
return BinaryVersion.create((0..size - 1).map { DataInputOutputUtil.readINT(input) }.toIntArray())
}
override fun save(output: DataOutput, value: BinaryVersion) {
val array = value.toArray()
DataInputOutputUtil.writeINT(output, array.size())
for (number in array) {
DataInputOutputUtil.writeINT(output, number)
}
}
}
override fun dependsOnFileContent() = true
@@ -40,7 +61,7 @@ abstract class KotlinAbiVersionIndexBase<T>(private val classOfIndex: Class<T>)
body()
}
catch (e: Throwable) {
LOG.warn("Could not index ABI version for file " + inputData.getFile() + ": " + e.getMessage())
LOG.warn("Could not index ABI version for file " + inputData.file + ": " + e.getMessage())
}
}
}
@@ -16,42 +16,43 @@
package org.jetbrains.kotlin.idea.versions
import com.google.common.collect.Maps
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.util.indexing.DataIndexer
import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.FileContent
import org.jetbrains.kotlin.js.JavaScript
import org.jetbrains.kotlin.load.java.AbiVersionUtil
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadata
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
import java.util.ArrayList
import java.util.*
public object KotlinJavaScriptAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinJavaScriptAbiVersionIndex>(javaClass<KotlinJavaScriptAbiVersionIndex>()) {
public object KotlinJavaScriptAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinJavaScriptAbiVersionIndex>(KotlinJavaScriptAbiVersionIndex::class.java) {
override fun getIndexer() = INDEXER
override fun getInputFilter() = FileBasedIndex.InputFilter() { file -> JavaScript.EXTENSION == file.getExtension() }
override fun getInputFilter() = FileBasedIndex.InputFilter() { file -> JavaScript.EXTENSION == file.extension }
override fun getVersion() = VERSION
private val VERSION = 1
private val VERSION = 2
private val INDEXER = DataIndexer() { inputData: FileContent ->
val result = Maps.newHashMap<Int, Void>()
private val INDEXER = DataIndexer { inputData: FileContent ->
val result = HashMap<BinaryVersion, Void?>()
tryBlock(inputData) {
val text = VfsUtilCore.loadText(inputData.getFile())
val text = VfsUtilCore.loadText(inputData.file)
val metadataList = ArrayList<KotlinJavascriptMetadata>()
KotlinJavascriptMetadataUtils.parseMetadata(text, metadataList)
for (metadata in metadataList) {
if (KotlinJavascriptMetadataUtils.isAbiVersionCompatible(metadata.abiVersion)) {
result.put(metadata.abiVersion, null)
val version = if (KotlinJavascriptMetadataUtils.isAbiVersionCompatible(metadata.abiVersion)) {
BinaryVersion.create(0, metadata.abiVersion, 0)
}
else {
// Version is set to something weird
result.put(AbiVersionUtil.INVALID_VERSION, null)
AbiVersionUtil.INVALID_VERSION
}
result[version] = null
}
}
@@ -50,6 +50,7 @@ import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider;
import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil;
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil;
import org.jetbrains.kotlin.load.java.AbiVersionUtil;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils;
import org.jetbrains.kotlin.utils.KotlinPaths;
import org.jetbrains.kotlin.utils.PathUtil;
@@ -76,10 +77,10 @@ public class KotlinRuntimeLibraryUtil {
return module != null && ProjectStructureUtil.isJavaKotlinModule(module);
}
},
new Function1<Integer, Boolean>() {
new Function1<BinaryVersion, Boolean>() {
@Override
public Boolean invoke(Integer abiVersion) {
return !AbiVersionUtil.isAbiVersionCompatible(abiVersion);
public Boolean invoke(@NotNull BinaryVersion version) {
return !AbiVersionUtil.isAbiVersionCompatible(version);
}
});
}
@@ -94,10 +95,11 @@ public class KotlinRuntimeLibraryUtil {
return module != null && ProjectStructureUtil.isJsKotlinModule(module);
}
},
new Function1<Integer, Boolean>() {
new Function1<BinaryVersion, Boolean>() {
@Override
public Boolean invoke(Integer abiVersion) {
return !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(abiVersion);
public Boolean invoke(@NotNull BinaryVersion version) {
// TODO: support major.minor.patch version in JS metadata
return !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(version.getMinor());
}
});
}
@@ -328,23 +330,23 @@ public class KotlinRuntimeLibraryUtil {
@NotNull
private static Collection<VirtualFile> getLibraryRootsWithAbiIncompatibleVersion(
@NotNull Project project,
@NotNull ScalarIndexExtension<Integer> index,
@NotNull ScalarIndexExtension<BinaryVersion> index,
@NotNull Function1<Module, Boolean> checkModule,
@NotNull Function1<Integer, Boolean> checkAbiVersion
@NotNull Function1<BinaryVersion, Boolean> checkVersion
) {
ID<Integer, Void> id = index.getName();
ID<BinaryVersion, Void> id = index.getName();
Module[] modules = ModuleManager.getInstance(project).getModules();
List<Module> modulesToCheck = KotlinPackage.filter(modules, checkModule);
if (modulesToCheck.isEmpty()) return Collections.emptyList();
Collection<Integer> abiVersions = collectAllKeys(id, modulesToCheck);
Set<Integer> badAbiVersions = Sets.newHashSet(KotlinPackage.filter(abiVersions, checkAbiVersion));
Collection<BinaryVersion> versions = collectAllKeys(id, modulesToCheck);
Set<BinaryVersion> badVersions = Sets.newHashSet(KotlinPackage.filter(versions, checkVersion));
Set<VirtualFile> badRoots = Sets.newHashSet();
ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(project);
for (Integer version : badAbiVersions) {
for (BinaryVersion version : badVersions) {
Collection<VirtualFile> indexedFiles = FileBasedIndex.getInstance().getContainingFiles(
id, version, ProjectScope.getLibrariesScope(project));
@@ -361,12 +363,12 @@ public class KotlinRuntimeLibraryUtil {
}
@NotNull
private static Collection<Integer> collectAllKeys(@NotNull ID<Integer, Void> id, @NotNull List<Module> modules) {
Set<Integer> allKeys = new HashSet<Integer>();
private static <T> Collection<T> collectAllKeys(@NotNull ID<T, Void> id, @NotNull List<Module> modules) {
Set<T> allKeys = new HashSet<T>();
for (Module module : modules) {
GlobalSearchScope scope = GlobalSearchScope.moduleWithLibrariesScope(module);
FileBasedIndex.getInstance().processAllKeys(id, new CommonProcessors.CollectProcessor<Integer>(allKeys), scope, null);
FileBasedIndex.getInstance().processAllKeys(id, new CommonProcessors.CollectProcessor<T>(allKeys), scope, null);
}
return allKeys;
@@ -68,7 +68,11 @@ class CacheFormatVersion(targetDataRoot: File) {
companion object {
// Change this when incremental cache format changes
private val INCREMENTAL_CACHE_OWN_VERSION = 4
private val CACHE_FORMAT_VERSION: Int = INCREMENTAL_CACHE_OWN_VERSION * 1000000 + JvmAbi.VERSION
private val CACHE_FORMAT_VERSION =
INCREMENTAL_CACHE_OWN_VERSION * 1000000 +
JvmAbi.VERSION.major * 1000 +
JvmAbi.VERSION.minor
private val NON_INCREMENTAL_MODE_PSEUDO_VERSION = Int.MAX_VALUE