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.jvm.RuntimeAssertionInfo;
import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.load.java.JavaVisibilities; 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.JvmAnnotationNames;
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor; import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
import org.jetbrains.kotlin.name.FqName; 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.isBoolean;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveClass; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveClass;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isInterface; 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.load.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; 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) { public static void writeKotlinSyntheticClassAnnotation(@NotNull ClassBuilder v, @NotNull KotlinSyntheticClass.Kind kind) {
AnnotationVisitor av = v.newAnnotation(Type.getObjectType(KotlinSyntheticClass.CLASS_NAME.getInternalName()).getDescriptor(), true); 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( av.visitEnum(
JvmAnnotationNames.KIND_FIELD_NAME, JvmAnnotationNames.KIND_FIELD_NAME,
Type.getObjectType(KotlinSyntheticClass.KIND_INTERNAL_NAME).getDescriptor(), Type.getObjectType(KotlinSyntheticClass.KIND_INTERNAL_NAME).getDescriptor(),
@@ -120,10 +120,13 @@ public class ClassFileFactory implements OutputFileCollection {
@Override @Override
public byte[] asBytes(ClassBuilderFactory factory) { public byte[] asBytes(ClassBuilderFactory factory) {
try { try {
ByteArrayOutputStream moduleMapping = new ByteArrayOutputStream(4*1024); ByteArrayOutputStream moduleMapping = new ByteArrayOutputStream(4096);
DataOutputStream dataOutStream = new DataOutputStream(moduleMapping); DataOutputStream dataOutStream = new DataOutputStream(moduleMapping);
dataOutStream.writeInt(1); int[] version = JvmAbi.VERSION.toArray();
dataOutStream.writeInt(JvmAbi.VERSION); dataOutStream.writeInt(version.length);
for (int number : version) {
dataOutStream.writeInt(number);
}
builder.build().writeTo(dataOutStream); builder.build().writeTo(dataOutStream);
dataOutStream.flush(); dataOutStream.flush();
return moduleMapping.toByteArray(); return moduleMapping.toByteArray();
@@ -237,7 +237,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
NameResolver nameResolver = new NameResolver(strings.serializeSimpleNames(), strings.serializeQualifiedNames()); NameResolver nameResolver = new NameResolver(strings.serializeSimpleNames(), strings.serializeQualifiedNames());
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CALLABLE), true); 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); AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(SerializationUtil.serializeCallableData(nameResolver, callableProto))) { for (String string : BitEncoding.encodeBytes(SerializationUtil.serializeCallableData(nameResolver, callableProto))) {
array.visit(null, string); array.visit(null, string);
@@ -264,7 +264,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
ClassData data = new ClassData(nameResolver, classProto); ClassData data = new ClassData(nameResolver, classProto);
AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CLASS), true); AnnotationVisitor av = v.getVisitor().visitAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_CLASS), true);
av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION); JvmCodegenUtil.writeAbiVersion(av);
//noinspection ConstantConditions //noinspection ConstantConditions
if (kind != null) { if (kind != null) {
av.visitEnum( 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.context.RootContext;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.*; 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.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment; import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment;
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass; 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.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import java.io.File; import java.io.File;
@@ -257,4 +260,11 @@ public class JvmCodegenUtil {
public static String getMappingFileName(@NotNull String moduleName) { public static String getMappingFileName(@NotNull String moduleName) {
return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT; 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.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils 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.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -184,7 +183,7 @@ public class MultifileClassCodegen(
if (files.any { it.isScript }) return if (files.any { it.isScript }) return
val av = classBuilder.newAnnotation(AsmUtil.asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_MULTIFILE_CLASS), true) 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 shortNames = partFqNames.map { it.shortName().asString() }.sorted()
val filePartClassNamesArray = av.visitArray(JvmAnnotationNames.FILE_PART_CLASS_NAMES_FIELD_NAME) 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.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile 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 org.jetbrains.org.objectweb.asm.Type
import java.util.* import java.util.*
public class MultifileClassPartCodegen( public class MultifileClassPartCodegen(
v: ClassBuilder, v: ClassBuilder,
file: JetFile, file: JetFile,
@@ -60,12 +58,12 @@ public class MultifileClassPartCodegen(
override fun generateBody() { override fun generateBody() {
for (declaration in element.declarations) { for (declaration in element.declarations) {
if (declaration is JetNamedFunction || declaration is JetProperty) { if (declaration is JetNamedFunction || declaration is JetProperty) {
genFunctionOrProperty(declaration); genFunctionOrProperty(declaration)
} }
} }
if (state.classBuilderMode == ClassBuilderMode.FULL) { if (state.classBuilderMode == ClassBuilderMode.FULL) {
generateInitializers({ createOrGetClInitCodegen(); }); generateInitializers { createOrGetClInitCodegen() }
} }
} }
@@ -100,7 +98,7 @@ public class MultifileClassPartCodegen(
val data = PackageData(nameResolver, packageProto) val data = PackageData(nameResolver, packageProto)
val av = v.newAnnotation(AsmUtil.asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_MULTIFILE_CLASS_PART), true) 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()) av.visit(JvmAnnotationNames.MULTIFILE_CLASS_NAME_FIELD_NAME, multifileClassFqName.shortName().asString())
val dataArray = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME) val dataArray = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME)
for (string in BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) { for (string in BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) {
@@ -301,7 +301,7 @@ public class PackageCodegen {
PackageData data = new PackageData(nameResolver, packageProto); PackageData data = new PackageData(nameResolver, packageProto);
AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true); 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); AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) { for (String string : BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) {
array.visit(null, string); array.visit(null, string);
@@ -16,18 +16,14 @@
package org.jetbrains.kotlin.codegen; package org.jetbrains.kotlin.codegen;
import com.google.common.collect.Lists;
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil;
import kotlin.jvm.functions.Function0; import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext; import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor; import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
import org.jetbrains.kotlin.psi.JetDeclaration; import org.jetbrains.kotlin.psi.JetDeclaration;
import org.jetbrains.kotlin.psi.JetFile; 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 org.jetbrains.org.objectweb.asm.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses; 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.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class PackagePartCodegen extends MemberCodegen<JetFile> { public class PackagePartCodegen extends MemberCodegen<JetFile> {
@@ -124,7 +117,7 @@ public class PackagePartCodegen extends MemberCodegen<JetFile> {
PackageData data = new PackageData(nameResolver, packageProto); PackageData data = new PackageData(nameResolver, packageProto);
AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_FILE_FACADE), true); 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); AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
for (String string : BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) { for (String string : BitEncoding.encodeBytes(SerializationUtil.serializePackageData(data))) {
array.visit(null, string); array.visit(null, string);
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.OverrideResolver import org.jetbrains.kotlin.resolve.OverrideResolver
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
import org.jetbrains.kotlin.util.slicedMap.Slices import org.jetbrains.kotlin.util.slicedMap.Slices
import org.jetbrains.kotlin.util.slicedMap.WritableSlice 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 { public class TraceBasedErrorReporter(private val trace: BindingTrace) : ErrorReporter {
companion object { 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() 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 // 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 data class AbiVersionErrorData(
public val actualVersion: Int, public val actualVersion: BinaryVersion,
public val classId: ClassId 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)) 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; import java.io.IOException;
public class JetFileElementType extends IStubFileElementType<KotlinFileStub> { 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"; 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/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 -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 unknown, expected ABI version is $ABI_VERSION$
compiler/testData/cli/jvm/wrongAbiVersion.kt:4:3: error: unresolved reference: bar compiler/testData/cli/jvm/wrongAbiVersion.kt:4:3: error: unresolved reference: bar
bar() bar()
^ ^
@@ -71,7 +71,7 @@ public class CliBaseTest {
public static String getNormalizedCompilerOutput(@NotNull String pureOutput, @NotNull ExitCode exitCode, @NotNull String testDataDir) { public static String getNormalizedCompilerOutput(@NotNull String pureOutput, @NotNull ExitCode exitCode, @NotNull String testDataDir) {
String normalizedOutputWithoutExitCode = pureOutput String normalizedOutputWithoutExitCode = pureOutput
.replace(new File(testDataDir).getAbsolutePath(), "$TESTDATA_DIR$") .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("\\", "/"); .replace("\\", "/");
return removePerfOutput(normalizedOutputWithoutExitCode) + exitCode; return removePerfOutput(normalizedOutputWithoutExitCode) + exitCode;
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.codegen;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.backend.common.output.OutputFile; import org.jetbrains.kotlin.backend.common.output.OutputFile;
import org.jetbrains.kotlin.load.java.AbiVersionUtil; 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.load.java.JvmAnnotationNames.KotlinSyntheticClass;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
import org.jetbrains.kotlin.test.ConfigurationKind; import org.jetbrains.kotlin.test.ConfigurationKind;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.Collection; import java.util.Collection;
import java.util.List; 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.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.ANONYMOUS_OBJECT;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass.Kind.LOCAL_CLASS; 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.KotlinSyntheticClass.Kind.*;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.VERSION_FIELD_NAME;
public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase { public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
public static final FqName PACKAGE_NAME = new FqName("test"); public static final FqName PACKAGE_NAME = new FqName("test");
@@ -194,10 +194,10 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
Annotation annotation = aClass.getAnnotation(annotationClass); 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); assertNotNull(version);
assertTrue("Annotation " + annotationFqName + " is written with an unsupported format", 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); Object actualKind = CodegenTestUtil.getAnnotationAttribute(annotation, KIND_FIELD_NAME);
assertNotNull(actualKind); assertNotNull(actualKind);
@@ -16,11 +16,16 @@
package org.jetbrains.kotlin.load.java; package org.jetbrains.kotlin.load.java;
public final class AbiVersionUtil { import org.jetbrains.annotations.NotNull;
public static final int INVALID_VERSION = -1; import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
public static boolean isAbiVersionCompatible(int abiVersion) { public final class AbiVersionUtil {
return abiVersion == JvmAbi.VERSION; 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() { private AbiVersionUtil() {
@@ -21,13 +21,20 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
public final class JvmAbi { public final class JvmAbi {
/** /**
* This constant is used to identify binary format (class file) versions * 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_CLASS_NAME = "$TImpl";
public static final String TRAIT_IMPL_SUFFIX = "$" + TRAIT_IMPL_CLASS_NAME; 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 KOTLIN_SIGNATURE = new FqName("kotlin.jvm.KotlinSignature");
public static final FqName OLD_KOTLIN_SIGNATURE = new FqName("jet.runtime.typeinfo.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 KIND_FIELD_NAME = "kind";
public static final String FILE_PART_CLASS_NAMES_FIELD_NAME = "filePartClassNames"; public static final String FILE_PART_CLASS_NAMES_FIELD_NAME = "filePartClassNames";
public static final String MULTIFILE_CLASS_NAME_FIELD_NAME = "multifileClassName"; public static final String MULTIFILE_CLASS_NAME_FIELD_NAME = "multifileClassName";
@@ -114,6 +114,8 @@ public final class JvmAnnotationNames {
@Deprecated @Deprecated
public static final FqName OLD_KOTLIN_TRAIT_IMPL = new FqName("jet.KotlinTraitImpl"); 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 // When these annotations appear on a declaration, they are copied to the _type_ of the declaration, becoming type annotations
// See also DescriptorRendererOptions#excludedTypeAnnotationClasses // See also DescriptorRendererOptions#excludedTypeAnnotationClasses
public static final Set<FqName> ANNOTATIONS_COPIED_TO_TYPES = KotlinPackage.setOf( public static final Set<FqName> ANNOTATIONS_COPIED_TO_TYPES = KotlinPackage.setOf(
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.load.kotlin package org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.load.java.AbiVersionUtil import org.jetbrains.kotlin.load.java.AbiVersionUtil
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.jvm.JvmPackageTable import org.jetbrains.kotlin.serialization.jvm.JvmPackageTable
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.DataInputStream import java.io.DataInputStream
@@ -28,19 +29,27 @@ public class ModuleMapping private constructor(val packageFqName2Parts: Map<Stri
} }
companion object { companion object {
public val MAPPING_FILE_EXT: String = "kotlin_module"; public val MAPPING_FILE_EXT: String = "kotlin_module"
public val EMPTY: ModuleMapping = ModuleMapping(emptyMap()) public val EMPTY: ModuleMapping = ModuleMapping(emptyMap())
fun create(protoWithAbi: ByteArray? = null): ModuleMapping { fun create(proto: ByteArray? = null): ModuleMapping {
if (protoWithAbi == null) { if (proto == null) {
return EMPTY return EMPTY
} }
val inputStream = DataInputStream(ByteArrayInputStream(protoWithAbi)) val inputStream = DataInputStream(ByteArrayInputStream(proto))
val intCount = inputStream.readInt() val size = inputStream.readInt()
assert(intCount == 1, {"Expected one int value for abi version, but: $intCount"})
val abiVersion = Integer.valueOf(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)) { if (AbiVersionUtil.isAbiVersionCompatible(abiVersion)) {
val parseFrom = JvmPackageTable.PackageTable.parseFrom(inputStream) val parseFrom = JvmPackageTable.PackageTable.parseFrom(inputStream)
if (parseFrom != null) { if (parseFrom != null) {
@@ -55,6 +64,7 @@ public class ModuleMapping private constructor(val packageFqName2Parts: Map<Stri
return ModuleMapping(packageFqNameParts) return ModuleMapping(packageFqNameParts)
} }
} }
return EMPTY return EMPTY
} }
} }
@@ -16,13 +16,14 @@
package org.jetbrains.kotlin.load.kotlin.header 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.AbiVersionUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass
public class KotlinClassHeader( public class KotlinClassHeader(
public val kind: KotlinClassHeader.Kind, public val kind: KotlinClassHeader.Kind,
public val version: Int, public val version: BinaryVersion,
public val annotationData: Array<String>?, public val annotationData: Array<String>?,
public val classKind: KotlinClass.Kind?, public val classKind: KotlinClass.Kind?,
public val syntheticClassKind: KotlinSyntheticClass.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.ClassId;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; 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); 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 multifileClassName = null;
private String[] filePartClassNames = null; private String[] filePartClassNames = null;
private String[] annotationData = null; private String[] annotationData = null;
@@ -151,12 +152,18 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
@Override @Override
public void visit(@Nullable Name name, @Nullable Object value) { public void visit(@Nullable Name name, @Nullable Object value) {
if (name != null) { if (name == null) return;
if (name.asString().equals(ABI_VERSION_FIELD_NAME)) {
version = value instanceof Integer ? (Integer) value : AbiVersionUtil.INVALID_VERSION; String string = name.asString();
} if (VERSION_FIELD_NAME.equals(string)) {
else if (name.asString().equals(MULTIFILE_CLASS_NAME_FIELD_NAME)) { version = value instanceof int[] ? BinaryVersion.create((int[]) value) : AbiVersionUtil.INVALID_VERSION;
multifileClassName = value instanceof String ? (String) value : null; }
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.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
public object RuntimeErrorReporter : ErrorReporter { public object RuntimeErrorReporter : ErrorReporter {
@@ -29,8 +29,8 @@ public object RuntimeErrorReporter : ErrorReporter {
throw IllegalStateException("Incomplete hierarchy for class ${descriptor.getName()}, unresolved classes $unresolvedSuperClasses") throw IllegalStateException("Incomplete hierarchy for class ${descriptor.getName()}, unresolved classes $unresolvedSuperClasses")
} }
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: Int) { override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: BinaryVersion) {
throw IllegalStateException("Incompatible ABI version of ${classId}: $actualVersion " + throw IllegalStateException("Incompatible ABI version of $classId: $actualVersion " +
"(expected version is ${JvmAbi.VERSION})") "(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; import java.util.List;
public interface ErrorReporter { 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); void reportIncompleteHierarchy(@NotNull ClassDescriptor descriptor, @NotNull List<String> unresolvedSuperClasses);
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface KotlinCallable { public @interface KotlinCallable {
@Deprecated
int abiVersion(); int abiVersion();
int[] version() default {};
String[] data(); String[] data();
} }
@@ -21,8 +21,11 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface KotlinClass { public @interface KotlinClass {
@Deprecated
int abiVersion(); int abiVersion();
int[] version() default {};
Kind kind() default Kind.CLASS; Kind kind() default Kind.CLASS;
String[] data(); String[] data();
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface KotlinFileFacade { public @interface KotlinFileFacade {
@Deprecated
int abiVersion(); int abiVersion();
int[] version() default {};
String[] data(); String[] data();
} }
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface KotlinMultifileClass { public @interface KotlinMultifileClass {
@Deprecated
int abiVersion(); int abiVersion();
int[] version() default {};
String[] filePartClassNames(); String[] filePartClassNames();
} }
@@ -21,8 +21,12 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface KotlinMultifileClassPart { public @interface KotlinMultifileClassPart {
@Deprecated
int abiVersion(); int abiVersion();
int[] version() default {};
String multifileClassName(); String multifileClassName();
String[] data(); String[] data();
} }
@@ -21,7 +21,10 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface KotlinPackage { public @interface KotlinPackage {
@Deprecated
int abiVersion(); int abiVersion();
int[] version() default {};
String[] data(); String[] data();
} }
@@ -21,8 +21,11 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface KotlinSyntheticClass { public @interface KotlinSyntheticClass {
@Deprecated
int abiVersion(); int abiVersion();
int[] version() default {};
Kind kind(); Kind kind();
enum Kind { enum Kind {
@@ -26,15 +26,18 @@ import org.jetbrains.org.objectweb.asm.*
* Some additional information is printed to stderr * Some additional information is printed to stderr
*/ */
fun loadAbiVersionOfClass(bytes: ByteArray): Int? { fun loadAbiVersionOfClass(bytes: ByteArray): IntArray? {
var result: Int? = null var result: IntArray? = null
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? { override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
if (desc == "Lkotlin/jvm/internal/KotlinClass;") { if (desc == "Lkotlin/jvm/internal/KotlinClass;") {
return object : AnnotationVisitor(Opcodes.ASM5) { return object : AnnotationVisitor(Opcodes.ASM5) {
override fun visit(name: String, value: Any) { override fun visit(name: String, value: Any) {
if (name == "abiVersion") { if (name == "version") {
result = value as Int 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 return result
} }
fun loadVersion(library: File): Int { fun loadVersion(library: File): IntArray {
val jarFile = JarFile(library) val jarFile = JarFile(library)
try { try {
for (entry in jarFile.entries()) { for (entry in jarFile.entries()) {
@@ -71,8 +74,8 @@ fun main(args: Array<String>) {
val library1 = File(args[0]) val library1 = File(args[0])
val library2 = File(args[1]) val library2 = File(args[1])
val v1 = loadVersion(library1) val v1 = loadVersion(library1).joinToString(".")
val v2 = loadVersion(library2) val v2 = loadVersion(library2).joinToString(".")
if (v1 != v2) { if (v1 != v2) {
System.err.println("ABI versions differ:") System.err.println("ABI versions differ:")
@@ -16,11 +16,12 @@
package org.jetbrains.kotlin.idea.decompiler.textBuilder 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 com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor 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 { class LoggingErrorReporter(private val log: Logger) : ErrorReporter {
override fun reportLoadingError(message: String, exception: Exception?) { 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") 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") 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.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses
import org.jetbrains.kotlin.load.java.AbiVersionUtil import org.jetbrains.kotlin.load.java.AbiVersionUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.* 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.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.ClassReader import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassVisitor import org.jetbrains.org.objectweb.asm.ClassVisitor
import org.jetbrains.org.objectweb.asm.Opcodes 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 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 override fun getVersion() = VERSION
private val VERSION = 1 private val VERSION = 2
@Suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
private val kotlinAnnotationsDesc = setOf( private val kotlinAnnotationsDesc = setOf(
OLD_JET_CLASS_ANNOTATION, OLD_JET_CLASS_ANNOTATION,
OLD_JET_PACKAGE_CLASS_ANNOTATION, OLD_JET_PACKAGE_CLASS_ANNOTATION,
@@ -48,12 +50,12 @@ public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersion
KOTLIN_FILE_FACADE) KOTLIN_FILE_FACADE)
.map { asmDescByFqNameWithoutInnerClasses(it) } .map { asmDescByFqNameWithoutInnerClasses(it) }
private val INDEXER = DataIndexer<Int, Void, FileContent>() { inputData: FileContent -> private val INDEXER = DataIndexer<BinaryVersion, Void, FileContent>() { inputData: FileContent ->
var version: Int? = null var version: BinaryVersion? = null
var annotationPresent = false var annotationPresent = false
tryBlock(inputData) { tryBlock(inputData) {
val classReader = ClassReader(inputData.getContent()) val classReader = ClassReader(inputData.content)
classReader.accept(object : ClassVisitor(Opcodes.ASM5) { classReader.accept(object : ClassVisitor(Opcodes.ASM5) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? { override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
if (!kotlinAnnotationsDesc.contains(desc)) { if (!kotlinAnnotationsDesc.contains(desc)) {
@@ -62,13 +64,12 @@ public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersion
annotationPresent = true annotationPresent = true
return object : AnnotationVisitor(Opcodes.ASM5) { return object : AnnotationVisitor(Opcodes.ASM5) {
override fun visit(name: String, value: Any) { override fun visit(name: String, value: Any) {
if (ABI_VERSION_FIELD_NAME == name) { when (name) {
if (value is Int) { VERSION_FIELD_NAME -> if (value is IntArray) {
version = value version = BinaryVersion.create(value)
} }
else { OLD_ABI_VERSION_FIELD_NAME -> if (version == null && value is Int) {
// Version is set to something weird version = BinaryVersion.create(0, value, 0)
version = AbiVersionUtil.INVALID_VERSION
} }
} }
} }
@@ -78,10 +79,10 @@ public object KotlinAbiVersionIndex : KotlinAbiVersionIndexBase<KotlinAbiVersion
} }
if (annotationPresent && version == null) { 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 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.FileContent
import com.intellij.util.indexing.ID import com.intellij.util.indexing.ID
import com.intellij.util.indexing.ScalarIndexExtension 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 * 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 override fun dependsOnFileContent() = true
@@ -40,7 +61,7 @@ abstract class KotlinAbiVersionIndexBase<T>(private val classOfIndex: Class<T>)
body() body()
} }
catch (e: Throwable) { 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 package org.jetbrains.kotlin.idea.versions
import com.google.common.collect.Maps
import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.util.indexing.DataIndexer import com.intellij.util.indexing.DataIndexer
import com.intellij.util.indexing.FileBasedIndex import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.FileContent import com.intellij.util.indexing.FileContent
import org.jetbrains.kotlin.js.JavaScript import org.jetbrains.kotlin.js.JavaScript
import org.jetbrains.kotlin.load.java.AbiVersionUtil 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.KotlinJavascriptMetadata
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils 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 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 override fun getVersion() = VERSION
private val VERSION = 1 private val VERSION = 2
private val INDEXER = DataIndexer() { inputData: FileContent -> private val INDEXER = DataIndexer { inputData: FileContent ->
val result = Maps.newHashMap<Int, Void>() val result = HashMap<BinaryVersion, Void?>()
tryBlock(inputData) { tryBlock(inputData) {
val text = VfsUtilCore.loadText(inputData.getFile()) val text = VfsUtilCore.loadText(inputData.file)
val metadataList = ArrayList<KotlinJavascriptMetadata>() val metadataList = ArrayList<KotlinJavascriptMetadata>()
KotlinJavascriptMetadataUtils.parseMetadata(text, metadataList) KotlinJavascriptMetadataUtils.parseMetadata(text, metadataList)
for (metadata in metadataList) { for (metadata in metadataList) {
if (KotlinJavascriptMetadataUtils.isAbiVersionCompatible(metadata.abiVersion)) { val version = if (KotlinJavascriptMetadataUtils.isAbiVersionCompatible(metadata.abiVersion)) {
result.put(metadata.abiVersion, null) BinaryVersion.create(0, metadata.abiVersion, 0)
} }
else { else {
// Version is set to something weird // 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.framework.LibraryPresentationProviderUtil;
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil; import org.jetbrains.kotlin.idea.project.ProjectStructureUtil;
import org.jetbrains.kotlin.load.java.AbiVersionUtil; 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.KotlinJavascriptMetadataUtils;
import org.jetbrains.kotlin.utils.KotlinPaths; import org.jetbrains.kotlin.utils.KotlinPaths;
import org.jetbrains.kotlin.utils.PathUtil; import org.jetbrains.kotlin.utils.PathUtil;
@@ -76,10 +77,10 @@ public class KotlinRuntimeLibraryUtil {
return module != null && ProjectStructureUtil.isJavaKotlinModule(module); return module != null && ProjectStructureUtil.isJavaKotlinModule(module);
} }
}, },
new Function1<Integer, Boolean>() { new Function1<BinaryVersion, Boolean>() {
@Override @Override
public Boolean invoke(Integer abiVersion) { public Boolean invoke(@NotNull BinaryVersion version) {
return !AbiVersionUtil.isAbiVersionCompatible(abiVersion); return !AbiVersionUtil.isAbiVersionCompatible(version);
} }
}); });
} }
@@ -94,10 +95,11 @@ public class KotlinRuntimeLibraryUtil {
return module != null && ProjectStructureUtil.isJsKotlinModule(module); return module != null && ProjectStructureUtil.isJsKotlinModule(module);
} }
}, },
new Function1<Integer, Boolean>() { new Function1<BinaryVersion, Boolean>() {
@Override @Override
public Boolean invoke(Integer abiVersion) { public Boolean invoke(@NotNull BinaryVersion version) {
return !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(abiVersion); // TODO: support major.minor.patch version in JS metadata
return !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(version.getMinor());
} }
}); });
} }
@@ -328,23 +330,23 @@ public class KotlinRuntimeLibraryUtil {
@NotNull @NotNull
private static Collection<VirtualFile> getLibraryRootsWithAbiIncompatibleVersion( private static Collection<VirtualFile> getLibraryRootsWithAbiIncompatibleVersion(
@NotNull Project project, @NotNull Project project,
@NotNull ScalarIndexExtension<Integer> index, @NotNull ScalarIndexExtension<BinaryVersion> index,
@NotNull Function1<Module, Boolean> checkModule, @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(); Module[] modules = ModuleManager.getInstance(project).getModules();
List<Module> modulesToCheck = KotlinPackage.filter(modules, checkModule); List<Module> modulesToCheck = KotlinPackage.filter(modules, checkModule);
if (modulesToCheck.isEmpty()) return Collections.emptyList(); if (modulesToCheck.isEmpty()) return Collections.emptyList();
Collection<Integer> abiVersions = collectAllKeys(id, modulesToCheck); Collection<BinaryVersion> versions = collectAllKeys(id, modulesToCheck);
Set<Integer> badAbiVersions = Sets.newHashSet(KotlinPackage.filter(abiVersions, checkAbiVersion)); Set<BinaryVersion> badVersions = Sets.newHashSet(KotlinPackage.filter(versions, checkVersion));
Set<VirtualFile> badRoots = Sets.newHashSet(); Set<VirtualFile> badRoots = Sets.newHashSet();
ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(project); ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(project);
for (Integer version : badAbiVersions) { for (BinaryVersion version : badVersions) {
Collection<VirtualFile> indexedFiles = FileBasedIndex.getInstance().getContainingFiles( Collection<VirtualFile> indexedFiles = FileBasedIndex.getInstance().getContainingFiles(
id, version, ProjectScope.getLibrariesScope(project)); id, version, ProjectScope.getLibrariesScope(project));
@@ -361,12 +363,12 @@ public class KotlinRuntimeLibraryUtil {
} }
@NotNull @NotNull
private static Collection<Integer> collectAllKeys(@NotNull ID<Integer, Void> id, @NotNull List<Module> modules) { private static <T> Collection<T> collectAllKeys(@NotNull ID<T, Void> id, @NotNull List<Module> modules) {
Set<Integer> allKeys = new HashSet<Integer>(); Set<T> allKeys = new HashSet<T>();
for (Module module : modules) { for (Module module : modules) {
GlobalSearchScope scope = GlobalSearchScope.moduleWithLibrariesScope(module); 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; return allKeys;
@@ -68,7 +68,11 @@ class CacheFormatVersion(targetDataRoot: File) {
companion object { companion object {
// Change this when incremental cache format changes // Change this when incremental cache format changes
private val INCREMENTAL_CACHE_OWN_VERSION = 4 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 private val NON_INCREMENTAL_MODE_PSEUDO_VERSION = Int.MAX_VALUE