Interop, backend: add partial support for callbacks
Support callbacks that don't take or return structs by value.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
3411ce2e8c
commit
9e716291c1
@@ -0,0 +1,10 @@
|
||||
package kotlinx.cinterop
|
||||
|
||||
/**
|
||||
* The type of C function which can be constructed from the appropriate Kotlin function without using any adapter.
|
||||
*/
|
||||
abstract class CTriviallyAdaptedFunctionType<F : Function<*>> : CAdaptedFunctionType<F> {
|
||||
@konan.internal.Intrinsic
|
||||
override final fun fromStatic(function: F): NativePtr =
|
||||
throw Error("CTriviallyAdaptedFunctionType.fromStatic is called virtually")
|
||||
}
|
||||
+18
-4
@@ -731,13 +731,27 @@ class StubGenerator(
|
||||
|
||||
private fun getRetValFfiType(type: Type) = getArgFfiType(type)
|
||||
|
||||
private fun generateFunctionType(type: FunctionType, name: String) {
|
||||
val kotlinFunctionType = getKotlinFunctionType(type)
|
||||
private fun generateFunctionType(type: FunctionType, name: String) = when (platform) {
|
||||
KotlinPlatform.JVM -> generateJvmFunctionType(type, name)
|
||||
KotlinPlatform.NATIVE -> generateNativeFunctionType(type, name)
|
||||
}
|
||||
|
||||
if (platform == KotlinPlatform.NATIVE) {
|
||||
private fun requiresAdapterOnNative(type: FunctionType): Boolean {
|
||||
assert (platform == KotlinPlatform.NATIVE)
|
||||
return (type.parameterTypes + type.returnType).any { it.unwrapTypedefs() is RecordType }
|
||||
}
|
||||
|
||||
private fun generateNativeFunctionType(type: FunctionType, name: String) {
|
||||
if (requiresAdapterOnNative(type)) {
|
||||
out("object $name : CFunctionType {}")
|
||||
return
|
||||
} else {
|
||||
val kotlinFunctionType = getKotlinFunctionType(type)
|
||||
out("object $name : CTriviallyAdaptedFunctionType<$kotlinFunctionType>()")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateJvmFunctionType(type: FunctionType, name: String) {
|
||||
val kotlinFunctionType = getKotlinFunctionType(type)
|
||||
|
||||
val constructorArgs = listOf(getRetValFfiType(type.returnType)) +
|
||||
type.parameterTypes.map { getArgFfiType(it) }
|
||||
|
||||
+19
-1
@@ -6,7 +6,11 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.StarProjectionImpl
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.replace
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
private val cPointerName = "CPointer"
|
||||
private val nativePointedName = "NativePointed"
|
||||
@@ -34,7 +38,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
|
||||
private val nativePointed = packageScope.getContributedClassifier(nativePointedName) as ClassDescriptor
|
||||
|
||||
private val cPointer = this.packageScope.getContributedClassifier(cPointerName) as ClassDescriptor
|
||||
val cPointer = this.packageScope.getContributedClassifier(cPointerName) as ClassDescriptor
|
||||
|
||||
val cPointerRawValue = cPointer.unsubstitutedMemberScope.getContributedVariables("rawValue").single()
|
||||
|
||||
@@ -112,6 +116,20 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
|
||||
val bitsToDouble = packageScope.getContributedFunctions("bitsToDouble").single()
|
||||
|
||||
val staticCFunction = packageScope.getContributedFunctions("staticCFunction").single()
|
||||
|
||||
private val triviallyAdaptedFunctionTypeClass =
|
||||
packageScope.getContributedClassifier("CTriviallyAdaptedFunctionType") as ClassDescriptor
|
||||
|
||||
private val trivallyAdaptedFunctionTypeType =
|
||||
triviallyAdaptedFunctionTypeClass.defaultType.replace(
|
||||
newArguments = listOf(
|
||||
StarProjectionImpl(triviallyAdaptedFunctionTypeClass.declaredTypeParameters.single())
|
||||
)
|
||||
)
|
||||
|
||||
fun isTriviallyAdaptedFunctionType(type: KotlinType): Boolean =
|
||||
type.isSubtypeOf(trivallyAdaptedFunctionTypeType)
|
||||
}
|
||||
|
||||
private fun MemberScope.getContributedVariables(name: String) =
|
||||
|
||||
+9
-2
@@ -1540,9 +1540,16 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateCallableReference(expression: IrCallableReference): LLVMValueRef {
|
||||
assert (expression.type.isUnboundCallableReference())
|
||||
// TODO: consider creating separate IR element for pointer to function.
|
||||
assert (expression.type.isUnboundCallableReference() ||
|
||||
TypeUtils.getClassDescriptor(expression.type) == context.interopBuiltIns.cPointer)
|
||||
|
||||
assert (expression.getArguments().isEmpty())
|
||||
val entry = codegen.functionEntryPointAddress(expression.descriptor as FunctionDescriptor)
|
||||
|
||||
val descriptor = expression.descriptor
|
||||
assert (descriptor.dispatchReceiverParameter == null)
|
||||
|
||||
val entry = codegen.functionEntryPointAddress(descriptor as FunctionDescriptor)
|
||||
return entry
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -100,7 +100,10 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
|
||||
}
|
||||
|
||||
override fun visitCallableReference(expression: IrCallableReference): IrExpression {
|
||||
assert (expression.type.isFunctionOrKFunctionType)
|
||||
if (!expression.type.isFunctionOrKFunctionType) {
|
||||
// Not a subject of this lowering.
|
||||
return expression
|
||||
}
|
||||
|
||||
val descriptor = expression.descriptor
|
||||
val boundArgs = expression.getArguments()
|
||||
|
||||
+29
-2
@@ -5,7 +5,9 @@ import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
||||
import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||
import org.jetbrains.kotlin.backend.konan.ir.getArguments
|
||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
||||
import org.jetbrains.kotlin.backend.konan.reportCompilationError
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilder
|
||||
@@ -14,11 +16,13 @@ import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallableReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
@@ -29,12 +33,12 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
*/
|
||||
internal class InteropLowering(val context: Context) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
val transformer = InteropTransformer(context)
|
||||
val transformer = InteropTransformer(context, irFile)
|
||||
irFile.transformChildrenVoid(transformer)
|
||||
}
|
||||
}
|
||||
|
||||
private class InteropTransformer(val context: Context) : IrBuildingTransformer(context) {
|
||||
private class InteropTransformer(val context: Context, val irFile: IrFile) : IrBuildingTransformer(context) {
|
||||
|
||||
val interop = context.interopBuiltIns
|
||||
|
||||
@@ -212,6 +216,29 @@ private class InteropTransformer(val context: Context) : IrBuildingTransformer(c
|
||||
}
|
||||
}
|
||||
|
||||
interop.staticCFunction -> {
|
||||
val argument = expression.getValueArgument(0)!!
|
||||
if (argument !is IrCallableReference || argument.getArguments().isNotEmpty()) {
|
||||
context.reportCompilationError(
|
||||
"${interop.staticCFunction.fqNameSafe} must take an unbound, non-capturing function",
|
||||
irFile, expression
|
||||
)
|
||||
// TODO: should probably be reported during analysis.
|
||||
}
|
||||
|
||||
val cFunctionType = expression.getTypeArgument(descriptor.typeParameters[1])!!
|
||||
|
||||
if (interop.isTriviallyAdaptedFunctionType(cFunctionType)) {
|
||||
IrCallableReferenceImpl(
|
||||
builder.startOffset, builder.endOffset,
|
||||
expression.type,
|
||||
argument.descriptor,
|
||||
typeArguments = null)
|
||||
} else {
|
||||
TODO("$cFunctionType requiring non-trivial C adapter")
|
||||
}
|
||||
}
|
||||
|
||||
else -> expression
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user