From 63e5aaecba7ec85b58d366ea800288027d117f35 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Mon, 22 May 2017 19:32:59 +0300 Subject: [PATCH] Added type checks for special bridges (Collection, Map, List) --- .../jetbrains/kotlin/backend/konan/Context.kt | 6 +- .../kotlin/backend/konan/ValueTypes.kt | 11 ++- .../kotlin/backend/konan/lower/Autoboxing.kt | 19 +--- .../backend/konan/lower/BridgesBuilding.kt | 89 +++++++++++++++++-- .../kotlin/ir/builders/IrBuilders.kt | 2 + 5 files changed, 102 insertions(+), 25 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 44fdefb52be..817e3a197cd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -118,20 +118,20 @@ internal class SpecialDeclarationsFactory(val context: Context) { val returnType = when (bridgeDirections[0]) { BridgeDirection.TO_VALUE_TYPE -> descriptor.returnType!! BridgeDirection.NOT_NEEDED -> descriptor.returnType - BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType + BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.nullableAnyType } val extensionReceiverType = when (bridgeDirections[1]) { BridgeDirection.TO_VALUE_TYPE -> descriptor.extensionReceiverParameter!!.type BridgeDirection.NOT_NEEDED -> descriptor.extensionReceiverParameter?.type - BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType + BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.nullableAnyType } val valueParameters = descriptor.valueParameters.mapIndexed { index, valueParameterDescriptor -> val outType = when (bridgeDirections[index + 2]) { BridgeDirection.TO_VALUE_TYPE -> valueParameterDescriptor.type BridgeDirection.NOT_NEEDED -> valueParameterDescriptor.type - BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType + BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.nullableAnyType } ValueParameterDescriptorImpl( containingDeclaration = valueParameterDescriptor.containingDeclaration, diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ValueTypes.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ValueTypes.kt index 2385b9a3bf0..1943d83c87f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ValueTypes.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ValueTypes.kt @@ -86,4 +86,13 @@ tailrec fun KotlinType.notNullableIsRepresentedAs(valueType: ValueType): Boolean return firstSupertype.notNullableIsRepresentedAs(valueType) } -internal fun KotlinType.isValueType() = ValueType.values().any { this.isRepresentedAs(it) } +fun KotlinType.isValueType() = ValueType.values().any { this.isRepresentedAs(it) } + +/** + * @return the [ValueType] given type represented in generated code as, + * or `null` if represented as object reference. + */ +val KotlinType.correspondingValueType: ValueType? + get() = ValueType.values().firstOrNull { + isRepresentedAs(it) + } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt index 0fde59edb6f..f0551b06191 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt @@ -19,11 +19,8 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.AbstractValueUsageTransformer import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.descriptors.isSuspend -import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.ValueType +import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.* -import org.jetbrains.kotlin.backend.konan.notNullableIsRepresentedAs -import org.jetbrains.kotlin.backend.konan.isRepresentedAs import org.jetbrains.kotlin.backend.konan.util.atMostOne import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrStatement @@ -116,16 +113,6 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr } } - /** - * @return the [ValueType] given type represented in generated code as, - * or `null` if represented as object reference. - */ - private fun getValueType(type: KotlinType): ValueType? { - return ValueType.values().firstOrNull { - type.isRepresentedAs(it) - } - } - private var currentFunctionDescriptor: FunctionDescriptor? = null override fun visitFunction(declaration: IrFunction): IrStatement { @@ -188,8 +175,8 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr } private fun IrExpression.adaptIfNecessary(actualType: KotlinType, expectedType: KotlinType): IrExpression { - val actualValueType = getValueType(actualType) - val expectedValueType = getValueType(expectedType) + val actualValueType = actualType.correspondingValueType + val expectedValueType = expectedType.correspondingValueType return when { actualValueType == expectedValueType -> this diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt index d331f76d215..c3d66cfe7e4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt @@ -18,24 +18,29 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.lower.irBlockBody +import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.ValueType import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.util.createParameterDeclarations +import org.jetbrains.kotlin.ir.util.type import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature import org.jetbrains.kotlin.types.KotlinType internal class DirectBridgesCallsLowering(val context: Context) : BodyLoweringPass { @@ -79,6 +84,19 @@ internal class DirectBridgesCallsLowering(val context: Context) : BodyLoweringPa } internal class BridgesBuilding(val context: Context) : ClassLoweringPass { + + private fun IrBuilderWithScope.returnIfBadType(value: IrExpression, + type: KotlinType, + returnValueOnFail: IrExpression) + = irIfThen(irNotIs(value, type), irReturn(returnValueOnFail)) + + private fun IrBuilderWithScope.irConst(value: Any?) = when (value) { + null -> irNull() + is Int -> irInt(value) + is Boolean -> if (value) irTrue() else irFalse() + else -> TODO() + } + override fun lower(irClass: IrClass) { val functions = mutableSetOf() irClass.declarations.forEach { @@ -93,6 +111,7 @@ internal class BridgesBuilding(val context: Context) : ClassLoweringPass { irClass.descriptor.contributedMethods.forEach { functions.add(it) } + val builtBridges = mutableSetOf() functions.filterNotNull() .filterNot { it.modality == Modality.ABSTRACT } .forEach { function -> @@ -104,17 +123,76 @@ internal class BridgesBuilding(val context: Context) : ClassLoweringPass { .distinctBy { it.bridgeDirections } .forEach { buildBridge(it, irClass) + builtBridges += it.descriptor } } + irClass.transformChildrenVoid(object: IrElementTransformerVoid() { + override fun visitFunction(declaration: IrFunction): IrStatement { + declaration.transformChildrenVoid(this) + + val descriptor = declaration.descriptor + val typeSafeBarrierDescription = BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor) + if (typeSafeBarrierDescription == null || builtBridges.contains(descriptor)) + return declaration + + val body = declaration.body as? IrBlockBody + ?: return declaration + val irBuilder = context.createIrBuilder(declaration.symbol, declaration.startOffset, declaration.endOffset) + declaration.body = irBuilder.irBlockBody(declaration) { + val valueParameters = declaration.valueParameters + for (i in valueParameters.indices) { + if (!typeSafeBarrierDescription.checkParameter(i)) + continue + val type = valueParameters[i].type + if (type != context.builtIns.nullableAnyType) { + +returnIfBadType(irGet(valueParameters[i].symbol), type, + if (typeSafeBarrierDescription == BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT) + irGet(valueParameters[2].symbol) + else irConst(typeSafeBarrierDescription.defaultValue) + ) + } + body.statements.forEach { +it } + } + } + return declaration + } + }) } private object DECLARATION_ORIGIN_BRIDGE_METHOD : IrDeclarationOriginImpl("BRIDGE_METHOD") + private val ValueType.shortName + get() = this.classFqName.shortName() + + private fun getBoxType(valueType: ValueType) = + context.builtIns.getKonanInternalClass("${valueType.shortName}Box").defaultType + private fun buildBridge(descriptor: OverriddenFunctionDescriptor, irClass: IrClass) { val bridgeDescriptor = context.specialDeclarationsFactory.getBridgeDescriptor(descriptor) val target = descriptor.descriptor.target + val statements = mutableListOf() + val irBuilder = context.createIrBuilder(bridgeDescriptor, irClass.startOffset, irClass.endOffset) + irBuilder.run { + val typeSafeBarrierDescription = BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor.overriddenDescriptor) + if (typeSafeBarrierDescription != null) { + val valueParameters = bridgeDescriptor.valueParameters + for (i in valueParameters.indices) { + if (!typeSafeBarrierDescription.checkParameter(i)) + continue + val type = target.valueParameters[i].type + if (type != context.builtIns.nullableAnyType) { + statements += returnIfBadType(irGet(valueParameters[i]), type, + if (typeSafeBarrierDescription == BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT) + irGet(valueParameters[2]) + else irConst(typeSafeBarrierDescription.defaultValue) + ) + } + } + } + } + val delegatingCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target, superQualifierDescriptor = target.containingDeclaration as ClassDescriptor /* Call non-virtually */ ).apply { @@ -133,9 +211,10 @@ internal class BridgesBuilding(val context: Context) : ClassLoweringPass { IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, bridgeDescriptor, delegatingCall) else delegatingCall + statements += bridgeBody irClass.declarations.add( IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_BRIDGE_METHOD, - bridgeDescriptor, IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(bridgeBody)) + bridgeDescriptor, IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, statements) ).apply { createParameterDeclarations() } ) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrBuilders.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrBuilders.kt index e5ace1f7187..489ae76e1fd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrBuilders.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrBuilders.kt @@ -55,6 +55,8 @@ fun IrBuilderWithScope.irContinue(loop: IrLoop) = fun IrBuilderWithScope.irTrue() = IrConstImpl.boolean(startOffset, endOffset, context.builtIns.booleanType, true) +fun IrBuilderWithScope.irFalse() = IrConstImpl.boolean(startOffset, endOffset, context.builtIns.booleanType, false) + @Deprecated("Creates unbound symbol") fun IrBuilderWithScope.irGet(value: ValueDescriptor) = IrGetValueImpl(startOffset, endOffset, value)