WASM: Implement spread operator

This commit is contained in:
Igor Laevsky
2021-06-27 15:04:12 +03:00
committed by teamcityserver
parent f5e59194b5
commit d835b3c164
22 changed files with 282 additions and 68 deletions
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="igor">
<words>
<w>descr</w>
</words>
</dictionary>
</component>
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
@@ -29,6 +30,8 @@ class WasmSymbols(
private val wasmInternalPackage: PackageViewDescriptor =
context.module.getPackage(FqName("kotlin.wasm.internal"))
private val collectionsPackage: PackageViewDescriptor =
context.module.getPackage(StandardNames.COLLECTIONS_PACKAGE_FQ_NAME)
override val throwNullPointerException = getInternalFunction("THROW_NPE")
override val throwISE = getInternalFunction("THROW_ISE")
@@ -152,6 +155,9 @@ class WasmSymbols(
}
}
val arraysCopyInto = findFunctions(collectionsPackage.memberScope, Name.identifier("copyInto"))
.map { symbolTable.referenceSimpleFunction(it) }
override fun functionN(n: Int): IrClassSymbol =
functionNInterfaces[n]
@@ -7,23 +7,20 @@ package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irComposite
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.expressions.IrVarargElement
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
import java.lang.IllegalArgumentException
internal class WasmVarargExpressionLowering(
private val context: WasmBackendContext
@@ -34,36 +31,183 @@ internal class WasmVarargExpressionLowering(
irFile.transformChildrenVoid(this)
}
override fun visitVararg(expression: IrVararg): IrExpression {
val irVararg = super.visitVararg(expression) as IrVararg
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol)
val arrayClass = irVararg.type.classOrNull!!.owner
val primaryConstructor = arrayClass.primaryConstructor!!
val setMethod = arrayClass.declarations.filterIsInstance<IrSimpleFunction>().find {
it.name == Name.identifier("set")
}!!
return builder.irComposite(irVararg) {
val arrayTempVariable = irTemporary(
value = irCall(primaryConstructor).apply {
putValueArgument(0, irInt(irVararg.elements.size))
if (primaryConstructor.typeParameters.isNotEmpty()) {
check(primaryConstructor.typeParameters.size == 1)
putTypeArgument(0, irVararg.varargElementType)
}
},
nameHint = "array_tmp"
)
for ((index: Int, element: IrVarargElement) in irVararg.elements.withIndex()) {
check(element is IrExpression) {
"TODO: Support $element as vararg elements"
// Helper which wraps an array class and allows to access it's commonly used methods.
private class ArrayDescr(val arrayType: IrType, val context: WasmBackendContext) {
val arrayClass =
arrayType.getClass() ?: throw IllegalArgumentException("Argument ${arrayType.render()} must have a class")
init {
check(arrayClass.symbol in context.wasmSymbols.arrays) { "Argument ${ir2string(arrayClass)} must be an array" }
}
val primaryConstructor
get() = arrayClass.primaryConstructor!!
val setMethod
get() = arrayClass.getSimpleFunction("set")!!.owner
val getMethod
get() = arrayClass.getSimpleFunction("get")!!.owner
val sizeMethod
get() = arrayClass.getPropertyGetter("size")!!.owner
val elementType: IrType
get() {
if (arrayType.isBoxedArray)
return arrayType.getArrayElementType(context.irBuiltIns)
// getArrayElementType doesn't work on unsigned arrays, use workaround instead
return getMethod.returnType
}
val copyInto: IrSimpleFunction
get() {
val func = context.wasmSymbols.arraysCopyInto.find {
it.owner.extensionReceiverParameter?.type?.classOrNull?.owner == arrayClass
}
+irCall(setMethod).apply {
dispatchReceiver = irGet(arrayTempVariable)
putValueArgument(0, irInt(index))
putValueArgument(1, element)
return func?.owner ?: throw IllegalArgumentException("copyInto is not found for ${arrayType.render()}")
}
}
private fun IrBlockBuilder.irCreateArray(size: IrExpression, arrDescr: ArrayDescr) =
irCall(arrDescr.primaryConstructor).apply {
putValueArgument(0, size)
if (typeArgumentsCount >= 1) {
check(typeArgumentsCount == 1 && arrDescr.arrayClass.typeParameters.size == 1)
putTypeArgument(0, arrDescr.elementType)
}
type = arrDescr.arrayType
}
// Represents single contiguous sequence of vararg arguments. It can generate IR for various operations on this
// segments. It's used to handle spreads and normal vararg arguments in a uniform manner.
private sealed class VarargSegmentBuilder(val wasmContext: WasmBackendContext) {
// Returns an expression which calculates size of this spread.
abstract fun IrBlockBuilder.irSize(): IrExpression
// Adds code into the current block which copies this spread into destArr. Returns nothing.
// If indexVar is present uses it as a start index in the destination array.
abstract fun IrBlockBuilder.irCopyInto(destArr: IrVariable, indexVar: IrVariable?)
class Plain(val exprs: List<IrVariable>, wasmContext: WasmBackendContext) :
VarargSegmentBuilder(wasmContext) {
override fun IrBlockBuilder.irSize() = irInt(exprs.size)
override fun IrBlockBuilder.irCopyInto(destArr: IrVariable, indexVar: IrVariable?) {
val destArrDescr = ArrayDescr(destArr.type, wasmContext)
// An infinite sequence of natural numbers possibly shifted by the indexVar when it's available
val indexes = generateSequence(0) { it + 1 }
.map { irInt(it) }
.let { seq ->
if (indexVar != null) seq.map { irIntPlus(irGet(indexVar), it, wasmContext) }
else seq
}
for ((element, index) in exprs.asSequence().zip(indexes)) {
+irCall(destArrDescr.setMethod).apply {
dispatchReceiver = irGet(destArr)
putValueArgument(0, index)
putValueArgument(1, irGet(element))
}
}
}
}
class Spread(val exprVar: IrVariable, wasmContext: WasmBackendContext) :
VarargSegmentBuilder(wasmContext) {
val srcArrDescr = ArrayDescr(exprVar.type, wasmContext) // will check that exprVar is an array
override fun IrBlockBuilder.irSize(): IrExpression =
irCall(srcArrDescr.sizeMethod).apply {
dispatchReceiver = irGet(exprVar)
}
override fun IrBlockBuilder.irCopyInto(destArr: IrVariable, indexVar: IrVariable?) {
assert(srcArrDescr.arrayClass == destArr.type.getClass()) { "type checker failure?" }
val destIdx = indexVar?.let { irGet(it) } ?: irInt(0)
+irCall(srcArrDescr.copyInto).apply {
if (typeArgumentsCount >= 1) {
check(typeArgumentsCount == 1 && srcArrDescr.arrayClass.typeParameters.size == 1)
putTypeArgument(0, srcArrDescr.elementType)
}
extensionReceiver = irGet(exprVar) // source
putValueArgument(0, irGet(destArr)) // destination
putValueArgument(1, destIdx) // destinationOffset
putValueArgument(2, irInt(0)) // startIndex
putValueArgument(3, irSize()) // endIndex
}
}
}
}
// This is needed to setup proper extension and dispatch receivers for the VarargSegmentBuilder.
private fun IrBlockBuilder.irCopyInto(destArr: IrVariable, indexVar: IrVariable?, segment: VarargSegmentBuilder) =
with(segment) {
this@irCopyInto.irCopyInto(destArr, indexVar)
}
private fun IrBlockBuilder.irSize(segment: VarargSegmentBuilder) =
with(segment) {
this@irSize.irSize()
}
override fun visitVararg(expression: IrVararg): IrExpression {
val irVararg = super.visitVararg(expression) as IrVararg
// Create temporary variable for each element and emit them all at once to preserve
// argument evaluation order as per kotlin language spec.
val elementVars = irVararg.elements
.map {
val exp = if (it is IrSpreadElement) it.expression else (it as IrExpression)
currentScope!!.scope.createTemporaryVariable(exp, "vararg_temp")
}
val segments: List<VarargSegmentBuilder> = sequence {
val currentElements = mutableListOf<IrVariable>()
for ((el, tempVar) in irVararg.elements.zip(elementVars)) {
when (el) {
is IrExpression -> currentElements.add(tempVar)
is IrSpreadElement -> {
if (currentElements.isNotEmpty()) {
yield(VarargSegmentBuilder.Plain(currentElements.toList(), context))
currentElements.clear()
}
yield(VarargSegmentBuilder.Spread(tempVar, context))
}
}
}
if (currentElements.isNotEmpty())
yield(VarargSegmentBuilder.Plain(currentElements.toList(), context))
}.toList()
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol)
val destArrayDescr = ArrayDescr(irVararg.type, context)
return builder.irComposite(irVararg) {
// Emit all of the variables first so that all vararg expressions
// are evaluated only once and in order of their appearance.
elementVars.forEach { +it }
val arrayLength = segments
.map { irSize(it) }
.reduceOrNull { acc, exp -> irIntPlus(acc, exp) }
?: irInt(0)
val arrayTempVariable = irTemporary(
value = irCreateArray(arrayLength, destArrayDescr),
nameHint = "vararg_array")
val indexVar = if (segments.size >= 2) irTemporary(irInt(0), "vararg_idx") else null
segments.forEach {
irCopyInto(arrayTempVariable, indexVar, it)
if (indexVar != null)
+irSet(indexVar, irIntPlus(irGet(indexVar), irSize(it)))
}
+irGet(arrayTempVariable)
}
}
@@ -97,4 +241,18 @@ internal class WasmVarargExpressionLowering(
}
return expression
}
}
private fun IrBlockBuilder.irIntPlus(rhs: IrExpression, lhs: IrExpression): IrExpression =
irIntPlus(rhs, lhs, this@WasmVarargExpressionLowering.context)
}
private fun IrBlockBuilder.irIntPlus(rhs: IrExpression, lhs: IrExpression, wasmContext: WasmBackendContext): IrExpression {
val plusOp = wasmContext.wasmSymbols.getBinaryOperator(
OperatorNameConventions.PLUS, context.irBuiltIns.intType, context.irBuiltIns.intType
).owner
return irCall(plusOp).apply {
dispatchReceiver = rhs
putValueArgument(0, lhs)
}
}
@@ -135,6 +135,9 @@ fun IrBuilderWithScope.irGet(variable: IrValueDeclaration) = irGet(variable.type
fun IrBuilderWithScope.irSet(variable: IrValueSymbol, value: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.EQ) =
IrSetValueImpl(startOffset, endOffset, context.irBuiltIns.unitType, variable, value, origin)
fun IrBuilderWithScope.irSet(variable: IrValueDeclaration, value: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.EQ) =
irSet(variable.symbol, value, origin)
fun IrBuilderWithScope.irGetField(receiver: IrExpression?, field: IrField) =
IrGetFieldImpl(startOffset, endOffset, field.symbol, field.type, receiver)
@@ -233,7 +233,7 @@ private fun IrClass.getPropertyDeclaration(name: String): IrProperty? {
return properties.firstOrNull()
}
private fun IrClass.getSimpleFunction(name: String): IrSimpleFunctionSymbol? =
fun IrClass.getSimpleFunction(name: String): IrSimpleFunctionSymbol? =
findDeclaration<IrSimpleFunction> { it.name.asString() == name }?.symbol
fun IrClass.getPropertyGetter(name: String): IrSimpleFunctionSymbol? =
@@ -1,5 +1,6 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// Doesn't fail on WASM backend if lambdas return Int. Need to investigate futher.
// WASM_MUTE_REASON: UNIT_ISSUES
// !LANGUAGE: +UseCorrectExecutionOrderForVarargArguments
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
var result = "fail"
fun foo(vararg xs: Int, s1: String = "", s2: String = "OK") {
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType
// WITH_RUNTIME
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// !LANGUAGE: +NewInference
fun sum(vararg args: Int): Int {
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// !LANGUAGE: +InlineClasses
inline class UInt(val value: Int)
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// !LANGUAGE: +NewInference +MixedNamedArgumentsInTheirOwnPosition
fun foo1(
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// !LANGUAGE: +NewInference +MixedNamedArgumentsInTheirOwnPosition
// See KT-17691: Wrong argument order in resolved call with varargs. (fixed in JVM_IR)
+1 -1
View File
@@ -1,5 +1,5 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// WASM_MUTE_REASON: STDLIB_STRING_BUILDER
// KJS_WITH_FULL_RUNTIME
// WITH_RUNTIME
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
fun join(x: Array<out String>): String {
var result = ""
for (i in x) {
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
abstract class Base(val s: String, vararg ints: Int)
fun foo(s: String, ints: IntArray) = object : Base(ints = *ints, s = s) {}
@@ -1,5 +1,5 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// WASM_MUTE_REASON: UNSIGNED_ARRAYS
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
fun booleanVararg(vararg xs: Boolean) {
if (xs.size != 1 && xs[0] != true) throw AssertionError()
}
-2
View File
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// !LANGUAGE: +AllowAssigningArrayElementsToVarargsInNamedFormForFunctions
fun test(vararg s: String) = s[1] + s.size
-2
View File
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
fun barB(vararg args: Byte) = args
fun barC(vararg args: Char) = args
fun barD(vararg args: Double) = args
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,5 +1,3 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: SPREAD_OPERATOR
fun box(): String {
if (test1() != "") return "fail 1"
if (test1(1) != "1") return "fail 2"
@@ -1342,6 +1342,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt");
}
@TestMetadata("referenceToVarargWithDefaults.kt")
public void testReferenceToVarargWithDefaults() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/referenceToVarargWithDefaults.kt");
}
@TestMetadata("simpleDefaultArgument.kt")
public void testSimpleDefaultArgument() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/simpleDefaultArgument.kt");
@@ -1352,11 +1357,21 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/simpleEmptyVararg.kt");
}
@TestMetadata("unboundReferences.kt")
public void testUnboundReferences() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/unboundReferences.kt");
}
@TestMetadata("varargViewedAsArray.kt")
public void testVarargViewedAsArray() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/varargViewedAsArray.kt");
}
@TestMetadata("varargViewedAsPrimitiveArray.kt")
public void testVarargViewedAsPrimitiveArray() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/varargViewedAsPrimitiveArray.kt");
}
@TestMetadata("varargWithDefaultValue.kt")
public void testVarargWithDefaultValue() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/varargWithDefaultValue.kt");
@@ -7135,6 +7150,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassAsVararg.kt");
}
@TestMetadata("passInlineClassWithSpreadOperatorToVarargs.kt")
public void testPassInlineClassWithSpreadOperatorToVarargs() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt");
}
@TestMetadata("propertyLoweringOrder.kt")
public void testPropertyLoweringOrder() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt");
@@ -9853,6 +9873,16 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/mixedNamedPosition/simple.kt");
}
@TestMetadata("varargs.kt")
public void testVarargs() throws Exception {
runTest("compiler/testData/codegen/box/mixedNamedPosition/varargs.kt");
}
@TestMetadata("varargsEvaluationOrder.kt")
public void testVarargsEvaluationOrder() throws Exception {
runTest("compiler/testData/codegen/box/mixedNamedPosition/varargsEvaluationOrder.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/multiDecl")
@@ -14341,6 +14371,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/secondaryConstructors/superCallSecondary.kt");
}
@TestMetadata("varargs.kt")
public void testVarargs() throws Exception {
runTest("compiler/testData/codegen/box/secondaryConstructors/varargs.kt");
}
@TestMetadata("withoutPrimarySimple.kt")
public void testWithoutPrimarySimple() throws Exception {
runTest("compiler/testData/codegen/box/secondaryConstructors/withoutPrimarySimple.kt");
@@ -14919,6 +14954,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/super/superConstructor/kt18356.kt");
}
@TestMetadata("kt18356_2.kt")
public void testKt18356_2() throws Exception {
runTest("compiler/testData/codegen/box/super/superConstructor/kt18356_2.kt");
}
@TestMetadata("objectExtendsInner.kt")
public void testObjectExtendsInner() throws Exception {
runTest("compiler/testData/codegen/box/super/superConstructor/objectExtendsInner.kt");
@@ -15789,6 +15829,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/vararg"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("doNotCopyImmediatelyCreatedArrays.kt")
public void testDoNotCopyImmediatelyCreatedArrays() throws Exception {
runTest("compiler/testData/codegen/box/vararg/doNotCopyImmediatelyCreatedArrays.kt");
}
@TestMetadata("evaluationOrder.kt")
public void testEvaluationOrder() throws Exception {
runTest("compiler/testData/codegen/box/vararg/evaluationOrder.kt");
@@ -15799,6 +15844,16 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/vararg/kt1978.kt");
}
@TestMetadata("kt37779.kt")
public void testKt37779() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt37779.kt");
}
@TestMetadata("kt6192.kt")
public void testKt6192() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt6192.kt");
}
@TestMetadata("kt796_797.kt")
public void testKt796_797() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt796_797.kt");
@@ -15814,6 +15869,16 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/vararg/singleAssignmentToVarargsInFunction.kt");
}
@TestMetadata("spreadCopiesArray.kt")
public void testSpreadCopiesArray() throws Exception {
runTest("compiler/testData/codegen/box/vararg/spreadCopiesArray.kt");
}
@TestMetadata("varargInFunParam.kt")
public void testVarargInFunParam() throws Exception {
runTest("compiler/testData/codegen/box/vararg/varargInFunParam.kt");
}
@TestMetadata("varargsAndFunctionLiterals.kt")
public void testVarargsAndFunctionLiterals() throws Exception {
runTest("compiler/testData/codegen/box/vararg/varargsAndFunctionLiterals.kt");