Split JVM binary version into two: metadata and bytecode interface

Currently all code only uses the first one (JvmMetadataVersion), later the
bytecode interface version (JvmBytecodeBinaryVersion) will be used only in
codegen and reflection to avoid compiling against or calling methods with
unsupported conventions like default method naming and signature,
getters/setters naming etc.
This commit is contained in:
Alexander Udalov
2015-12-28 20:01:00 +03:00
parent fc88a0186f
commit bd47e9d47b
13 changed files with 136 additions and 79 deletions
@@ -18,6 +18,10 @@ package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
/**
* The version of conventions used in bytecode of generated .class files, such as default method naming & signatures,
* internal member name mangling specifics, property getter/setter names, etc.
*/
class JvmBytecodeBinaryVersion protected constructor( class JvmBytecodeBinaryVersion protected constructor(
major: Int, minor: Int, patch: Int, rest: List<Int> major: Int, minor: Int, patch: Int, rest: List<Int>
) : BinaryVersion(major, minor, patch, rest) { ) : BinaryVersion(major, minor, patch, rest) {
@@ -70,8 +70,8 @@ fun LazyJavaResolverContext.resolveKotlinBinaryClass(kotlinClass: KotlinJvmBinar
val header = kotlinClass.classHeader val header = kotlinClass.classHeader
return when { return when {
!header.version.isCompatible() -> { !header.metadataVersion.isCompatible() -> {
components.errorReporter.reportIncompatibleAbiVersion(kotlinClass.classId, kotlinClass.location, header.version) components.errorReporter.reportIncompatibleAbiVersion(kotlinClass.classId, kotlinClass.location, header.metadataVersion)
KotlinClassLookupResult.NotFound KotlinClassLookupResult.NotFound
} }
header.kind == KotlinClassHeader.Kind.CLASS -> { header.kind == KotlinClassHeader.Kind.CLASS -> {
@@ -115,8 +115,8 @@ public final class DeserializedDescriptorResolver {
@Nullable @Nullable
public String[] readData(@NotNull KotlinJvmBinaryClass kotlinClass, @NotNull Set<KotlinClassHeader.Kind> expectedKinds) { public String[] readData(@NotNull KotlinJvmBinaryClass kotlinClass, @NotNull Set<KotlinClassHeader.Kind> expectedKinds) {
KotlinClassHeader header = kotlinClass.getClassHeader(); KotlinClassHeader header = kotlinClass.getClassHeader();
if (!header.getVersion().isCompatible()) { if (!header.getMetadataVersion().isCompatible()) {
errorReporter.reportIncompatibleAbiVersion(kotlinClass.getClassId(), kotlinClass.getLocation(), header.getVersion()); errorReporter.reportIncompatibleAbiVersion(kotlinClass.getClassId(), kotlinClass.getLocation(), header.getMetadataVersion());
} }
else if (expectedKinds.contains(header.getKind())) { else if (expectedKinds.contains(header.getKind())) {
return header.getAnnotationData(); return header.getAnnotationData();
@@ -0,0 +1,43 @@
/*
* 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.load.kotlin
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
/**
* The version of the metadata serialized by the compiler and deserialized by the compiler and reflection.
* This version includes the version of the core protobuf messages (descriptors.proto) as well as JVM extensions (jvm_descriptors.proto).
*/
class JvmMetadataVersion protected constructor(
major: Int, minor: Int, patch: Int, rest: List<Int>
) : BinaryVersion(major, minor, patch, rest) {
override fun isCompatible() = this.isCompatibleTo(INSTANCE)
companion object {
@JvmField
val INSTANCE = create(1, 0, 2)
@JvmField
val INVALID_VERSION = JvmMetadataVersion.create(IntArray(0))
@JvmStatic
fun create(version: IntArray) = create(version, ::JvmMetadataVersion)
@JvmStatic
fun create(major: Int, minor: Int, patch: Int) = create(major, minor, patch, ::JvmMetadataVersion)
}
}
@@ -16,11 +16,13 @@
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.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
class KotlinClassHeader( class KotlinClassHeader(
val kind: KotlinClassHeader.Kind, val kind: KotlinClassHeader.Kind,
val version: BinaryVersion, val metadataVersion: JvmMetadataVersion,
val bytecodeVersion: JvmBytecodeBinaryVersion,
val annotationData: Array<String>?, val annotationData: Array<String>?,
val strings: Array<String>?, val strings: Array<String>?,
val filePartClassNames: Array<String>?, val filePartClassNames: Array<String>?,
@@ -36,10 +38,5 @@ class KotlinClassHeader(
SYNTHETIC_CLASS SYNTHETIC_CLASS
} }
override fun toString() = "$kind " + (if (isLocalClass) "(local) " else "") + "version=$version" override fun toString() = "$kind " + (if (isLocalClass) "(local) " else "") + "version=$metadataVersion"
} }
fun KotlinClassHeader.isCompatibleClassKind(): Boolean = version.isCompatible() && kind == KotlinClassHeader.Kind.CLASS
fun KotlinClassHeader.isCompatibleFileFacadeKind(): Boolean = version.isCompatible() && kind == KotlinClassHeader.Kind.FILE_FACADE
fun KotlinClassHeader.isCompatibleMultifileClassKind(): Boolean = version.isCompatible() && kind == KotlinClassHeader.Kind.MULTIFILE_CLASS
fun KotlinClassHeader.isCompatibleMultifileClassPartKind(): Boolean = version.isCompatible() && kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
@@ -20,10 +20,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.SourceElement; import org.jetbrains.kotlin.descriptors.SourceElement;
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion; import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion;
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
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;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -45,7 +45,8 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
HEADER_KINDS.put(ClassId.topLevel(KOTLIN_SYNTHETIC_CLASS), SYNTHETIC_CLASS); HEADER_KINDS.put(ClassId.topLevel(KOTLIN_SYNTHETIC_CLASS), SYNTHETIC_CLASS);
} }
private BinaryVersion version = JvmBytecodeBinaryVersion.INVALID_VERSION; private JvmMetadataVersion metadataVersion = null;
private JvmBytecodeBinaryVersion bytecodeVersion = null;
private String multifileClassName = null; private String multifileClassName = null;
private String[] filePartClassNames = null; private String[] filePartClassNames = null;
private String[] annotationData = null; private String[] annotationData = null;
@@ -60,7 +61,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
return null; return null;
} }
if (!version.isCompatible()) { if (metadataVersion == null || !metadataVersion.isCompatible()) {
annotationData = null; annotationData = null;
} }
else if (shouldHaveData() && annotationData == null) { else if (shouldHaveData() && annotationData == null) {
@@ -70,7 +71,10 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
} }
return new KotlinClassHeader( return new KotlinClassHeader(
headerKind, version, annotationData, strings, filePartClassNames, multifileClassName, isInterfaceDefaultImpls, isLocalClass headerKind,
metadataVersion != null ? metadataVersion : JvmMetadataVersion.INVALID_VERSION,
bytecodeVersion != null ? bytecodeVersion : JvmBytecodeBinaryVersion.INVALID_VERSION,
annotationData, strings, filePartClassNames, multifileClassName, isInterfaceDefaultImpls, isLocalClass
); );
} }
@@ -118,7 +122,14 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
String string = name.asString(); String string = name.asString();
if (VERSION_FIELD_NAME.equals(string)) { if (VERSION_FIELD_NAME.equals(string)) {
version = value instanceof int[] ? JvmBytecodeBinaryVersion.create((int[]) value) : JvmBytecodeBinaryVersion.INVALID_VERSION; if (value instanceof int[]) {
metadataVersion = JvmMetadataVersion.create((int[]) value);
// If there's no bytecode binary version in the class file, we assume it to be equal to the metadata version
if (bytecodeVersion == null) {
bytecodeVersion = JvmBytecodeBinaryVersion.create((int[]) value);
}
}
} }
else if (MULTIFILE_CLASS_NAME_FIELD_NAME.equals(string)) { else if (MULTIFILE_CLASS_NAME_FIELD_NAME.equals(string)) {
multifileClassName = value instanceof String ? (String) value : null; multifileClassName = value instanceof String ? (String) value : null;
@@ -54,7 +54,7 @@ fun isKotlinWithCompatibleAbiVersion(file: VirtualFile): Boolean {
if (!isKotlinJvmCompiledFile(file)) return false if (!isKotlinJvmCompiledFile(file)) return false
val kotlinClass = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file) val kotlinClass = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)
return kotlinClass != null && kotlinClass.classHeader.version.isCompatible() return kotlinClass != null && kotlinClass.classHeader.metadataVersion.isCompatible()
} }
/** /**
@@ -29,9 +29,7 @@ import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolverForDecompiler
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.types.flexibility import org.jetbrains.kotlin.types.flexibility
import org.jetbrains.kotlin.types.isFlexible import org.jetbrains.kotlin.types.isFlexible
@@ -79,30 +77,33 @@ fun buildDecompiledTextForClassFile(
classFile: VirtualFile, classFile: VirtualFile,
resolver: ResolverForDecompiler = DeserializerForClassfileDecompiler(classFile) resolver: ResolverForDecompiler = DeserializerForClassfileDecompiler(classFile)
): DecompiledText { ): DecompiledText {
val kotlinClassHeaderInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile) val (classHeader, classId) = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile)
assert(kotlinClassHeaderInfo != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile } ?: error("Decompiled data factory shouldn't be called on an unsupported file: " + classFile)
val classId = kotlinClassHeaderInfo!!.classId
val classHeader = kotlinClassHeaderInfo.classHeader
val packageFqName = classId.packageFqName
return when { if (!classHeader.metadataVersion.isCompatible()) {
!classHeader.version.isCompatible() -> { return DecompiledText(
DecompiledText( INCOMPATIBLE_ABI_VERSION_COMMENT
INCOMPATIBLE_ABI_VERSION_COMMENT .replace(CURRENT_ABI_VERSION_MARKER, JvmBytecodeBinaryVersion.INSTANCE.toString())
.replace(CURRENT_ABI_VERSION_MARKER, JvmBytecodeBinaryVersion.INSTANCE.toString()) .replace(FILE_ABI_VERSION_MARKER, classHeader.metadataVersion.toString()),
.replace(FILE_ABI_VERSION_MARKER, classHeader.version.toString()), mapOf()
mapOf()) )
} }
classHeader.isCompatibleFileFacadeKind() ->
buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInFacade(classId.asSingleFqName())), decompilerRendererForClassFiles) return when (classHeader.kind) {
classHeader.isCompatibleClassKind() -> KotlinClassHeader.Kind.FILE_FACADE ->
buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), decompilerRendererForClassFiles) buildDecompiledText(classId.packageFqName, ArrayList(resolver.resolveDeclarationsInFacade(classId.asSingleFqName())),
classHeader.isCompatibleMultifileClassKind() -> { decompilerRendererForClassFiles)
KotlinClassHeader.Kind.CLASS ->
buildDecompiledText(classId.packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)),
decompilerRendererForClassFiles)
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
val partClasses = findMultifileClassParts(classFile, classId, classHeader) val partClasses = findMultifileClassParts(classFile, classId, classHeader)
val partMembers = partClasses.flatMap { partClass -> resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName()) } val partMembers = partClasses.flatMap { partClass ->
buildDecompiledText(packageFqName, partMembers, decompilerRendererForClassFiles) resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName())
}
buildDecompiledText(classId.packageFqName, partMembers, decompilerRendererForClassFiles)
} }
else -> else ->
throw UnsupportedOperationException("Unknown header kind: ${classHeader.kind} ${classHeader.version.isCompatible()}") throw UnsupportedOperationException("Unknown header kind: $classHeader, class $classId")
} }
} }
@@ -30,9 +30,7 @@ import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
import org.jetbrains.kotlin.load.kotlin.AbstractBinaryClassAnnotationAndConstantLoader import org.jetbrains.kotlin.load.kotlin.AbstractBinaryClassAnnotationAndConstantLoader
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind
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.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
@@ -62,12 +60,12 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
val header = kotlinClassHeaderInfo.classHeader val header = kotlinClassHeaderInfo.classHeader
val classId = kotlinClassHeaderInfo.classId val classId = kotlinClassHeaderInfo.classId
val packageFqName = classId.packageFqName val packageFqName = classId.packageFqName
if (!header.version.isCompatible()) { if (!header.metadataVersion.isCompatible()) {
return createIncompatibleAbiVersionFileStub() return createIncompatibleAbiVersionFileStub()
} }
val components = createStubBuilderComponents(file, packageFqName) val components = createStubBuilderComponents(file, packageFqName)
if (header.isCompatibleMultifileClassKind()) { if (header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
val partFiles = findMultifileClassParts(file, classId, header) val partFiles = findMultifileClassParts(file, classId, header)
return createMultifileClassStub(header, partFiles, classId.asSingleFqName(), components) return createMultifileClassStub(header, partFiles, classId.asSingleFqName(), components)
} }
@@ -82,14 +80,14 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
LOG.error("String table not found in file ${file.name}") LOG.error("String table not found in file ${file.name}")
return null return null
} }
return when { return when (header.kind) {
header.isCompatibleClassKind() -> { KotlinClassHeader.Kind.CLASS -> {
if (header.isLocalClass) return null if (header.isLocalClass) return null
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(annotationData, strings) val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(annotationData, strings)
val context = components.createContext(nameResolver, packageFqName, TypeTable(classProto.typeTable)) val context = components.createContext(nameResolver, packageFqName, TypeTable(classProto.typeTable))
createTopLevelClassStub(classId, classProto, context) createTopLevelClassStub(classId, classProto, context)
} }
header.isCompatibleFileFacadeKind() -> { KotlinClassHeader.Kind.FILE_FACADE -> {
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(annotationData, strings) val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(annotationData, strings)
val context = components.createContext(nameResolver, packageFqName, TypeTable(packageProto.typeTable)) val context = components.createContext(nameResolver, packageFqName, TypeTable(packageProto.typeTable))
createFileFacadeStub(packageProto, classId.asSingleFqName(), context) createFileFacadeStub(packageProto, classId.asSingleFqName(), context)
@@ -79,7 +79,7 @@ object KotlinClassFileIndex : KotlinFileIndexBase<KotlinClassFileIndex>(KotlinCl
private val INDEXER = indexer() { fileContent -> private val INDEXER = indexer() { fileContent ->
val headerInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(fileContent.file, fileContent.content) val headerInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(fileContent.file, fileContent.content)
if (headerInfo != null && headerInfo.classHeader.version.isCompatible()) headerInfo.classId.asSingleFqName() else null if (headerInfo != null && headerInfo.classHeader.metadataVersion.isCompatible()) headerInfo.classId.asSingleFqName() else null
} }
} }
@@ -30,10 +30,7 @@ import org.jetbrains.kotlin.jps.build.GeneratedJvmClass
import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.jps.incremental.storage.* import org.jetbrains.kotlin.jps.incremental.storage.*
import org.jetbrains.kotlin.load.kotlin.ModuleMapping import org.jetbrains.kotlin.load.kotlin.ModuleMapping
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassPartKind
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -147,8 +144,12 @@ open class IncrementalCacheImpl(
} }
val header = kotlinClass.classHeader val header = kotlinClass.classHeader
val changesInfo = when { if (header.isLocalClass) {
header.isCompatibleFileFacadeKind() -> { return CompilationResult.NO_CHANGES
}
val changesInfo = when (header.kind) {
KotlinClassHeader.Kind.FILE_FACADE -> {
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" } assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
packagePartMap.addPackagePart(className) packagePartMap.addPackagePart(className)
@@ -156,7 +157,7 @@ open class IncrementalCacheImpl(
constantsMap.process(kotlinClass) + constantsMap.process(kotlinClass) +
additionalProcessChangedClass(kotlinClass, isPackage = true) additionalProcessChangedClass(kotlinClass, isPackage = true)
} }
header.isCompatibleMultifileClassKind() -> { KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
val partNames = kotlinClass.classHeader.filePartClassNames?.toList() val partNames = kotlinClass.classHeader.filePartClassNames?.toList()
?: throw AssertionError("Multifile class has no parts: ${kotlinClass.className}") ?: throw AssertionError("Multifile class has no parts: ${kotlinClass.className}")
multifileClassFacadeMap.add(className, partNames) multifileClassFacadeMap.add(className, partNames)
@@ -165,7 +166,7 @@ open class IncrementalCacheImpl(
constantsMap.process(kotlinClass) + constantsMap.process(kotlinClass) +
additionalProcessChangedClass(kotlinClass, isPackage = true) additionalProcessChangedClass(kotlinClass, isPackage = true)
} }
header.isCompatibleMultifileClassPartKind() -> { KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" } assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
packagePartMap.addPackagePart(className) packagePartMap.addPackagePart(className)
multifileClassPartMap.add(className.internalName, header.multifileClassName!!) multifileClassPartMap.add(className.internalName, header.multifileClassName!!)
@@ -174,7 +175,7 @@ open class IncrementalCacheImpl(
constantsMap.process(kotlinClass) + constantsMap.process(kotlinClass) +
additionalProcessChangedClass(kotlinClass, isPackage = true) additionalProcessChangedClass(kotlinClass, isPackage = true)
} }
header.isCompatibleClassKind() && !header.isLocalClass -> { KotlinClassHeader.Kind.CLASS -> {
addToClassStorage(kotlinClass) addToClassStorage(kotlinClass)
protoMap.process(kotlinClass, isPackage = false) + protoMap.process(kotlinClass, isPackage = false) +
@@ -22,9 +22,7 @@ import com.google.common.io.Files
import com.google.protobuf.ExtensionRegistry import com.google.protobuf.ExtensionRegistry
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.jps.incremental.LocalFileKotlinClass import org.jetbrains.kotlin.jps.incremental.LocalFileKotlinClass
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassPartKind
import org.jetbrains.kotlin.serialization.DebugProtoBuf import org.jetbrains.kotlin.serialization.DebugProtoBuf
import org.jetbrains.kotlin.serialization.jvm.BitEncoding import org.jetbrains.kotlin.serialization.jvm.BitEncoding
import org.jetbrains.kotlin.serialization.jvm.DebugJvmProtoBuf import org.jetbrains.kotlin.serialization.jvm.DebugJvmProtoBuf
@@ -141,14 +139,17 @@ fun classFileToString(classFile: File): String {
out.write("\n------ string table types proto -----\n${DebugJvmProtoBuf.StringTableTypes.parseDelimitedFrom(input)}") out.write("\n------ string table types proto -----\n${DebugJvmProtoBuf.StringTableTypes.parseDelimitedFrom(input)}")
when { if (!classHeader!!.metadataVersion.isCompatible()) {
classHeader!!.isCompatibleFileFacadeKind() -> error("Incompatible class ($classHeader): $classFile")
out.write("\n------ file facade proto -----\n${DebugProtoBuf.Package.parseFrom(input, getExtensionRegistry())}") }
classHeader.isCompatibleClassKind() ->
out.write("\n------ class proto -----\n${DebugProtoBuf.Class.parseFrom(input, getExtensionRegistry())}")
classHeader.isCompatibleMultifileClassPartKind() ->
out.write("\n------ multi-file part proto -----\n${DebugProtoBuf.Package.parseFrom(input, getExtensionRegistry())}")
when (classHeader.kind) {
KotlinClassHeader.Kind.FILE_FACADE ->
out.write("\n------ file facade proto -----\n${DebugProtoBuf.Package.parseFrom(input, getExtensionRegistry())}")
KotlinClassHeader.Kind.CLASS ->
out.write("\n------ class proto -----\n${DebugProtoBuf.Class.parseFrom(input, getExtensionRegistry())}")
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART ->
out.write("\n------ multi-file part proto -----\n${DebugProtoBuf.Package.parseFrom(input, getExtensionRegistry())}")
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
} }
@@ -20,9 +20,7 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.UsefulTestCase
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.jps.incremental.storage.ProtoMapValue import org.jetbrains.kotlin.jps.incremental.storage.ProtoMapValue
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassPartKind
import org.jetbrains.kotlin.serialization.jvm.BitEncoding import org.jetbrains.kotlin.serialization.jvm.BitEncoding
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.MockLibraryUtil import org.jetbrains.kotlin.test.MockLibraryUtil
@@ -93,19 +91,22 @@ abstract class AbstractProtoComparisonTest : UsefulTestCase() {
val oldProtoBytes = BitEncoding.decodeBytes(oldClassHeader.annotationData!!) val oldProtoBytes = BitEncoding.decodeBytes(oldClassHeader.annotationData!!)
val newProtoBytes = BitEncoding.decodeBytes(newClassHeader.annotationData!!) val newProtoBytes = BitEncoding.decodeBytes(newClassHeader.annotationData!!)
assert(oldClassHeader.metadataVersion.isCompatible()) { "Incompatible class ($oldClassHeader): $oldClassFile" }
assert(newClassHeader.metadataVersion.isCompatible()) { "Incompatible class ($newClassHeader): $newClassFile" }
val oldProto = ProtoMapValue( val oldProto = ProtoMapValue(
oldClassHeader.isCompatibleFileFacadeKind() || oldClassHeader.isCompatibleMultifileClassPartKind(), oldClassHeader.kind == KotlinClassHeader.Kind.FILE_FACADE ||
oldClassHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART,
oldProtoBytes, oldClassHeader.strings!! oldProtoBytes, oldClassHeader.strings!!
) )
val newProto = ProtoMapValue( val newProto = ProtoMapValue(
newClassHeader.isCompatibleFileFacadeKind() || newClassHeader.isCompatibleMultifileClassPartKind(), newClassHeader.kind == KotlinClassHeader.Kind.FILE_FACADE ||
newClassHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART,
newProtoBytes, newClassHeader.strings!! newProtoBytes, newClassHeader.strings!!
) )
val diff = when { val diff = when (newClassHeader.kind) {
newClassHeader.isCompatibleClassKind() || KotlinClassHeader.Kind.CLASS, KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART ->
newClassHeader.isCompatibleFileFacadeKind() ||
newClassHeader.isCompatibleMultifileClassPartKind() ->
difference(oldProto, newProto) difference(oldProto, newProto)
else -> { else -> {
println("ignore ${oldLocalFileKotlinClass.classId}") println("ignore ${oldLocalFileKotlinClass.classId}")