From 5081adbf87785367e452a0f85ac503439763938f Mon Sep 17 00:00:00 2001 From: SvyatoslavScherbina Date: Fri, 11 Oct 2019 10:00:19 +0300 Subject: [PATCH] Fix handling of 'null' passed as C string (#3452) --- .../kotlin/backend/konan/cgen/CBridgeGen.kt | 38 ++++++++++++------- backend.native/tests/build.gradle | 10 +++++ .../tests/interop/basics/cvalues.def | 10 +++++ backend.native/tests/interop/basics/values.kt | 10 +++++ 4 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 backend.native/tests/interop/basics/cvalues.def create mode 100644 backend.native/tests/interop/basics/values.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt index 5e089ccea69..0e9dbdf8c25 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGen.kt @@ -1375,8 +1375,10 @@ private class ObjCBlockPointerValuePassing( private class WCStringArgumentPassing : KotlinToCArgumentPassing { override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression { - val wcstr = irBuilder.irCall(symbols.interopWcstr.owner).apply { - extensionReceiver = expression + val wcstr = irBuilder.irSafeTransform(expression) { + irCall(symbols.interopWcstr.owner).apply { + extensionReceiver = it + } } return with(CValuesRefArgumentPassing) { passValue(wcstr) } } @@ -1386,8 +1388,10 @@ private class WCStringArgumentPassing : KotlinToCArgumentPassing { private class CStringArgumentPassing : KotlinToCArgumentPassing { override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression { - val cstr = irBuilder.irCall(symbols.interopCstr.owner).apply { - extensionReceiver = expression + val cstr = irBuilder.irSafeTransform(expression) { + irCall(symbols.interopCstr.owner).apply { + extensionReceiver = it + } } return with(CValuesRefArgumentPassing) { passValue(cstr) } } @@ -1410,25 +1414,33 @@ private fun KotlinToCCallBuilder.cValuesRefToPointer( value: IrExpression ): IrExpression = if (value.type.classifierOrNull == symbols.interopCPointer) { value // Optimization -} else with(irBuilder) { +} else { val getPointerFunction = symbols.interopCValuesRef.owner .simpleFunctions() .single { it.name.asString() == "getPointer" } - fun getPointer(expression: IrExpression) = irCall(getPointerFunction).apply { - dispatchReceiver = expression - putValueArgument(0, bridgeCallBuilder.getMemScope()) + irBuilder.irSafeTransform(value) { + irCall(getPointerFunction).apply { + dispatchReceiver = it + putValueArgument(0, bridgeCallBuilder.getMemScope()) + } } +} - if (!value.type.isNullable()) { - getPointer(value) // Optimization - } else irLetS(value) { valueVarSymbol -> +private fun IrBuilderWithScope.irSafeTransform( + value: IrExpression, + block: IrBuilderWithScope.(IrExpression) -> IrExpression +): IrExpression = if (!value.type.isNullable()) { + block(value) // Optimization +} else { + irLetS(value) { valueVarSymbol -> val valueVar = valueVarSymbol.owner + val transformed = block(irGet(valueVar)) irIfThenElse( - type = symbols.interopCPointer.typeWithStarProjections.makeNullable(), + type = transformed.type.makeNullable(), condition = irEqeqeq(irGet(valueVar), irNull()), thenPart = irNull(), - elsePart = getPointer(irGet(valueVar)) + elsePart = transformed ) } } diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 449a06f1503..2c0d080fc79 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3292,6 +3292,10 @@ createInterop("ctypes") { it.defFile 'interop/basics/ctypes.def' } +createInterop("cvalues") { + it.defFile 'interop/basics/cvalues.def' +} + createInterop("ccallbacksAndVarargs") { it.defFile 'interop/basics/ccallbacksAndVarargs.def' } @@ -3449,6 +3453,12 @@ interopTest("interop_types") { interop = 'ctypes' } +interopTest("interop_values") { + disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. + source = "interop/basics/values.kt" + interop = 'cvalues' +} + interopTest("interop_callbacksAndVarargs") { disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. source = "interop/basics/callbacksAndVarargs.kt" diff --git a/backend.native/tests/interop/basics/cvalues.def b/backend.native/tests/interop/basics/cvalues.def new file mode 100644 index 00000000000..77e0cbac565 --- /dev/null +++ b/backend.native/tests/interop/basics/cvalues.def @@ -0,0 +1,10 @@ +--- +_Bool isNullString(const char* str) { + return str == (const char*)0; +} + +typedef const short* LPCWSTR; + +_Bool isNullWString(LPCWSTR str) { + return str == (LPCWSTR)0; +} diff --git a/backend.native/tests/interop/basics/values.kt b/backend.native/tests/interop/basics/values.kt new file mode 100644 index 00000000000..8a6e03941c8 --- /dev/null +++ b/backend.native/tests/interop/basics/values.kt @@ -0,0 +1,10 @@ +import kotlinx.cinterop.* +import kotlin.test.* +import cvalues.* + +fun main() { + assertTrue(isNullString(null)) + assertTrue(isNullWString(null)) + assertFalse(isNullString("a")) + assertFalse(isNullWString("b")) +} \ No newline at end of file