JS: support callable references with vararg and default parameters conversion
This commit is contained in:
Vendored
-1
@@ -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)
|
||||
|
||||
+1
-1
@@ -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"
|
||||
}
|
||||
|
||||
-1
@@ -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"
|
||||
|
||||
-1
@@ -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"
|
||||
|
||||
-1
@@ -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')
|
||||
|
||||
Vendored
-1
@@ -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"
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
Vendored
-1
@@ -1,6 +1,5 @@
|
||||
// !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
|
||||
class Outer(val o: String) {
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
// !LANGUAGE: +NewInference
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun foo(
|
||||
f: (
|
||||
|
||||
Vendored
-1
@@ -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)
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
// !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JS
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
-1
@@ -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"
|
||||
|
||||
+50
@@ -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"
|
||||
}
|
||||
Vendored
-1
@@ -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 {
|
||||
|
||||
Vendored
-1
@@ -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"
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Generated
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
+62
-26
@@ -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<FunctionDescriptor>(realResolvedCall) {
|
||||
val valueArgumentList = fakeCall.valueArguments.map(::ExpressionValueArgument)
|
||||
val valueArgumentMap = valueArgumentList.withIndex().associate { (index, arg) -> descriptor.valueParameters[index] to arg }
|
||||
val valueArgumentMap = mutableMapOf<ValueParameterDescriptor, ResolvedValueArgument>().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<out T>. 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<KtExpression, JsExpression>()
|
||||
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)
|
||||
|
||||
@@ -63,6 +63,8 @@ external fun oneMoreParamCount(before: IntArray, vararg middle: Int, after: IntA
|
||||
@JsName("paramCount")
|
||||
external fun <T> oneMoreGenericParamCount(before: Array<T>, vararg middle: T, after: Array<T>): 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"
|
||||
}
|
||||
Reference in New Issue
Block a user