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
@@ -54,7 +54,7 @@ fun isKotlinWithCompatibleAbiVersion(file: VirtualFile): Boolean {
if (!isKotlinJvmCompiledFile(file)) return false
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.defaultDecompilerRendererOptions
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.types.flexibility
import org.jetbrains.kotlin.types.isFlexible
@@ -79,30 +77,33 @@ fun buildDecompiledTextForClassFile(
classFile: VirtualFile,
resolver: ResolverForDecompiler = DeserializerForClassfileDecompiler(classFile)
): DecompiledText {
val kotlinClassHeaderInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile)
assert(kotlinClassHeaderInfo != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
val classId = kotlinClassHeaderInfo!!.classId
val classHeader = kotlinClassHeaderInfo.classHeader
val packageFqName = classId.packageFqName
val (classHeader, classId) = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile)
?: error("Decompiled data factory shouldn't be called on an unsupported file: " + classFile)
return when {
!classHeader.version.isCompatible() -> {
DecompiledText(
INCOMPATIBLE_ABI_VERSION_COMMENT
.replace(CURRENT_ABI_VERSION_MARKER, JvmBytecodeBinaryVersion.INSTANCE.toString())
.replace(FILE_ABI_VERSION_MARKER, classHeader.version.toString()),
mapOf())
}
classHeader.isCompatibleFileFacadeKind() ->
buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInFacade(classId.asSingleFqName())), decompilerRendererForClassFiles)
classHeader.isCompatibleClassKind() ->
buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), decompilerRendererForClassFiles)
classHeader.isCompatibleMultifileClassKind() -> {
if (!classHeader.metadataVersion.isCompatible()) {
return DecompiledText(
INCOMPATIBLE_ABI_VERSION_COMMENT
.replace(CURRENT_ABI_VERSION_MARKER, JvmBytecodeBinaryVersion.INSTANCE.toString())
.replace(FILE_ABI_VERSION_MARKER, classHeader.metadataVersion.toString()),
mapOf()
)
}
return when (classHeader.kind) {
KotlinClassHeader.Kind.FILE_FACADE ->
buildDecompiledText(classId.packageFqName, ArrayList(resolver.resolveDeclarationsInFacade(classId.asSingleFqName())),
decompilerRendererForClassFiles)
KotlinClassHeader.Kind.CLASS ->
buildDecompiledText(classId.packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)),
decompilerRendererForClassFiles)
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
val partClasses = findMultifileClassParts(classFile, classId, classHeader)
val partMembers = partClasses.flatMap { partClass -> resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName()) }
buildDecompiledText(packageFqName, partMembers, decompilerRendererForClassFiles)
val partMembers = partClasses.flatMap { partClass ->
resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName())
}
buildDecompiledText(classId.packageFqName, partMembers, decompilerRendererForClassFiles)
}
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.KotlinClassFinder
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
@@ -62,12 +60,12 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
val header = kotlinClassHeaderInfo.classHeader
val classId = kotlinClassHeaderInfo.classId
val packageFqName = classId.packageFqName
if (!header.version.isCompatible()) {
if (!header.metadataVersion.isCompatible()) {
return createIncompatibleAbiVersionFileStub()
}
val components = createStubBuilderComponents(file, packageFqName)
if (header.isCompatibleMultifileClassKind()) {
if (header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
val partFiles = findMultifileClassParts(file, classId, header)
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}")
return null
}
return when {
header.isCompatibleClassKind() -> {
return when (header.kind) {
KotlinClassHeader.Kind.CLASS -> {
if (header.isLocalClass) return null
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(annotationData, strings)
val context = components.createContext(nameResolver, packageFqName, TypeTable(classProto.typeTable))
createTopLevelClassStub(classId, classProto, context)
}
header.isCompatibleFileFacadeKind() -> {
KotlinClassHeader.Kind.FILE_FACADE -> {
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(annotationData, strings)
val context = components.createContext(nameResolver, packageFqName, TypeTable(packageProto.typeTable))
createFileFacadeStub(packageProto, classId.asSingleFqName(), context)
@@ -79,7 +79,7 @@ object KotlinClassFileIndex : KotlinFileIndexBase<KotlinClassFileIndex>(KotlinCl
private val INDEXER = indexer() { fileContent ->
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
}
}