Extract isCompatibleAbiVersion from kinds enum and store read kinds even for incompatible version

This commit is contained in:
Nikolay Krasko
2014-11-06 19:03:48 +03:00
parent 95a2dce627
commit c242665cd3
9 changed files with 70 additions and 37 deletions
@@ -89,15 +89,15 @@ private fun LazyJavaResolverContext.resolveBinaryClass(kotlinClass: KotlinJvmBin
if (kotlinClass == null) return null if (kotlinClass == null) return null
val header = kotlinClass.getClassHeader() val header = kotlinClass.getClassHeader()
if (header.kind == KotlinClassHeader.Kind.CLASS) { if (!header.isCompatibleAbiVersion) {
errorReporter.reportIncompatibleAbiVersion(kotlinClass, header.version)
}
else if (header.kind == KotlinClassHeader.Kind.CLASS) {
val descriptor = packageFragmentProvider.resolveKotlinBinaryClass(kotlinClass) val descriptor = packageFragmentProvider.resolveKotlinBinaryClass(kotlinClass)
if (descriptor != null) { if (descriptor != null) {
return JavaClassLookupResult(kClass = descriptor) return JavaClassLookupResult(kClass = descriptor)
} }
} }
else if (header.kind == KotlinClassHeader.Kind.INCOMPATIBLE_ABI_VERSION) {
errorReporter.reportIncompatibleAbiVersion(kotlinClass, header.version)
}
else { else {
// This is a package or trait-impl or something like that // This is a package or trait-impl or something like that
return JavaClassLookupResult() return JavaClassLookupResult()
@@ -78,7 +78,7 @@ public final class DeserializedDescriptorResolver {
@Nullable @Nullable
public String[] readData(@NotNull KotlinJvmBinaryClass kotlinClass, @NotNull KotlinClassHeader.Kind expectedKind) { public String[] readData(@NotNull KotlinJvmBinaryClass kotlinClass, @NotNull KotlinClassHeader.Kind expectedKind) {
KotlinClassHeader header = kotlinClass.getClassHeader(); KotlinClassHeader header = kotlinClass.getClassHeader();
if (header.getKind() == KotlinClassHeader.Kind.INCOMPATIBLE_ABI_VERSION) { if (!header.getIsCompatibleAbiVersion()) {
errorReporter.reportIncompatibleAbiVersion(kotlinClass, header.getVersion()); errorReporter.reportIncompatibleAbiVersion(kotlinClass, header.getVersion());
} }
else if (header.getKind() == expectedKind) { else if (header.getKind() == expectedKind) {
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve.kotlin.header package org.jetbrains.jet.lang.resolve.kotlin.header
import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass
import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil
public class KotlinClassHeader( public class KotlinClassHeader(
public val kind: KotlinClassHeader.Kind, public val kind: KotlinClassHeader.Kind,
@@ -24,19 +25,24 @@ public class KotlinClassHeader(
public val annotationData: Array<String>?, public val annotationData: Array<String>?,
public val syntheticClassKind: KotlinSyntheticClass.Kind? public val syntheticClassKind: KotlinSyntheticClass.Kind?
) { ) {
public val isCompatibleAbiVersion: Boolean get() = AbiVersionUtil.isAbiVersionCompatible(version);
{ {
assert((annotationData == null) == (kind != Kind.CLASS && kind != Kind.PACKAGE_FACADE)) { assert(!isCompatibleAbiVersion || (annotationData == null) == (kind != Kind.CLASS && kind != Kind.PACKAGE_FACADE)) {
"Annotation data should be not null only for CLASS and PACKAGE_FACADE (kind=" + kind + ")" "Annotation data should be not null only for CLASS and PACKAGE_FACADE (kind=" + kind + ")"
} }
assert((syntheticClassKind == null) == (kind != Kind.SYNTHETIC_CLASS)) { assert(!isCompatibleAbiVersion || (syntheticClassKind == null) == (kind != Kind.SYNTHETIC_CLASS)) {
"Synthetic class kind should be present for SYNTHETIC_CLASS (kind=" + kind + ")" "Synthetic class kind should be present for SYNTHETIC_CLASS (kind=" + kind + ")"
} }
} }
public enum class Kind { public enum class Kind {
INCOMPATIBLE_ABI_VERSION
CLASS CLASS
PACKAGE_FACADE PACKAGE_FACADE
SYNTHETIC_CLASS SYNTHETIC_CLASS
} }
} }
public fun KotlinClassHeader.isCompatibleClassKind(): Boolean = isCompatibleAbiVersion && kind == KotlinClassHeader.Kind.CLASS
public fun KotlinClassHeader.isCompatiblePackageFacadeKind(): Boolean = isCompatibleAbiVersion && kind == KotlinClassHeader.Kind.PACKAGE_FACADE
public fun KotlinClassHeader.isCompatibleSyntheticClassKind(): Boolean = isCompatibleAbiVersion && kind == KotlinClassHeader.Kind.SYNTHETIC_CLASS
@@ -21,7 +21,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil; import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil;
import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.name.ClassId; import org.jetbrains.jet.lang.resolve.name.ClassId;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.name.Name;
import java.util.*; import java.util.*;
@@ -33,21 +32,28 @@ import static org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kin
public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor { public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
private static final Map<JvmClassName, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<JvmClassName, KotlinClassHeader.Kind>(); private static final Map<JvmClassName, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<JvmClassName, KotlinClassHeader.Kind>();
private static final Map<JvmClassName, KotlinClassHeader.Kind> OLD_DEPRECATED_ANNOTATIONS_KINDS = new HashMap<JvmClassName, KotlinClassHeader.Kind>();
private int version = AbiVersionUtil.INVALID_VERSION;
static { static {
HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_CLASS), CLASS); HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_CLASS), CLASS);
HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE), PACKAGE_FACADE); HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE), PACKAGE_FACADE);
HEADER_KINDS.put(KotlinSyntheticClass.CLASS_NAME, SYNTHETIC_CLASS); HEADER_KINDS.put(KotlinSyntheticClass.CLASS_NAME, SYNTHETIC_CLASS);
@SuppressWarnings("deprecation") initOldAnnotations();
List<FqName> incompatible = Arrays.asList(OLD_JET_CLASS_ANNOTATION, OLD_JET_PACKAGE_CLASS_ANNOTATION, OLD_KOTLIN_CLASS, }
OLD_KOTLIN_PACKAGE, OLD_KOTLIN_PACKAGE_FRAGMENT, OLD_KOTLIN_TRAIT_IMPL);
for (FqName fqName : incompatible) { @SuppressWarnings("deprecation")
HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(fqName), INCOMPATIBLE_ABI_VERSION); private static void initOldAnnotations() {
} OLD_DEPRECATED_ANNOTATIONS_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(OLD_JET_CLASS_ANNOTATION), CLASS);
OLD_DEPRECATED_ANNOTATIONS_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(OLD_JET_PACKAGE_CLASS_ANNOTATION),
KotlinClassHeader.Kind.PACKAGE_FACADE);
OLD_DEPRECATED_ANNOTATIONS_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(OLD_KOTLIN_CLASS), CLASS);
OLD_DEPRECATED_ANNOTATIONS_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(OLD_KOTLIN_PACKAGE), PACKAGE_FACADE);
OLD_DEPRECATED_ANNOTATIONS_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(OLD_KOTLIN_PACKAGE_FRAGMENT), SYNTHETIC_CLASS);
OLD_DEPRECATED_ANNOTATIONS_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(OLD_KOTLIN_TRAIT_IMPL), SYNTHETIC_CLASS);
} }
private int version = AbiVersionUtil.INVALID_VERSION;
private String[] annotationData = null; private String[] annotationData = null;
private KotlinClassHeader.Kind headerKind = null; private KotlinClassHeader.Kind headerKind = null;
private KotlinSyntheticClass.Kind syntheticClassKind = null; private KotlinSyntheticClass.Kind syntheticClassKind = null;
@@ -59,7 +65,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
} }
if (!AbiVersionUtil.isAbiVersionCompatible(version)) { if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
return new KotlinClassHeader(INCOMPATIBLE_ABI_VERSION, version, null, null); return new KotlinClassHeader(headerKind, version, null, syntheticClassKind);
} }
if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) { if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) {
@@ -74,22 +80,32 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
@Nullable @Nullable
@Override @Override
public AnnotationArgumentVisitor visitAnnotation(@NotNull ClassId classId) { public AnnotationArgumentVisitor visitAnnotation(@NotNull ClassId classId) {
JvmClassName annotation = JvmClassName.byClassId(classId);
KotlinClassHeader.Kind newKind = HEADER_KINDS.get(annotation);
if (newKind == null) return null;
if (headerKind != null) { if (headerKind != null) {
// Ignore all Kotlin annotations except the first found // Ignore all Kotlin annotations except the first found
return null; return null;
} }
headerKind = newKind; JvmClassName annotation = JvmClassName.byClassId(classId);
if (newKind == CLASS || newKind == PACKAGE_FACADE) { KotlinClassHeader.Kind newKind = HEADER_KINDS.get(annotation);
return kotlinClassOrPackageVisitor(annotation); if (newKind != null) {
headerKind = newKind;
switch (newKind) {
case CLASS:
return kotlinClassOrPackageVisitor(annotation);
case PACKAGE_FACADE:
return kotlinClassOrPackageVisitor(annotation);
case SYNTHETIC_CLASS:
return syntheticClassAnnotationVisitor();
default:
throw new IllegalStateException("Unknown kind: " + newKind);
}
} }
else if (newKind == SYNTHETIC_CLASS) {
return syntheticClassAnnotationVisitor(); KotlinClassHeader.Kind oldAnnotationKind = OLD_DEPRECATED_ANNOTATIONS_KINDS.get(annotation);
if (oldAnnotationKind != null) {
headerKind = oldAnnotationKind;
} }
return null; return null;
@@ -145,6 +161,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
@Override @Override
public void visitEnd() { public void visitEnd() {
//noinspection SSBasedInspection
annotationData = strings.toArray(new String[strings.size()]); annotationData = strings.toArray(new String[strings.size()]);
} }
}; };
@@ -27,6 +27,7 @@ public fun isKotlinCompiledFile(file: VirtualFile): Boolean {
if (isKotlinCompiledFileWithIncompatibleAbiVersion(file)) { if (isKotlinCompiledFileWithIncompatibleAbiVersion(file)) {
return false return false
} }
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file)?.getClassHeader() val header = KotlinBinaryClassCache.getKotlinBinaryClass(file)?.getClassHeader()
return header != null && header.syntheticClassKind != KotlinSyntheticClass.Kind.TRAIT_IMPL return header != null && header.syntheticClassKind != KotlinSyntheticClass.Kind.TRAIT_IMPL
} }
@@ -36,7 +37,7 @@ public fun isKotlinCompiledFileWithIncompatibleAbiVersion(file: VirtualFile): Bo
return false return false
} }
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file)?.getClassHeader() val header = KotlinBinaryClassCache.getKotlinBinaryClass(file)?.getClassHeader()
return header?.kind == KotlinClassHeader.Kind.INCOMPATIBLE_ABI_VERSION return header != null && !header.isCompatibleAbiVersion
} }
public fun isKotlinInternalCompiledFile(file: VirtualFile): Boolean { public fun isKotlinInternalCompiledFile(file: VirtualFile): Boolean {
@@ -40,8 +40,13 @@ public fun buildDecompiledText(
val kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(classFile) val kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(classFile)
assert(kotlinClass != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile } assert(kotlinClass != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
val classId = kotlinClass!!.getClassId() val classId = kotlinClass!!.getClassId()
val classHeader = kotlinClass.getClassHeader()
val kind = kotlinClass.getClassHeader().kind val kind = kotlinClass.getClassHeader().kind
val packageFqName = classId.getPackageFqName() val packageFqName = classId.getPackageFqName()
if (!classHeader.isCompatibleAbiVersion) {
throw UnsupportedOperationException("Illegal operation for incompatibel header")
}
return if (kind == KotlinClassHeader.Kind.PACKAGE_FACADE) { return if (kind == KotlinClassHeader.Kind.PACKAGE_FACADE) {
buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInPackage(packageFqName))) buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInPackage(packageFqName)))
@@ -83,7 +88,7 @@ private fun buildDecompiledText(packageFqName: FqName, descriptors: List<Declara
} }
fun sortDeclarations(input: Collection<DeclarationDescriptor>): List<DeclarationDescriptor> { fun sortDeclarations(input: Collection<DeclarationDescriptor>): List<DeclarationDescriptor> {
val r = ArrayList<DeclarationDescriptor>(input) val r = ArrayList(input)
Collections.sort(r, MemberComparator.INSTANCE) Collections.sort(r, MemberComparator.INSTANCE)
return r return r
} }
@@ -73,7 +73,7 @@ public final class KotlinClassFileIndex extends ScalarIndexExtension<FqName> {
public Map<FqName, Void> map(@NotNull FileContent inputData) { public Map<FqName, Void> map(@NotNull FileContent inputData) {
try { try {
KotlinJvmBinaryClass kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(inputData.getFile()); KotlinJvmBinaryClass kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(inputData.getFile());
if (kotlinClass != null && kotlinClass.getClassHeader().getKind() != KotlinClassHeader.Kind.INCOMPATIBLE_ABI_VERSION) { if (kotlinClass != null && kotlinClass.getClassHeader().getIsCompatibleAbiVersion()) {
return Collections.singletonMap(kotlinClass.getClassId().asSingleFqName().toSafe(), null); return Collections.singletonMap(kotlinClass.getClassId().asSingleFqName().toSafe(), null);
} }
} }
@@ -42,6 +42,8 @@ import org.jetbrains.jps.builders.storage.StorageProvider
import java.io.IOException import java.io.IOException
import java.util.Scanner import java.util.Scanner
import org.jetbrains.jet.lang.resolve.java.JvmAbi import org.jetbrains.jet.lang.resolve.java.JvmAbi
import org.jetbrains.jet.lang.resolve.kotlin.header.isCompatiblePackageFacadeKind
import org.jetbrains.jet.lang.resolve.kotlin.header.isCompatibleClassKind
val INLINE_ANNOTATION_DESC = "Lkotlin/inline;" val INLINE_ANNOTATION_DESC = "Lkotlin/inline;"
@@ -106,11 +108,11 @@ public class IncrementalCacheImpl(val baseDir: File): StorageOwner, IncrementalC
val annotationDataEncoded = header.annotationData val annotationDataEncoded = header.annotationData
if (annotationDataEncoded != null) { if (annotationDataEncoded != null) {
val data = BitEncoding.decodeBytes(annotationDataEncoded) val data = BitEncoding.decodeBytes(annotationDataEncoded)
when (header.kind) { when {
KotlinClassHeader.Kind.PACKAGE_FACADE -> { header.isCompatiblePackageFacadeKind() -> {
return if (protoMap.put(className, data)) COMPILE_OTHERS else DO_NOTHING return if (protoMap.put(className, data)) COMPILE_OTHERS else DO_NOTHING
} }
KotlinClassHeader.Kind.CLASS -> { header.isCompatibleClassKind() -> {
val inlinesChanged = inlineFunctionsMap.process(className, fileBytes) val inlinesChanged = inlineFunctionsMap.process(className, fileBytes)
val protoChanged = protoMap.put(className, data) val protoChanged = protoMap.put(className, data)
val constantsChanged = constantsMap.process(className, fileBytes) val constantsChanged = constantsMap.process(className, fileBytes)
@@ -118,7 +120,7 @@ public class IncrementalCacheImpl(val baseDir: File): StorageOwner, IncrementalC
return if (inlinesChanged) RECOMPILE_ALL else if (protoChanged || constantsChanged) COMPILE_OTHERS else DO_NOTHING return if (inlinesChanged) RECOMPILE_ALL else if (protoChanged || constantsChanged) COMPILE_OTHERS else DO_NOTHING
} }
else -> { else -> {
throw IllegalStateException("Unexpected kind with annotationData: ${header.kind}") throw IllegalStateException("Unexpected kind with annotationData: ${header.kind}, isCompatible: ${header.isCompatibleAbiVersion}")
} }
} }
} }
@@ -36,6 +36,8 @@ import java.io.ByteArrayInputStream
import org.jetbrains.jet.descriptors.serialization.DebugProtoBuf import org.jetbrains.jet.descriptors.serialization.DebugProtoBuf
import java.util.Arrays import java.util.Arrays
import org.jetbrains.jet.jps.incremental.LocalFileKotlinClass import org.jetbrains.jet.jps.incremental.LocalFileKotlinClass
import org.jetbrains.jet.lang.resolve.kotlin.header.isCompatibleClassKind
import org.jetbrains.jet.lang.resolve.kotlin.header.isCompatiblePackageFacadeKind
// Set this to true if you want to dump all bytecode (test will fail in this case) // Set this to true if you want to dump all bytecode (test will fail in this case)
val DUMP_ALL = System.getProperty("comparison.dump.all") == "true" val DUMP_ALL = System.getProperty("comparison.dump.all") == "true"
@@ -140,11 +142,11 @@ fun classFileToString(classFile: File): String {
out.write("\n------ simpleNames proto -----\n${DebugProtoBuf.StringTable.parseDelimitedFrom(input)}") out.write("\n------ simpleNames proto -----\n${DebugProtoBuf.StringTable.parseDelimitedFrom(input)}")
out.write("\n------ qualifiedNames proto -----\n${DebugProtoBuf.QualifiedNameTable.parseDelimitedFrom(input)}") out.write("\n------ qualifiedNames proto -----\n${DebugProtoBuf.QualifiedNameTable.parseDelimitedFrom(input)}")
when (classHeader!!.kind) { when {
KotlinClassHeader.Kind.PACKAGE_FACADE -> classHeader!!.isCompatiblePackageFacadeKind() ->
out.write("\n------ package proto -----\n${DebugProtoBuf.Package.parseFrom(input, getExtensionRegistry())}") out.write("\n------ package proto -----\n${DebugProtoBuf.Package.parseFrom(input, getExtensionRegistry())}")
KotlinClassHeader.Kind.CLASS -> classHeader.isCompatibleClassKind() ->
out.write("\n------ class proto -----\n${DebugProtoBuf.Class.parseFrom(input, getExtensionRegistry())}") out.write("\n------ class proto -----\n${DebugProtoBuf.Class.parseFrom(input, getExtensionRegistry())}")
else -> throw IllegalStateException() else -> throw IllegalStateException()