[Interop][Lowering] Add support for fields of reference type

This commit is contained in:
Sergey Bogolepov
2020-05-19 12:22:11 +07:00
committed by Sergey Bogolepov
parent d72a955411
commit c6f11781fe
2 changed files with 49 additions and 12 deletions
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.backend.konan.cgen.isCEnumType
import org.jetbrains.kotlin.backend.konan.cgen.isVector import org.jetbrains.kotlin.backend.konan.cgen.isVector
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.isAny
import org.jetbrains.kotlin.backend.konan.ir.isObjCObjectType
import org.jetbrains.kotlin.backend.konan.ir.superClasses import org.jetbrains.kotlin.backend.konan.ir.superClasses
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
@@ -48,7 +50,8 @@ private fun isBitFieldAccessor(function: IrFunction): Boolean =
private class InteropCallContext( private class InteropCallContext(
val symbols: KonanSymbols, val symbols: KonanSymbols,
val builder: IrBuilderWithScope val builder: IrBuilderWithScope,
val failCompilation: (String) -> Nothing
) { ) {
fun IrType.isCPointer(): Boolean = this.classOrNull == symbols.interopCPointer fun IrType.isCPointer(): Boolean = this.classOrNull == symbols.interopCPointer
@@ -56,13 +59,22 @@ private class InteropCallContext(
fun IrType.isStoredInMemoryDirectly(): Boolean = fun IrType.isStoredInMemoryDirectly(): Boolean =
isPrimitiveType() || isUnsigned() || isVector() isPrimitiveType() || isUnsigned() || isVector()
fun IrType.isSupportedReference(): Boolean = isObjCObjectType()
|| getClass()?.isAny() == true
|| isStringClassType()
|| classOrNull == symbols.list
|| classOrNull == symbols.mutableList
|| classOrNull == symbols.set
|| classOrNull == symbols.map
} }
private inline fun <T> generateInteropCall( private inline fun <T> generateInteropCall(
symbols: KonanSymbols, symbols: KonanSymbols,
builder: IrBuilderWithScope, builder: IrBuilderWithScope,
noinline failCompilation: (String) -> Nothing,
block: InteropCallContext.() -> T block: InteropCallContext.() -> T
) = InteropCallContext(symbols, builder).block() ) = InteropCallContext(symbols, builder, failCompilation).block()
/** /**
* Search for memory read/write function in [kotlinx.cinterop.nativeMemUtils] of a given [valueType]. * Search for memory read/write function in [kotlinx.cinterop.nativeMemUtils] of a given [valueType].
@@ -216,6 +228,17 @@ private fun InteropCallContext.writePointerToMemory(
return writeValueToMemory(nativePtr, valueToWrite, valueToWrite.type) return writeValueToMemory(nativePtr, valueToWrite, valueToWrite.type)
} }
private fun InteropCallContext.writeObjCReferenceToMemory(
nativePtr: IrExpression,
value: IrExpression,
pointerType: IrType
): IrExpression {
val valueToWrite = builder.irCall(symbols.interopObjCObjectRawValueGetter).also {
it.extensionReceiver = value
}
return writeValueToMemory(nativePtr, valueToWrite, valueToWrite.type)
}
private fun InteropCallContext.calculateFieldPointer(receiver: IrExpression, offset: Long): IrExpression { private fun InteropCallContext.calculateFieldPointer(receiver: IrExpression, offset: Long): IrExpression {
val base = builder.irCall(symbols.interopNativePointedRawPtrGetter).also { val base = builder.irCall(symbols.interopNativePointedRawPtrGetter).also {
it.dispatchReceiver = receiver it.dispatchReceiver = receiver
@@ -243,6 +266,16 @@ private fun InteropCallContext.readPointed(nativePtr: IrExpression): IrExpressio
} }
} }
private fun InteropCallContext.readObjectiveCReferenceFromMemory(
nativePtr: IrExpression,
type: IrType
): IrExpression {
val readMemory = readValueFromMemory(nativePtr, symbols.nativePtrType)
return builder.irCall(symbols.interopInterpretObjCPointerOrNull, listOf(type)).apply {
putValueArgument(0, readMemory)
}
}
/** Returns non-null result if [callSite] is accessor to: /** Returns non-null result if [callSite] is accessor to:
* 1. T.value, T : CEnumVar * 1. T.value, T : CEnumVar
* 2. T.<field-name>, T : CStructVar and accessor is annotated with * 2. T.<field-name>, T : CStructVar and accessor is annotated with
@@ -251,16 +284,17 @@ private fun InteropCallContext.readPointed(nativePtr: IrExpression): IrExpressio
internal fun tryGenerateInteropMemberAccess( internal fun tryGenerateInteropMemberAccess(
callSite: IrCall, callSite: IrCall,
symbols: KonanSymbols, symbols: KonanSymbols,
builder: IrBuilderWithScope builder: IrBuilderWithScope,
failCompilation: (String) -> Nothing
): IrExpression? = when { ): IrExpression? = when {
isEnumVarValueAccessor(callSite.symbol.owner, symbols) -> isEnumVarValueAccessor(callSite.symbol.owner, symbols) ->
generateInteropCall(symbols, builder) { generateEnumVarValueAccess(callSite) } generateInteropCall(symbols, builder, failCompilation) { generateEnumVarValueAccess(callSite) }
isMemberAtAccessor(callSite.symbol.owner) -> isMemberAtAccessor(callSite.symbol.owner) ->
generateInteropCall(symbols, builder) { generateMemberAtAccess(callSite) } generateInteropCall(symbols, builder, failCompilation) { generateMemberAtAccess(callSite) }
isBitFieldAccessor(callSite.symbol.owner) -> isBitFieldAccessor(callSite.symbol.owner) ->
generateInteropCall(symbols, builder) { generateBitFieldAccess(callSite) } generateInteropCall(symbols, builder, failCompilation) { generateBitFieldAccess(callSite) }
isArrayMemberAtAccessor(callSite.symbol.owner) -> isArrayMemberAtAccessor(callSite.symbol.owner) ->
generateInteropCall(symbols, builder) { generateArrayMemberAtAccess(callSite) } generateInteropCall(symbols, builder, failCompilation) { generateArrayMemberAtAccess(callSite) }
else -> null else -> null
} }
@@ -292,7 +326,8 @@ private fun InteropCallContext.generateMemberAtAccess(callSite: IrCall): IrExpre
type.isStoredInMemoryDirectly() -> readValueFromMemory(fieldPointer, type) type.isStoredInMemoryDirectly() -> readValueFromMemory(fieldPointer, type)
type.isCPointer() -> readPointerFromMemory(fieldPointer) type.isCPointer() -> readPointerFromMemory(fieldPointer)
type.isNativePointed() -> readPointed(fieldPointer) type.isNativePointed() -> readPointed(fieldPointer)
else -> error("Cannot get field type: ${type.getClass()?.name}") type.isSupportedReference() -> readObjectiveCReferenceFromMemory(fieldPointer, type)
else -> failCompilation("Unsupported struct field type: ${type.getClass()?.name}")
} }
} }
accessor.isSetter -> { accessor.isSetter -> {
@@ -302,10 +337,11 @@ private fun InteropCallContext.generateMemberAtAccess(callSite: IrCall): IrExpre
type.isCEnumType() -> writeEnumValueToMemory(fieldPointer, value, type) type.isCEnumType() -> writeEnumValueToMemory(fieldPointer, value, type)
type.isStoredInMemoryDirectly() -> writeValueToMemory(fieldPointer, value, type) type.isStoredInMemoryDirectly() -> writeValueToMemory(fieldPointer, value, type)
type.isCPointer() -> writePointerToMemory(fieldPointer, value, type) type.isCPointer() -> writePointerToMemory(fieldPointer, value, type)
else -> error("Cannot set field of type ${type.getClass()?.name}") type.isSupportedReference() -> writeObjCReferenceToMemory(fieldPointer, value, type)
else -> failCompilation("Unsupported struct field type: ${type.getClass()?.name}")
} }
} }
else -> error("") else -> error("Unexpected accessor function: ${accessor.name}")
} }
} }
@@ -386,6 +422,6 @@ private fun InteropCallContext.generateBitFieldAccess(callSite: IrCall): IrExpre
val type = accessor.returnType val type = accessor.returnType
readBits(base, offset, size, type) readBits(base, offset, size, type)
} }
else -> error("Unexpected function: ${accessor.name}") else -> error("Unexpected accessor function: ${accessor.name}")
} }
} }
@@ -950,7 +950,8 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
return generateWithStubs { generateCCall(expression, builder, isInvoke = false) } return generateWithStubs { generateCCall(expression, builder, isInvoke = false) }
} }
tryGenerateInteropMemberAccess(expression, symbols, builder)?.let { return it } val failCompilation = { msg: String -> context.reportCompilationError(msg, irFile, expression) }
tryGenerateInteropMemberAccess(expression, symbols, builder, failCompilation)?.let { return it }
tryGenerateInteropConstantRead(expression)?.let { return it } tryGenerateInteropConstantRead(expression)?.let { return it }