JVM_IR KT-43109 generate internal bridge for custom internal 'toArray'

Also add some tests for internal collection stubs.
This commit is contained in:
Dmitry Petrov
2020-12-04 11:18:22 +03:00
parent 149bcc2d22
commit 3dbe02b7fe
20 changed files with 266 additions and 15 deletions
@@ -49,11 +49,11 @@ private class ToArrayLowering(private val context: JvmBackendContext) : ClassLow
it.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
}?.isCollectionSubClass == true
irClass.findOrCreate(indirectCollectionSubClass, { it.isGenericToArray(context) }) {
irClass.findOrCreate(indirectCollectionSubClass, { it.isGenericToArray(context) }) { bridge ->
irClass.addFunction {
name = Name.identifier("toArray")
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
modality = Modality.OPEN
modality = if (bridge != null) Modality.FINAL else Modality.OPEN
}.apply {
val elementType = addTypeParameter {
name = Name.identifier("T")
@@ -67,19 +67,26 @@ private class ToArrayLowering(private val context: JvmBackendContext) : ClassLow
}
val prototype = addValueParameter("array", returnType, JvmLoweredDeclarationOrigin.TO_ARRAY)
body = context.createIrBuilder(symbol).irBlockBody {
+irReturn(irCall(symbols.genericToArray, symbols.genericToArray.owner.returnType).apply {
putValueArgument(0, irGet(receiver))
putValueArgument(1, irGet(prototype))
})
if (bridge == null) {
+irReturn(irCall(symbols.genericToArray, symbols.genericToArray.owner.returnType).apply {
putValueArgument(0, irGet(receiver))
putValueArgument(1, irGet(prototype))
})
} else {
+irReturn(irCall(bridge.target.symbol, bridge.target.returnType).apply {
dispatchReceiver = irGet(receiver)
putValueArgument(0, irGet(prototype))
})
}
}
}
}
irClass.findOrCreate(indirectCollectionSubClass, IrSimpleFunction::isNonGenericToArray) {
irClass.findOrCreate(indirectCollectionSubClass, IrSimpleFunction::isNonGenericToArray) { bridge ->
irClass.addFunction {
name = Name.identifier("toArray")
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
modality = Modality.OPEN
modality = if (bridge != null) Modality.FINAL else Modality.OPEN
returnType = context.irBuiltIns.arrayClass.typeWith(context.irBuiltIns.anyNType)
}.apply {
val receiver = addDispatchReceiver {
@@ -87,28 +94,49 @@ private class ToArrayLowering(private val context: JvmBackendContext) : ClassLow
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
}
body = context.createIrBuilder(symbol).irBlockBody {
+irReturn(irCall(symbols.nonGenericToArray, symbols.nonGenericToArray.owner.returnType).apply {
putValueArgument(0, irGet(receiver))
})
if (bridge == null) {
+irReturn(irCall(symbols.nonGenericToArray, symbols.nonGenericToArray.owner.returnType).apply {
putValueArgument(0, irGet(receiver))
})
} else {
+irReturn(irCall(bridge.target.symbol, bridge.target.returnType).apply {
dispatchReceiver = irGet(receiver)
})
}
}
}
}
}
private fun IrClass.findOrCreate(indirectSubclass: Boolean, matcher: (IrSimpleFunction) -> Boolean, fallback: () -> IrSimpleFunction) {
private class ToArrayBridge(
val target: IrSimpleFunction
)
private fun IrClass.findOrCreate(
indirectSubclass: Boolean,
matcher: (IrSimpleFunction) -> Boolean,
fallback: (ToArrayBridge?) -> IrSimpleFunction
) {
val existing = functions.find(matcher)
if (existing != null) {
// This is an explicit override of a method defined in `kotlin.collections.AbstractCollection`
// or `java.util.Collection`. From here on, the frontend will check the existence of implementations;
// we just need to match visibility in the former case to the latter.
existing.visibility = DescriptorVisibilities.PUBLIC
if (existing.visibility == DescriptorVisibilities.INTERNAL) {
// If existing `toArray` is internal, create a public bridge method
// to preserve binary compatibility with code generated by the old back-end.
// NB This will preserve existing custom `toArray` signature.
fallback(ToArrayBridge(existing))
} else {
existing.visibility = DescriptorVisibilities.PUBLIC
}
return
}
if (indirectSubclass) {
// There's a Kotlin class up the hierarchy that should already have `toArray`.
return
}
fallback()
fallback(null)
}
}