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 ### ### Callbacks ###
To convert Kotlin function to pointer to C function, 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, 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 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 -> { in interop.staticCFunction -> {
val argument = expression.getValueArgument(0)!! val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
if (argument !is IrCallableReference || argument.getArguments().isNotEmpty()) {
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()) {
context.reportCompilationError( context.reportCompilationError(
"${descriptor.fqNameSafe} must take an unbound, non-capturing function", "${descriptor.fqNameSafe} must take an unbound, non-capturing function or lambda",
irFile, expression irFile, expression
) )
// TODO: should probably be reported during analysis. // 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!! val signatureTypes = target.allParameters.map { it.type } + target.returnType!!
signatureTypes.forEachIndexed { index, type -> 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") 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 { private fun IrCall.getSingleTypeArgument(): KotlinType {
val typeParameter = descriptor.original.typeParameters.single() val typeParameter = descriptor.original.typeParameters.single()
return getTypeArgument(typeParameter)!! return getTypeArgument(typeParameter)!!
+6 -8
View File
@@ -11,7 +11,12 @@ fun main(args: Array<String>) {
values[3] = 13 values[3] = 13
values[4] = 8 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) { for (i in 0 .. count - 1) {
print(values[i]) print(values[i])
@@ -20,10 +25,3 @@ fun main(args: Array<String>) {
println() println()
} }
} }
private fun comparator(a: COpaquePointer?, b: COpaquePointer?): Int {
val aValue = a!!.reinterpret<IntVar>()[0]
val bValue = b!!.reinterpret<IntVar>()[0]
return (aValue - bValue)
}