[JS IR] Constructor call with vararg invoking with apply
[JS IR] Nullize external empty varargs [JS IR] Concat varargs with array of nonVarargs arguments ^KT-42357 fixed
This commit is contained in:
+27
-11
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
@@ -156,14 +153,33 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
// Argument value constructs unboxed inline class instance
|
||||
arguments.single()
|
||||
} else {
|
||||
val ref = when {
|
||||
klass.isEffectivelyExternal() ->
|
||||
context.getRefForExternalClass(klass)
|
||||
|
||||
else ->
|
||||
context.getNameForClass(klass).makeRef()
|
||||
when {
|
||||
klass.isEffectivelyExternal() -> {
|
||||
val refForExternalClass = context.getRefForExternalClass(klass)
|
||||
val varargParameterIndex = expression.symbol.owner.varargParameterIndex()
|
||||
if (varargParameterIndex == -1) {
|
||||
JsNew(refForExternalClass, arguments)
|
||||
} else {
|
||||
val argumentsAsSingleArray = argumentsAsSingleArray(
|
||||
JsNullLiteral(),
|
||||
arguments,
|
||||
varargParameterIndex
|
||||
)
|
||||
JsNew(
|
||||
JsInvocation(
|
||||
JsNameRef("apply", JsNameRef("bind", JsNameRef("Function"))),
|
||||
refForExternalClass,
|
||||
argumentsAsSingleArray
|
||||
),
|
||||
emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val ref = context.getNameForClass(klass).makeRef()
|
||||
JsNew(ref, arguments)
|
||||
}
|
||||
}
|
||||
JsNew(ref, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+64
-22
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
@@ -120,7 +121,7 @@ fun translateCall(
|
||||
return JsInvocation(callRef, jsDispatchReceiver?.let { receiver -> listOf(receiver) + arguments } ?: arguments)
|
||||
}
|
||||
|
||||
val varargParameterIndex = function.valueParameters.indexOfFirst { it.varargElementType != null }
|
||||
val varargParameterIndex = function.varargParameterIndex()
|
||||
val isExternalVararg = function.isEffectivelyExternal() && varargParameterIndex != -1
|
||||
|
||||
val symbolName = when (jsDispatchReceiver) {
|
||||
@@ -134,28 +135,12 @@ fun translateCall(
|
||||
}
|
||||
|
||||
return if (isExternalVararg) {
|
||||
|
||||
// External vararg arguments should be represented in JS as multiple "plain" arguments (opposed to arrays in Kotlin)
|
||||
// We are using `Function.prototype.apply` function to pass all arguments as a single array.
|
||||
// For this purpose are concatenating non-vararg arguments with vararg.
|
||||
// TODO: Don't use `Function.prototype.apply` when number of arguments is known at compile time (e.g. there are no spread operators)
|
||||
val arrayConcat = JsNameRef("concat", JsArrayLiteral())
|
||||
val arraySliceCall = JsNameRef("call", JsNameRef("slice", JsArrayLiteral()))
|
||||
|
||||
val argumentsAsSingleArray = JsInvocation(
|
||||
arrayConcat,
|
||||
listOfNotNull(jsExtensionReceiver) + arguments.mapIndexed { index, argument ->
|
||||
when (index) {
|
||||
|
||||
// Call `Array.prototype.slice` on vararg arguments in order to convert array-like objects into proper arrays
|
||||
// TODO: Optimize for proper arrays
|
||||
varargParameterIndex -> JsInvocation(arraySliceCall, argument)
|
||||
|
||||
// TODO: Don't wrap non-array-like arguments with array literal
|
||||
// TODO: Wrap adjacent non-vararg arguments in a single array literal
|
||||
else -> JsArrayLiteral(listOf(argument))
|
||||
}
|
||||
}
|
||||
val argumentsAsSingleArray = argumentsAsSingleArray(
|
||||
jsExtensionReceiver,
|
||||
arguments,
|
||||
varargParameterIndex
|
||||
)
|
||||
|
||||
if (jsDispatchReceiver != null) {
|
||||
@@ -198,13 +183,60 @@ fun translateCall(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun argumentsAsSingleArray(
|
||||
additionalReceiver: JsExpression?,
|
||||
arguments: List<JsExpression>,
|
||||
varargParameterIndex: Int?
|
||||
): JsExpression {
|
||||
// External vararg arguments should be represented in JS as multiple "plain" arguments (opposed to arrays in Kotlin)
|
||||
// We are using `Function.prototype.apply` function to pass all arguments as a single array.
|
||||
// For this purpose are concatenating non-vararg arguments with vararg.
|
||||
var varargArgument: JsExpression? = null
|
||||
val additionalSize = additionalReceiver?.let { 1 } ?: 0
|
||||
// size + 1 because arguments size + potential additionalReceiver
|
||||
val nonVarArgs: MutableList<JsExpression> =
|
||||
ArrayList<JsExpression>(arguments.size + additionalSize).apply {
|
||||
additionalReceiver?.let { add(it) }
|
||||
}
|
||||
|
||||
arguments
|
||||
.forEachIndexed { index, argument ->
|
||||
when (index) {
|
||||
|
||||
// Call `Array.prototype.slice` on vararg arguments in order to convert array-like objects into proper arrays
|
||||
varargParameterIndex -> {
|
||||
varargArgument = if (argument is JsArrayLiteral) {
|
||||
argument
|
||||
} else {
|
||||
val arraySliceCall = JsNameRef("call", JsNameRef("slice", JsArrayLiteral()))
|
||||
JsInvocation(arraySliceCall, argument)
|
||||
}
|
||||
}
|
||||
|
||||
else -> nonVarArgs.add(argument)
|
||||
}
|
||||
}
|
||||
|
||||
val nonVarArgArrayLiteral = JsArrayLiteral(nonVarArgs)
|
||||
return varargArgument?.let {
|
||||
JsInvocation(
|
||||
JsNameRef("concat", nonVarArgArrayLiteral),
|
||||
it
|
||||
)
|
||||
} ?: nonVarArgArrayLiteral
|
||||
}
|
||||
|
||||
fun IrFunction.varargParameterIndex() = valueParameters.indexOfFirst { it.varargElementType != null }
|
||||
|
||||
fun translateCallArguments(
|
||||
expression: IrMemberAccessExpression<*>,
|
||||
expression: IrMemberAccessExpression<IrFunctionSymbol>,
|
||||
context: JsGenerationContext,
|
||||
transformer: IrElementToJsExpressionTransformer,
|
||||
): List<JsExpression> {
|
||||
val size = expression.valueArgumentsCount
|
||||
|
||||
val varargParameterIndex = expression.symbol.owner.realOverrideTarget.varargParameterIndex()
|
||||
|
||||
val validWithNullArgs = expression.validWithNullArgs()
|
||||
val arguments = (0 until size)
|
||||
.mapTo(ArrayList(size)) { index ->
|
||||
@@ -216,6 +248,16 @@ fun translateCallArguments(
|
||||
assert(validWithNullArgs)
|
||||
}
|
||||
}
|
||||
.mapIndexed { index, result ->
|
||||
val isEmptyExternalVararg = validWithNullArgs &&
|
||||
varargParameterIndex == index &&
|
||||
result is JsArrayLiteral &&
|
||||
result.expressions.isEmpty()
|
||||
|
||||
if (isEmptyExternalVararg) {
|
||||
null
|
||||
} else result
|
||||
}
|
||||
.dropLastWhile { it == null }
|
||||
.map { it ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user