From e7816b4ec2fa9e357bf5408df275a740af2c2765 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Thu, 20 Feb 2020 15:22:01 +0300 Subject: [PATCH] JS: support callable references with vararg and default parameters conversion --- .../bothWithCoercionToUnit.kt | 1 - .../varargAndDefaults/boundReferences.kt | 2 +- .../varargAndDefaults/defaultAfterVararg.kt | 1 - .../emptyVarargAndDefault.kt | 1 - .../varargAndDefaults/inlineVararg.kt | 1 - .../inlineVarargAndDefault.kt | 1 - .../varargAndDefaults/inlineVarargInts.kt | 1 - .../innerConstructorWithVararg.kt | 1 - .../varargAndDefaults/largeVararg.kt | 1 - .../localFunctionWithDefault.kt | 1 - .../manyDefaultsAndVararg.kt | 1 - .../varargAndDefaults/simpleEmptyVararg.kt | 1 - .../varargAndDefaults/unboundReferences.kt | 50 +++++++++++ .../varargViewedAsPrimitiveArray.kt | 1 - .../varargWithDefaultValue.kt | 1 - .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++ .../LightAnalysisModeTestGenerated.java | 5 ++ .../ir/FirBlackBoxCodegenTestGenerated.java | 5 ++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++ .../IrJsCodegenBoxTestGenerated.java | 5 ++ .../semantics/JsCodegenBoxTestGenerated.java | 5 ++ .../reference/CallArgumentTranslator.kt | 2 +- .../reference/CallableReferenceTranslator.kt | 88 +++++++++++++------ .../testData/box/native/vararg.kt | 8 ++ 24 files changed, 152 insertions(+), 41 deletions(-) create mode 100644 compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/bothWithCoercionToUnit.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/bothWithCoercionToUnit.kt index d5c771d672a..fbb88415904 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/bothWithCoercionToUnit.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/bothWithCoercionToUnit.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun foo(s: String = "kotlin", vararg t: String): Boolean { if (s != "kotlin") throw AssertionError(s) diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/boundReferences.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/boundReferences.kt index bcb54e324a1..e8fa1549a92 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/boundReferences.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/boundReferences.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS // WITH_RUNTIME import kotlin.test.assertEquals @@ -44,5 +43,6 @@ fun box(): String { test(C(42)::extensionVararg, 42) test(C(42)::extensionDefault, 42) test(C(42)::extensionBoth, 42) + return "OK" } diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/defaultAfterVararg.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/defaultAfterVararg.kt index 8402be239e4..acf59a33b9f 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/defaultAfterVararg.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/defaultAfterVararg.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun foo(vararg a: String, result: String = "OK"): String = if (a.size == 0) result else "Fail" diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt index fc85194457a..9fcb33afc52 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun foo(x: String = "O", vararg y: String): String = if (y.size == 0) x + "K" else "Fail" diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt index 0881d3fd117..b470cc7b803 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt @@ -2,7 +2,6 @@ // IGNORE_BACKEND_FIR: JVM_IR // WITH_RUNTIME // KJS_WITH_FULL_RUNTIME -// IGNORE_BACKEND: JS inline fun foo(mkString: (Char, Char) -> String): String = mkString('O','K') diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt index 5fa3366754d..e53c0238a15 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun foo(vararg l: Long, s: String = "OK"): String = if (l.size == 0) s else "Fail" diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt index 75e3354f16e..73c84bb74e1 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt @@ -2,7 +2,6 @@ // IGNORE_BACKEND_FIR: JVM_IR // WITH_RUNTIME // KJS_WITH_FULL_RUNTIME -// IGNORE_BACKEND: JS inline fun foo(x: (Int, Int) -> Int): Int = x(120,3) diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/innerConstructorWithVararg.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/innerConstructorWithVararg.kt index 65fe243ff5a..836250e00fa 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/innerConstructorWithVararg.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/innerConstructorWithVararg.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS class Outer(val o: String) { diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/largeVararg.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/largeVararg.kt index fe55bd43fe6..bc14da0c82b 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/largeVararg.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/largeVararg.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun foo( f: ( diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/localFunctionWithDefault.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/localFunctionWithDefault.kt index 20a6adcbe66..5ffc9ea741e 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/localFunctionWithDefault.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/localFunctionWithDefault.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun call0(f: (String) -> String, x: String): String = f(x) fun call1(f: (String, String) -> String, x: String, y: String): String = f(x, y) diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/manyDefaultsAndVararg.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/manyDefaultsAndVararg.kt index b8eec0ae69f..4858662b33d 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/manyDefaultsAndVararg.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/manyDefaultsAndVararg.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt index 68c348b1a5b..e61ff31dd06 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun foo(x: String, vararg y: String): String = if (y.size == 0) x + "K" else "Fail" diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt new file mode 100644 index 00000000000..695418b2801 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt @@ -0,0 +1,50 @@ +// !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType +// IGNORE_BACKEND_FIR: JVM_IR +// IGNORE_BACKEND: JVM_IR, JS_IR +// WITH_RUNTIME + +import kotlin.test.assertEquals + +class C(val expected: Int) { + fun memberVararg(i: Int, vararg s: String) { + assertEquals(expected, i) + assertEquals(0, s.size) + } + + fun memberDefault(i: Int, s: String = "") { + assertEquals(expected, i) + assertEquals("", s) + } + + fun memberBoth(i: Int, s: String = "", vararg t: String) { + assertEquals(expected, i) + assertEquals("", s) + assertEquals(0, t.size) + } +} + +fun C.extensionVararg(i: Int, vararg s: String) { + memberVararg(i, *s) +} + +fun C.extensionDefault(i: Int, s: String = "") { + memberDefault(i, s) +} + +fun C.extensionBoth(i: Int, s: String = "", vararg t: String) { + memberBoth(i, s, *t) +} + +fun test(f: C.(Int) -> Unit, p: Int) = C(p).f(p) + +fun box(): String { + + test(C::memberVararg, 43) + test(C::memberDefault, 43) + test(C::memberBoth, 43) + test(C::extensionVararg, 43) + test(C::extensionDefault, 43) + test(C::extensionBoth, 43) + + return "OK" +} diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt index 9cbac0a95c9..f4ac91d044d 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsPrimitiveArray.kt @@ -1,6 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR // !LANGUAGE: +NewInference -// DONT_RUN_GENERATED_CODE: JS // IGNORE_BACKEND_FIR: JVM_IR fun sum(vararg args: Int): Int { diff --git a/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt b/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt index e0c6a271f28..435ed1162ea 100644 --- a/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt +++ b/compiler/testData/codegen/box/callableReference/varargAndDefaults/varargWithDefaultValue.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JS fun foo(x: Int, s: Int, vararg y: CharSequence = arrayOf("Aaa")): String = if (y.size == s && y[0].length == x) "OK" else "Fail" diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 7c0c63f73dc..03f2d8e0766 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2833,6 +2833,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt"); } + @TestMetadata("unboundReferences.kt") + public void testUnboundReferences() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt"); + } + @TestMetadata("varargViewedAsArray.kt") public void testVarargViewedAsArray() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index dc4b1b946cc..16533cdb539 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -2833,6 +2833,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt"); } + @TestMetadata("unboundReferences.kt") + public void testUnboundReferences() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt"); + } + @TestMetadata("varargViewedAsArray.kt") public void testVarargViewedAsArray() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index f1553a96167..bfe6eae8c9e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -2813,6 +2813,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt"); } + @TestMetadata("unboundReferences.kt") + public void testUnboundReferences() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt"); + } + @TestMetadata("varargViewedAsArray.kt") public void testVarargViewedAsArray() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b3e54740692..42138f47631 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -2813,6 +2813,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt"); } + @TestMetadata("unboundReferences.kt") + public void testUnboundReferences() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt"); + } + @TestMetadata("varargViewedAsArray.kt") public void testVarargViewedAsArray() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 86ac196190a..7a5e939402a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -2213,6 +2213,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt"); } + @TestMetadata("unboundReferences.kt") + public void testUnboundReferences() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt"); + } + @TestMetadata("varargViewedAsArray.kt") public void testVarargViewedAsArray() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 88512640383..af13da6b9ad 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -2213,6 +2213,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/simpleEmptyVararg.kt"); } + @TestMetadata("unboundReferences.kt") + public void testUnboundReferences() throws Exception { + runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/unboundReferences.kt"); + } + @TestMetadata("varargViewedAsArray.kt") public void testVarargViewedAsArray() throws Exception { runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/varargViewedAsArray.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt index 0dba5e1954e..d56d0ee1ed2 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt @@ -233,7 +233,7 @@ class CallArgumentTranslator private constructor( val arguments = resolvedArgument.arguments if (arguments.isEmpty()) { return if (shouldWrapVarargInArray) { - return toArray(varargElementType, listOf()).wrapInUArray(varargElementType) + return toArray(varargElementType, mutableListOf()).wrapInUArray(varargElementType) } else { null } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt index 7da092f33db..9477ae26c82 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt @@ -28,13 +28,12 @@ import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.utils.* import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getFunctionResolvedCallWithAssert import org.jetbrains.kotlin.resolve.calls.callUtil.getPropertyResolvedCallWithAssert import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert -import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument +import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue +import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.util.CallMaker import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension @@ -74,18 +73,57 @@ object CallableReferenceTranslator { } private fun translateForFunction( - descriptor: FunctionDescriptor, - context: TranslationContext, - expression: KtCallableReferenceExpression, - receiver: JsExpression? + descriptor: FunctionDescriptor, + context: TranslationContext, + expression: KtCallableReferenceExpression, + receiver: JsExpression? ): JsExpression { val realResolvedCall = expression.callableReference.getFunctionResolvedCallWithAssert(context.bindingContext()) - val fakeExpression = CodegenUtil.constructFakeFunctionCall(expression.project, descriptor.valueParameters.size) + val functionDescriptor = context.bindingContext().get(BindingContext.FUNCTION, expression)!! - val fakeCall = CallMaker.makeCall(fakeExpression, null, null, fakeExpression, fakeExpression.valueArguments) + val receivers = + if (receiver == null && (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)) 1 else 0 + val fakeArgCount = functionDescriptor.valueParameters.size - receivers + + val fakeExpression = CodegenUtil.constructFakeFunctionCall(expression.project, fakeArgCount) + val fakeArguments = fakeExpression.valueArguments + + val fakeCall = CallMaker.makeCall(fakeExpression, null, null, fakeExpression, fakeArguments) val fakeResolvedCall = object : DelegatingResolvedCall(realResolvedCall) { - val valueArgumentList = fakeCall.valueArguments.map(::ExpressionValueArgument) - val valueArgumentMap = valueArgumentList.withIndex().associate { (index, arg) -> descriptor.valueParameters[index] to arg } + val valueArgumentMap = mutableMapOf().also { argumentMap -> + var i = 0 + + for (parameter in descriptor.valueParameters) { + if (parameter.varargElementType != null) { + // Two cases are possible for a function reference with a vararg parameter of type T: either several arguments + // of type T are bound to that parameter, or one argument of type Array. In the former case the argument + // is bound as a VarargValueArgument, in the latter it's an ExpressionValueArgument + if (i == fakeArgCount) { + // If we've exhausted the argument list of the reference and we still have one vararg parameter left, + // we should use its default value if present, or simply an empty vararg instead + argumentMap[parameter] = + if (parameter.hasDefaultValue()) DefaultValueArgument.DEFAULT else VarargValueArgument() + continue + } + if (functionDescriptor.valueParameters[receivers + i].type == parameter.varargElementType) { + argumentMap[parameter] = VarargValueArgument(fakeArguments.subList(i, fakeArgCount)) + i = fakeArgCount + continue + } + } + if (i < fakeArgCount) { + argumentMap[parameter] = ExpressionValueArgument(fakeArguments.get(i++)) + } else { + assert(parameter.hasDefaultValue()) { + "Parameter should be either vararg or expression or default: " + parameter + + " (reference in: " + functionDescriptor.containingDeclaration + ")" + } + argumentMap[parameter] = DefaultValueArgument.DEFAULT + } + } + } + + val valueArgumentList = valueArgumentMap.values.toList() override fun getCall() = fakeCall @@ -96,8 +134,7 @@ object CallableReferenceTranslator { override fun getExplicitReceiverKind(): ExplicitReceiverKind { if (receiver != null) { return if (descriptor.isExtension) ExplicitReceiverKind.EXTENSION_RECEIVER else ExplicitReceiverKind.DISPATCH_RECEIVER - } - else { + } else { return super.getExplicitReceiverKind() } } @@ -106,36 +143,35 @@ object CallableReferenceTranslator { val function = JsFunction(context.scope(), JsBlock(), "") function.source = expression val receiverParam = if (descriptor.dispatchReceiverParameter != null || - descriptor.extensionReceiverParameter != null || - receiver != null) { + descriptor.extensionReceiverParameter != null || + receiver != null + ) { val paramName = JsScope.declareTemporaryName(Namer.getReceiverParameterName()) function.parameters += JsParameter(paramName) paramName.makeRef() - } - else { + } else { null } - val functionDescriptor = realResolvedCall.resultingDescriptor val aliases = mutableMapOf() for ((index, valueArg) in fakeCall.valueArguments.withIndex()) { - val paramName = JsScope.declareTemporaryName(descriptor.valueParameters[index].name.asString()) + val paramName = JsScope.declareTemporaryName(functionDescriptor.valueParameters[index].name.asString()) function.parameters += JsParameter(paramName) val paramRef = paramName.makeRef() paramRef.type = context.currentModule.builtIns.anyType - val type = functionDescriptor.valueParameters[index].type - aliases[valueArg.getArgumentExpression()!!] = TranslationUtils.coerce(context, paramRef, type) + aliases[valueArg.getArgumentExpression()!!] = paramRef } - var functionContext = context.innerBlock(function.body).innerContextWithAliasesForExpressions(aliases).inner(functionDescriptor) + var functionContext = context.innerBlock(function.body).innerContextWithAliasesForExpressions(aliases).inner(descriptor) functionContext.continuationParameterDescriptor?.let { continuationDescriptor -> function.parameters += JsParameter(context.getNameForDescriptor(continuationDescriptor)) - functionContext = functionContext.innerContextWithDescriptorsAliased(mapOf(continuationDescriptor to JsAstUtils.stateMachineReceiver())) + functionContext = + functionContext.innerContextWithDescriptorsAliased(mapOf(continuationDescriptor to JsAstUtils.stateMachineReceiver())) } - if (functionDescriptor.isSuspend) { - function.fillCoroutineMetadata(functionContext, descriptor, hasController = false) + if (descriptor.isSuspend) { + function.fillCoroutineMetadata(functionContext, functionDescriptor, hasController = false) } val invocation = CallTranslator.translate(functionContext, fakeResolvedCall, receiverParam) diff --git a/js/js.translator/testData/box/native/vararg.kt b/js/js.translator/testData/box/native/vararg.kt index 8ad118457e6..873be2673a3 100644 --- a/js/js.translator/testData/box/native/vararg.kt +++ b/js/js.translator/testData/box/native/vararg.kt @@ -63,6 +63,8 @@ external fun oneMoreParamCount(before: IntArray, vararg middle: Int, after: IntA @JsName("paramCount") external fun oneMoreGenericParamCount(before: Array, vararg middle: T, after: Array): Int +fun runCallable(fn: (Int, Int, Int, Int, Int) -> Int, a1: Int, a2: Int, a3: Int, a4: Int, a5: Int): Int = fn(a1, a2, a3, a4, a5) + fun box(): String { if (paramCount() != 0) return "failed when call native function without args" @@ -147,5 +149,11 @@ fun box(): String { assertEquals(6, oneMoreParamCount(intArrayOf(1, 2), 3, *intArrayOf(4, 5), 6, after = intArrayOf(7, 8))) assertEquals(6, oneMoreGenericParamCount(arrayOf("1", "2"), "3", *arrayOf("4", "5"), "6", after = arrayOf("7", "8"))) + + assertEquals(5, runCallable(::paramCount, 1, 2, 3, 4, 5)) + assertEquals(5, runCallable(::anotherParamCount, 1, 2, 3, 4, 5)) + assertEquals(5, runCallable(::anotherCount, 1, 2, 3, 4, 5)) + assertEquals(11111, runCallable(::sumOfParameters, 1, 10, 100, 1000, 10000)) + return "OK" } \ No newline at end of file