Interop, backend: add partial support for callbacks

Support callbacks that don't take or return structs by value.
This commit is contained in:
Svyatoslav Scherbina
2017-02-13 14:50:48 +07:00
committed by SvyatoslavScherbina
parent 3411ce2e8c
commit 9e716291c1
6 changed files with 89 additions and 10 deletions
@@ -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")
}
@@ -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) }
@@ -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) =
@@ -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
}
@@ -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()
@@ -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
}
}