JVM_IR fix special bridges in mixed Java/Kotlin hierarchies

KT-50257 KT-50476
This commit is contained in:
Dmitry Petrov
2021-12-22 15:30:09 +03:00
committed by Space
parent 3f056bc086
commit 5946242d75
34 changed files with 1632 additions and 102 deletions
@@ -228,33 +228,35 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
return irFunction
}
// If irFunction is a fake override, we replace it with a stub and redirect all calls to irFunction with
// calls to the stub instead. Otherwise we'll end up calling the special method itself and get into an
// infinite loop.
//
// There are three cases to consider. If the method is abstract, then we simply generate a concrete abstract method
// to avoid generating a call to a method which does not exist in the current class. If the method is final,
// then we will not override it in a subclass and we do not need to generate an additional stub method.
//
// Finally, if we have a non-abstract, non-final fake-override we need to put in an additional bridge which uses
// INVOKESPECIAL to call the special bridge implementation in the superclass. We can be sure that an implementation
// exists in a superclass, since we do not generate bridges for fake overrides of interface methods.
if (irFunction.isFakeOverride) {
// If irFunction is a fake override, we replace it with a stub and redirect all calls to irFunction with calls to the stub
// instead. Otherwise, we'll end up calling the special method itself and get into an infinite loop.
bridgeTarget = when {
irFunction.isJvmAbstract(context.state.jvmDefaultMode) -> {
// If the method is abstract, then we simply generate a concrete abstract method
// to avoid generating a call to a method which does not exist in the current class.
irClass.declarations.remove(irFunction)
irClass.addAbstractMethodStub(irFunction)
}
irFunction.modality != Modality.FINAL -> {
// If we have a non-abstract, non-final fake-override we need to put in an additional bridge which uses
// INVOKESPECIAL to call the special bridge implementation in the superclass.
// We can be sure that an implementation exists in a superclass,
// since we do not generate bridges for fake overrides of interface methods.
val overriddenFromClass = irFunction.overriddenFromClass()!!
val superBridge = SpecialBridge(
irFunction, irFunction.jvmMethod, superQualifierSymbol = overriddenFromClass.parentAsClass.symbol,
overridden = irFunction,
signature = irFunction.jvmMethod,
superQualifierSymbol = overriddenFromClass.parentAsClass.symbol,
methodInfo = specialBridge.methodInfo?.copy(argumentsToCheck = 0), // For potential argument boxing
isFinal = false,
)
// The part after '?:' is needed for methods with default implementations in collection interfaces:
// MutableMap.remove() and getOrDefault().
val superTarget = overriddenFromClass.takeIf { !it.isFakeOverride } ?: specialBridge.overridden
val superTarget = overriddenFromClass.takeIf { !it.isFakeOverride || !specialBridge.isOverriding }
?: specialBridge.overridden
if (superBridge.signature == superTarget.jvmMethod) {
// If the resulting bridge to a super member matches the signature of the bridge callee,
// bridge is not needed.
@@ -265,6 +267,8 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
}
}
else -> {
// If the method is final,
// then we will not override it in a subclass and we do not need to generate an additional stub method.
irFunction
}
}
@@ -320,7 +324,7 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
return
}
// For concrete fake overrides, some of the bridges may be inherited from the super-classes. Specifically, bridges for all
// For concrete fake overrides, some bridges may be inherited from the super-classes. Specifically, bridges for all
// declarations that are reachable from all concrete immediate super-functions of the given function. Note that all such bridges are
// guaranteed to delegate to the same implementation as bridges for the given function, that's why it's safe to inherit them.
//
@@ -359,7 +363,6 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
private fun IrSimpleFunction.isClashingWithPotentialBridge(name: Name, signature: Method): Boolean =
(!this.isFakeOverride || this.modality == Modality.FINAL) && this.name == name && this.jvmMethod == signature
// Returns the special bridge overridden by the current methods if it exists.
private val IrSimpleFunction.specialBridgeOrNull: SpecialBridge?
get() = context.bridgeLoweringCache.computeSpecialBridge(this)
@@ -54,7 +54,8 @@ class BridgeLoweringCache(private val context: JvmBackendContext) {
val specialMethodInfo = specialBridgeMethods.getSpecialMethodInfo(function)
if (specialMethodInfo != null)
return SpecialBridge(
overridden = function, signature = computeJvmMethod(function),
overridden = function,
signature = computeJvmMethod(function),
needsGenericSignature = specialMethodInfo.needsGenericSignature,
methodInfo = specialMethodInfo,
needsUnsubstitutedBridge = specialMethodInfo.needsUnsubstitutedBridge
@@ -63,7 +64,9 @@ class BridgeLoweringCache(private val context: JvmBackendContext) {
val specialBuiltInInfo = specialBridgeMethods.getBuiltInWithDifferentJvmName(function)
if (specialBuiltInInfo != null)
return SpecialBridge(
function, computeJvmMethod(function), specialBuiltInInfo.needsGenericSignature,
overridden = function,
signature = computeJvmMethod(function),
needsGenericSignature = specialBuiltInInfo.needsGenericSignature,
isOverriding = specialBuiltInInfo.isOverriding
)
@@ -227,7 +227,10 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
mapSignature(function, false)
private fun mapSignature(function: IrFunction, skipGenericSignature: Boolean, skipSpecial: Boolean = false): JvmMethodGenericSignature {
if (function is IrLazyFunctionBase && !function.isFakeOverride && function.initialSignatureFunction != null) {
if (function is IrLazyFunctionBase &&
(!function.isFakeOverride || function.parentAsClass.isFromJava()) &&
function.initialSignatureFunction != null
) {
// Overrides of special builtin in Kotlin classes always have special signature
if ((function as? IrSimpleFunction)?.getDifferentNameForJvmBuiltinFunction() == null ||
(function.parent as? IrClass)?.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
@@ -25,7 +25,11 @@ interface IrLazyFunctionBase : IrLazyDeclarationBase, IrTypeParametersContainer
fun createInitialSignatureFunction(): Lazy<IrFunction?> =
// Need SYNCHRONIZED; otherwise two stubs generated in parallel may fight for the same symbol.
lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
descriptor.initialSignatureDescriptor?.takeIf { it != descriptor }?.original?.let(stubGenerator::generateFunctionStub)
val initialSignatureDescriptor = descriptor.initialSignatureDescriptor
?: return@lazy null
if (initialSignatureDescriptor == descriptor)
return@lazy null
stubGenerator.generateFunctionStub(initialSignatureDescriptor.original)
}
fun createValueParameters(): List<IrValueParameter> =
@@ -17,12 +17,12 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrLock
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.*
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -235,9 +234,12 @@ abstract class DeclarationStubGenerator(
private fun KotlinType.toIrType() = typeTranslator.translateType(this)
internal fun generateValueParameterStub(descriptor: ValueParameterDescriptor, index: Int): IrValueParameter = with(descriptor) {
IrLazyValueParameter(UNDEFINED_OFFSET, UNDEFINED_OFFSET, computeOrigin(this), IrValueParameterSymbolImpl(this), this, name, index,
type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, isHidden = false, isAssignable = false, this@DeclarationStubGenerator, typeTranslator)
.also { irValueParameter ->
IrLazyValueParameter(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, computeOrigin(this), IrValueParameterSymbolImpl(this), this, name, index,
type.toIrType(), varargElementType?.toIrType(),
isCrossinline = isCrossinline, isNoinline = isNoinline, isHidden = false, isAssignable = false,
stubGenerator = this@DeclarationStubGenerator, typeTranslator = typeTranslator
).also { irValueParameter ->
if (descriptor.declaresDefaultValue()) {
irValueParameter.defaultValue = irValueParameter.createStubDefaultValue()
}
@@ -324,7 +326,8 @@ abstract class DeclarationStubGenerator(
descriptor.index,
descriptor.isReified,
descriptor.variance,
this, typeTranslator)
this, typeTranslator
)
}
}
@@ -344,7 +347,8 @@ abstract class DeclarationStubGenerator(
}
}
private fun findDescriptorBySignature(signature: IdSignature): DeclarationDescriptor? = when (signature) {
private fun findDescriptorBySignature(signature: IdSignature): DeclarationDescriptor? =
when (signature) {
is IdSignature.AccessorSignature -> findDescriptorForAccessorSignature(signature)
is IdSignature.CommonSignature -> findDescriptorForPublicSignature(signature)
else -> error("only PublicSignature or AccessorSignature should reach this point, got $signature")