Construct fake overrides after IR deserialization

This commit is contained in:
Alexander Gorshenev
2020-05-12 13:44:58 +03:00
parent ee1ea15684
commit 024385cbbd
13 changed files with 319 additions and 47 deletions
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2020 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.backend.common.serialization
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
import org.jetbrains.kotlin.ir.declarations.impl.IrFakeOverrideFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFakeOverridePropertyImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.descriptors.WrappedPropertyDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
interface PlatformFakeOverrideClassFilter {
fun constructFakeOverrides(clazz: IrClass): Boolean = true
}
object DefaultFakeOverrideClassFilter : PlatformFakeOverrideClassFilter
object FakeOverrideControl {
// If set to true: all fake overrides go to klib serialized IR.
// If set to false: eligible fake overrides are not serialized.
val serializeFakeOverrides: Boolean = false
// If set to true: fake overrides are deserialized from klib serialized IR.
// If set to false: eligible fake overrides are constructed within IR linker.
val deserializeFakeOverrides: Boolean = false
}
class FakeOverrideBuilderImpl(
val symbolTable: SymbolTable,
val signaturer: IdSignatureSerializer,
val irBuiltIns: IrBuiltIns,
private val platformSpecificClassFilter: PlatformFakeOverrideClassFilter = DefaultFakeOverrideClassFilter
) : FakeOverrideBuilder, FakeOverrideBuilderStrategy {
private val haveFakeOverrides = mutableSetOf<IrClass>()
override val propertyOverriddenSymbols = mutableMapOf<IrOverridableMember, List<IrSymbol>>()
private val irOverridingUtil = IrOverridingUtil(irBuiltIns, this)
override fun fakeOverrideMember(
superType: IrType,
member: IrOverridableMember,
clazz: IrClass
): IrOverridableMember {
require(superType is IrSimpleType) { "superType is $superType, expected IrSimpleType" }
val classifier = superType.classifier
require(classifier is IrClassSymbol) { "superType classifier is not IrClassSymbol: $classifier" }
val typeParameters = classifier.owner.typeParameters.map { it.symbol }
val typeArguments =
superType.arguments.map { it as? IrSimpleType ?: error("Unexpected super type $it") }
assert(typeParameters.size == typeArguments.size) {
"typeParameters = $typeParameters size != typeArguments = $typeArguments size "
}
val substitutionMap = typeParameters.zip(typeArguments).toMap()
val copier =
DeepCopyIrTreeWithSymbolsForFakeOverrides(substitutionMap, superType, clazz)
val deepCopyFakeOverride = copier.copy(member) as IrOverridableMember
deepCopyFakeOverride.parent = clazz
return deepCopyFakeOverride
}
fun buildFakeOverrideChainsForClass(clazz: IrClass) {
if (haveFakeOverrides.contains(clazz)) return
if (!platformSpecificClassFilter.constructFakeOverrides(clazz) || !clazz.symbol.isPublicApi) return
val superTypes = clazz.superTypes
val superClasses = superTypes.map {
it.getClass() ?: error("Unexpected super type: $it")
}
superClasses.forEach {
buildFakeOverrideChainsForClass(it)
haveFakeOverrides.add(it)
}
irOverridingUtil.buildFakeOverridesForClass(clazz)
}
override fun buildFakeOverridesForClass(clazz: IrClass) = irOverridingUtil.buildFakeOverridesForClass(clazz)
override fun linkFakeOverride(fakeOverride: IrOverridableMember) {
when (fakeOverride) {
is IrFakeOverrideFunctionImpl -> linkFunctionFakeOverride(fakeOverride)
is IrFakeOverridePropertyImpl -> linkPropertyFakeOverride(fakeOverride)
else -> error("Unexpected fake override: $fakeOverride")
}
}
private fun linkFunctionFakeOverride(declaration: IrFakeOverrideFunctionImpl) {
val signature = signaturer.composePublicIdSignature(declaration)
symbolTable.declareSimpleFunctionFromLinker(WrappedSimpleFunctionDescriptor(), signature) {
declaration.acquireSymbol(it)
declaration
}
}
private fun linkPropertyFakeOverride(declaration: IrFakeOverridePropertyImpl) {
val signature = signaturer.composePublicIdSignature(declaration)
symbolTable.declarePropertyFromLinker(WrappedPropertyDescriptor(), signature) {
declaration.acquireSymbol(it)
declaration
}
declaration.getter?.let {
it.correspondingPropertySymbol = declaration.symbol
linkFunctionFakeOverride(it as? IrFakeOverrideFunctionImpl
?: error("Unexpected fake override getter: $it")
)
}
declaration.setter?.let {
it.correspondingPropertySymbol = declaration.symbol
linkFunctionFakeOverride(it as? IrFakeOverrideFunctionImpl
?: error("Unexpected fake override setter: $it")
)
}
}
fun provideFakeOverrides(module: IrModuleFragment) {
module.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
buildFakeOverrideChainsForClass(declaration)
haveFakeOverrides.add(declaration)
super.visitClass(declaration)
}
override fun visitFunction(declaration: IrFunction) {
// Don't go for function local classes
}
})
}
}
@@ -105,8 +105,8 @@ class CurrentModuleWithICDeserializer(
icDeserializer.deserializeReachableDeclarations()
}
override fun postProcess() {
icDeserializer.postProcess()
override fun postProcess(postProcessor: (IrModuleFragment) -> Unit) {
icDeserializer.postProcess(postProcessor)
}
private fun DeclarationDescriptor.isDirtyDescriptor(): Boolean {
@@ -134,4 +134,6 @@ class CurrentModuleWithICDeserializer(
get() = delegate.moduleFragment
override val moduleDependencies: Collection<IrModuleDeserializer>
get() = delegate.moduleDependencies
override val isCurrent: Boolean
get() = delegate.isCurrent
}
@@ -104,7 +104,7 @@ import org.jetbrains.kotlin.backend.common.serialization.proto.PublicIdSignature
import org.jetbrains.kotlin.backend.common.serialization.proto.AccessorIdSignature as ProtoAccessorIdSignature
import org.jetbrains.kotlin.backend.common.serialization.proto.FileLocalIdSignature as ProtoFileLocalIdSignature
abstract class IrFileDeserializer(val logger: LoggingContext, val builtIns: IrBuiltIns, val symbolTable: SymbolTable, protected var deserializeBodies: Boolean) {
abstract class IrFileDeserializer(val logger: LoggingContext, val builtIns: IrBuiltIns, val symbolTable: SymbolTable, var constructFakeOverrides: Boolean, protected var deserializeBodies: Boolean) {
abstract fun deserializeIrSymbolToDeclare(code: Long): Pair<IrSymbol, IdSignature>
abstract fun deserializeIrSymbol(code: Long): IrSymbol
@@ -1038,7 +1038,9 @@ abstract class IrFileDeserializer(val logger: LoggingContext, val builtIns: IrBu
}.usingParent {
typeParameters = deserializeTypeParameters(proto.typeParameterList, true)
proto.declarationList.mapTo(declarations) { deserializeDeclaration(it) }
proto.declarationList
//.filterNot { isSkippableFakeOverride(it) }
.mapTo(declarations) { deserializeDeclaration(it) }
thisReceiver = deserializeIrValueParameter(proto.thisReceiver, -1)
@@ -1363,6 +1365,26 @@ abstract class IrFileDeserializer(val logger: LoggingContext, val builtIns: IrBu
return declaration
}
// Depending on deserialization strategy we either deserialize public api fake overrides
// or reconstruct them after IR linker completes.
private fun isSkippableFakeOverride(proto: ProtoDeclaration): Boolean {
if (!constructFakeOverrides) return false
val isFakeOverride = when (proto.declaratorCase!!) {
IR_FUNCTION -> FunctionFlags.decode(proto.irFunction.base.base.flags).isFakeOverride
IR_PROPERTY -> PropertyFlags.decode(proto.irProperty.base.flags).isFakeOverride
// Don't consider IR_FIELDS here.
else -> false
}
val isPublicApi = when (proto.declaratorCase!!) {
IR_FUNCTION -> deserializeIrSymbol(proto.irFunction.base.base.symbol).signature.isPublic
IR_PROPERTY -> deserializeIrSymbol(proto.irProperty.base.symbol).signature.isPublic
// Don't consider IR_FIELDS here.
else -> false
}
return isFakeOverride && isPublicApi
}
fun deserializeDeclaration(proto: ProtoDeclaration, parent: IrDeclarationParent) =
usingParent(parent) {
deserializeDeclaration(proto)
@@ -1121,7 +1121,7 @@ open class IrFileSerializer(
.setName(serializeName(clazz.name))
clazz.declarations.forEach {
proto.addDeclaration(serializeDeclaration(it))
if (memberNeedsSerialization(it)) proto.addDeclaration(serializeDeclaration(it))
}
clazz.typeParameters.forEach {
@@ -1211,6 +1211,16 @@ open class IrFileSerializer(
open fun backendSpecificExplicitRoot(declaration: IrFunction) = false
open fun backendSpecificExplicitRoot(declaration: IrClass) = false
open fun keepOrderOfProperties(property: IrProperty): Boolean = !property.isConst
open fun backendSpecificSerializeAllMembers(irClass: IrClass) = false
fun memberNeedsSerialization(member: IrDeclaration): Boolean {
assert(member.parent is IrClass)
if (FakeOverrideControl.serializeFakeOverrides) return true
if (backendSpecificSerializeAllMembers(member.parent as IrClass)) return true
// TODO: we can, probably, maintain a privacy bit while traversing the tree
// instead of running the parent hierarchy for isExported every time.
return !(member.isFakeOverride && declarationTable.isExportedDeclaration(member))
}
fun serializeIrFile(file: IrFile): SerializedIrFile {
val topLevelDeclarations = mutableListOf<SerializedDeclaration>()
@@ -50,13 +50,17 @@ abstract class IrModuleDeserializer(val moduleDescriptor: ModuleDescriptor) {
open fun deserializeReachableDeclarations() { error("Unsupported Operation") }
open fun postProcess() {}
open fun postProcess(postProcessor: (IrModuleFragment) -> Unit) {
postProcessor(moduleFragment)
}
abstract val moduleFragment: IrModuleFragment
abstract val moduleDependencies: Collection<IrModuleDeserializer>
open val strategy: DeserializationStrategy = DeserializationStrategy.ONLY_DECLARATION_HEADERS
open val isCurrent = false
}
// Used to resolve built in symbols like `kotlin.ir.internal.*` or `kotlin.FunctionN`
@@ -178,8 +182,8 @@ class IrModuleDeserializerWithBuiltIns(
else delegate.declareIrSymbol(symbol)
}
override fun postProcess() {
delegate.postProcess()
override fun postProcess(postProcessor: (IrModuleFragment) -> Unit) {
delegate.postProcess(postProcessor)
}
override fun init() {
@@ -198,6 +202,7 @@ class IrModuleDeserializerWithBuiltIns(
override val moduleFragment: IrModuleFragment get() = delegate.moduleFragment
override val moduleDependencies: Collection<IrModuleDeserializer> get() = delegate.moduleDependencies
override val isCurrent get() = delegate.isCurrent
}
open class CurrentModuleDeserializer(
@@ -211,4 +216,8 @@ open class CurrentModuleDeserializer(
}
override fun declareIrSymbol(symbol: IrSymbol) {}
}
override fun postProcess(postProcessor: (IrModuleFragment) -> Unit) {}
override val isCurrent = true
}
@@ -23,6 +23,10 @@ import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.impl.IrErrorTypeImpl
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.IrDeserializer
import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.library.IrLibrary
import org.jetbrains.kotlin.library.KotlinLibrary
@@ -57,6 +61,10 @@ abstract class KotlinIrLinker(
protected val deserializersForModules = mutableMapOf<ModuleDescriptor, IrModuleDeserializer>()
abstract val fakeOverrideBuilderImpl: FakeOverrideBuilderImpl
private val haveSeen = mutableSetOf<IrSymbol>()
abstract inner class BasicIrModuleDeserializer(moduleDescriptor: ModuleDescriptor, override val klib: IrLibrary, override val strategy: DeserializationStrategy) :
IrModuleDeserializer(moduleDescriptor) {
@@ -133,8 +141,6 @@ abstract class KotlinIrLinker(
}
}
override fun postProcess() {}
override val moduleFragment: IrModuleFragment = IrModuleFragmentImpl(moduleDescriptor, builtIns, emptyList())
private fun deserializeIrFile(fileProto: ProtoFile, fileIndex: Int, moduleDeserializer: IrModuleDeserializer): IrFile {
@@ -144,7 +150,13 @@ abstract class KotlinIrLinker(
val fileEntry = NaiveSourceBasedFileEntryImpl(fileName, fileProto.fileEntry.lineStartOffsetsList.toIntArray())
val fileDeserializer =
IrDeserializerForFile(fileProto.annotationList, fileProto.actualsList, fileIndex, !strategy.needBodies, strategy.inlineBodies, moduleDeserializer).apply {
IrDeserializerForFile(fileProto.annotationList,
fileProto.actualsList,
fileIndex,
!strategy.needBodies,
strategy.inlineBodies,
!strategy.fakeOverrides,
moduleDeserializer).apply {
// Explicitly exported declarations (e.g. top-level initializers) must be deserialized before all other declarations.
// Thus we schedule their deserialization in deserializer's constructor.
@@ -205,8 +217,9 @@ abstract class KotlinIrLinker(
private val fileIndex: Int,
onlyHeaders: Boolean,
inlineBodies: Boolean,
private val moduleDeserializer: IrModuleDeserializer
) : IrFileDeserializer(logger, builtIns, symbolTable, !onlyHeaders) {
constructFakeOverrrides: Boolean,
private val moduleDeserializer: IrModuleDeserializer,
) : IrFileDeserializer(logger, builtIns, symbolTable, constructFakeOverrrides, !onlyHeaders) {
private var fileLoops = mutableMapOf<Int, IrLoopBase>()
@@ -338,7 +351,9 @@ abstract class KotlinIrLinker(
private fun deserializeIrSymbolData(idSignature: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
if (idSignature.isLocal) return deserializeIrLocalSymbolData(idSignature, symbolKind)
return findModuleDeserializer(idSignature).deserializeIrSymbol(idSignature, symbolKind)
return findModuleDeserializer(idSignature).deserializeIrSymbol(idSignature, symbolKind).also {
haveSeen.add(it)
}
}
override fun deserializeIrSymbolToDeclare(code: Long): Pair<IrSymbol, IdSignature> {
@@ -482,6 +497,11 @@ abstract class KotlinIrLinker(
private fun findDeserializedDeclarationForSymbol(symbol: IrSymbol): DeclarationDescriptor? {
assert(symbol.isPublicApi || symbol.descriptor.module === currentModule || platformSpecificSymbol(symbol))
if (haveSeen.contains(symbol)) {
return null
}
haveSeen.add(symbol)
val descriptor = symbol.descriptor
val moduleDeserializer = resolveModuleDeserializer(descriptor.module)
@@ -518,9 +538,11 @@ abstract class KotlinIrLinker(
if (!symbol.isBound && symbol.owner.isExpectMember())
return null
assert(symbol.isBound) {
"getDeclaration: symbol $symbol is unbound, descriptor = ${symbol.descriptor}, signature = ${symbol.signature}"
}
if (!symbol.isBound) return null
//assert(symbol.isBound) {
// "getDeclaration: symbol $symbol is unbound, descriptor = ${symbol.descriptor}, signature = ${symbol.signature}"
//}
return symbol.owner as IrDeclaration
}
@@ -541,8 +563,15 @@ abstract class KotlinIrLinker(
}
override fun postProcess() {
deserializersForModules.values.forEach { it.postProcess() }
finalizeExpectActualLinker()
deserializersForModules.values.forEach {
it.postProcess {
fakeOverrideBuilderImpl.provideFakeOverrides(it)
}
}
symbolTable.noUnboundLeft("unbound after fake overrides:")
}
// The issue here is that an expect can not trigger its actual deserialization by reachability
@@ -621,10 +650,20 @@ abstract class KotlinIrLinker(
deserializeIrModuleHeader(moduleDescriptor, kotlinLibrary, DeserializationStrategy.WITH_INLINE_BODIES)
}
enum class DeserializationStrategy(val needBodies: Boolean, val explicitlyExported: Boolean, val theWholeWorld: Boolean, val inlineBodies: Boolean) {
enum class DeserializationStrategy(
val needBodies: Boolean,
val explicitlyExported: Boolean,
val theWholeWorld: Boolean,
val inlineBodies: Boolean,
val fakeOverrides: Boolean = FakeOverrideControl.deserializeFakeOverrides
) {
ONLY_REFERENCED(true, false, false, true),
ALL(true, true, true, true),
EXPLICITLY_EXPORTED(true, true, false, true),
ONLY_DECLARATION_HEADERS(false, false, false, false),
WITH_INLINE_BODIES(false, false, false, true)
}
interface FakeOverrideBuilder {
fun buildFakeOverridesForClass(clazz: IrClass)
}