Handle lambda argument of staticCFunction

This commit is contained in:
Svyatoslav Scherbina
2017-04-21 12:51:51 +03:00
committed by SvyatoslavScherbina
parent c6ad4c3307
commit ea2d7cbf01
3 changed files with 43 additions and 13 deletions
+3 -1
View File
@@ -275,7 +275,9 @@ methods available:
### Callbacks ###
To convert Kotlin function to pointer to C function,
`staticCFunction(::kotlinFunction)` can be used.
`staticCFunction(::kotlinFunction)` can be used. It is also allowed to provide
the lambda instead of function reference. The function or lambda must not
capture any values.
Note that some function types are not supported currently. For example,
it is not possible to get pointer to function that receives or returns structs
@@ -277,16 +277,17 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
}
in interop.staticCFunction -> {
val argument = expression.getValueArgument(0)!!
if (argument !is IrCallableReference || argument.getArguments().isNotEmpty()) {
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()) {
context.reportCompilationError(
"${descriptor.fqNameSafe} must take an unbound, non-capturing function",
"${descriptor.fqNameSafe} must take an unbound, non-capturing function or lambda",
irFile, expression
)
// TODO: should probably be reported during analysis.
}
val target = argument.descriptor.original
val target = irCallableReference.descriptor.original
val signatureTypes = target.allParameters.map { it.type } + target.returnType!!
signatureTypes.forEachIndexed { index, type ->
@@ -385,6 +386,35 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
reportError("Type $this is not supported in callback signature")
}
private fun unwrapStaticFunctionArgument(argument: IrExpression): IrCallableReference? {
if (argument is IrCallableReference) {
return argument
}
// Otherwise check whether it is a lambda:
// 1. It is a container with two statements and expected origin:
if (argument !is IrContainerExpression || argument.statements.size != 2) {
return null
}
if (argument.origin != IrStatementOrigin.LAMBDA && argument.origin != IrStatementOrigin.ANONYMOUS_FUNCTION) {
return null
}
// 2. First statement is an empty container (created during local functions lowering):
val firstStatement = argument.statements.first()
if (firstStatement !is IrContainerExpression || firstStatement.statements.size != 0) {
return null
}
// 3. Second statement is IrCallableReference:
return argument.statements.last() as? IrCallableReference
}
private fun IrCall.getSingleTypeArgument(): KotlinType {
val typeParameter = descriptor.original.typeParameters.single()
return getTypeArgument(typeParameter)!!
+6 -8
View File
@@ -11,7 +11,12 @@ fun main(args: Array<String>) {
values[3] = 13
values[4] = 8
cstdlib.qsort(values, count.toLong(), IntVar.size, staticCFunction(::comparator))
cstdlib.qsort(values, count.toLong(), IntVar.size, staticCFunction { a, b ->
val aValue = a!!.reinterpret<IntVar>()[0]
val bValue = b!!.reinterpret<IntVar>()[0]
(aValue - bValue)
})
for (i in 0 .. count - 1) {
print(values[i])
@@ -20,10 +25,3 @@ fun main(args: Array<String>) {
println()
}
}
private fun comparator(a: COpaquePointer?, b: COpaquePointer?): Int {
val aValue = a!!.reinterpret<IntVar>()[0]
val bValue = b!!.reinterpret<IntVar>()[0]
return (aValue - bValue)
}