Support *arrayOf(...) for variadic C/Objective-C functions
To be used with named arguments, which may be required to resolve ambiguity.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
e437339d9a
commit
39e31d7086
+88
-12
@@ -53,6 +53,7 @@ internal interface KotlinStubs {
|
||||
fun getUniqueKotlinFunctionReferenceClassName(prefix: String): String
|
||||
|
||||
fun reportError(location: IrElement, message: String): Nothing
|
||||
fun throwCompilerError(element: IrElement?, message: String): Nothing
|
||||
}
|
||||
|
||||
private class KotlinToCCallBuilder(
|
||||
@@ -173,18 +174,7 @@ private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, ca
|
||||
val parameter = callee.valueParameters[index]
|
||||
if (parameter.isVararg) {
|
||||
require(index == arguments.lastIndex)
|
||||
require(argument is IrVararg?)
|
||||
argument?.elements.orEmpty().forEach {
|
||||
when (it) {
|
||||
is IrExpression -> addArgument(it, it.type, variadic = true, parameter = null)
|
||||
|
||||
is IrSpreadElement ->
|
||||
stubs.reportError(it, "spread operator is not supported for variadic " +
|
||||
if (isObjCMethod) "Objective-C methods" else "C functions")
|
||||
|
||||
else -> error(it)
|
||||
}
|
||||
}
|
||||
addVariadicArguments(argument)
|
||||
cFunctionBuilder.variadic = true
|
||||
} else {
|
||||
addArgument(argument!!, parameter.type, variadic = false, parameter = parameter)
|
||||
@@ -192,6 +182,92 @@ private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, ca
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.addVariadicArguments(
|
||||
argumentForVarargParameter: IrExpression?
|
||||
) = handleArgumentForVarargParameter(argumentForVarargParameter) { variable, elements ->
|
||||
if (variable == null) {
|
||||
unwrapVariadicArguments(elements).forEach {
|
||||
addArgument(it, it.type, variadic = true, parameter = null)
|
||||
}
|
||||
} else {
|
||||
// See comment in [handleArgumentForVarargParameter].
|
||||
// Array for this vararg parameter is already computed before the call,
|
||||
// so query statically known typed arguments from this array.
|
||||
|
||||
with(irBuilder) {
|
||||
val argumentTypes = unwrapVariadicArguments(elements).map { it.type }
|
||||
argumentTypes.forEachIndexed { index, type ->
|
||||
val untypedArgument = irCall(symbols.arrayGet[symbols.array]!!.owner).apply {
|
||||
dispatchReceiver = irGet(variable)
|
||||
putValueArgument(0, irInt(index))
|
||||
}
|
||||
val argument = irAs(untypedArgument, type) // Note: this cast always succeeds.
|
||||
addArgument(argument, type, variadic = true, parameter = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.unwrapVariadicArguments(
|
||||
elements: List<IrVarargElement>
|
||||
): List<IrExpression> = elements.flatMap {
|
||||
when (it) {
|
||||
is IrExpression -> listOf(it)
|
||||
is IrSpreadElement -> {
|
||||
val expression = it.expression
|
||||
if (expression is IrCall && expression.symbol == symbols.arrayOf) {
|
||||
handleArgumentForVarargParameter(expression.getValueArgument(0)) { _, elements ->
|
||||
unwrapVariadicArguments(elements)
|
||||
}
|
||||
} else {
|
||||
stubs.reportError(it, "When calling variadic " +
|
||||
if (isObjCMethod) "Objective-C methods" else "C functions " +
|
||||
"spread operator is supported only for *arrayOf(...)")
|
||||
}
|
||||
}
|
||||
else -> stubs.throwCompilerError(it, "unexpected IrVarargElement")
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R> KotlinToCCallBuilder.handleArgumentForVarargParameter(
|
||||
argument: IrExpression?,
|
||||
block: (variable: IrVariable?, elements: List<IrVarargElement>) -> R
|
||||
): R = when (argument) {
|
||||
|
||||
null -> block(null, emptyList())
|
||||
|
||||
is IrVararg -> block(null, argument.elements)
|
||||
|
||||
is IrGetValue -> {
|
||||
/* This is possible when using named arguments with reordering, i.e.
|
||||
*
|
||||
* foo(second = *arrayOf(...), first = ...)
|
||||
*
|
||||
* psi2ir generates as
|
||||
*
|
||||
* val secondTmp = *arrayOf(...)
|
||||
* val firstTmp = ...
|
||||
* foo(firstTmp, secondTmp)
|
||||
*
|
||||
*
|
||||
**/
|
||||
|
||||
val variable = argument.symbol.owner
|
||||
if (variable is IrVariable && variable.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE && !variable.isVar) {
|
||||
val initializer = variable.initializer
|
||||
if (initializer is IrVararg) {
|
||||
block(variable, initializer.elements)
|
||||
} else {
|
||||
stubs.throwCompilerError(initializer, "unexpected initializer")
|
||||
}
|
||||
} else {
|
||||
stubs.throwCompilerError(variable, "unexpected value declaration")
|
||||
}
|
||||
}
|
||||
|
||||
else -> stubs.throwCompilerError(argument, "unexpected vararg")
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.emitCBridge() {
|
||||
val cLines = mutableListOf<String>()
|
||||
|
||||
|
||||
+4
@@ -85,6 +85,10 @@ internal abstract class BaseInteropIrTransformer(private val context: Context) :
|
||||
|
||||
override fun reportError(location: IrElement, message: String): Nothing =
|
||||
context.reportCompilationError(message, irFile, location)
|
||||
|
||||
override fun throwCompilerError(element: IrElement?, message: String): Nothing {
|
||||
error(irFile, element, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -281,6 +281,22 @@ fun testVarargs() {
|
||||
0.1.toFloat(), 0.2, true, false
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"1 2 3",
|
||||
TestVarargs(
|
||||
format = "%d %d %d",
|
||||
args = *arrayOf(1, 2, 3)
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"4 5 6",
|
||||
TestVarargs(
|
||||
args = *arrayOf(4, *arrayOf(5, 6)),
|
||||
format = "%d %d %d"
|
||||
).formatted
|
||||
)
|
||||
}
|
||||
|
||||
private class MyException : Throwable()
|
||||
|
||||
Reference in New Issue
Block a user