[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.backend.common.ir.isElseBranch
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
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.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
@@ -156,14 +153,33 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
// Argument value constructs unboxed inline class instance
|
// Argument value constructs unboxed inline class instance
|
||||||
arguments.single()
|
arguments.single()
|
||||||
} else {
|
} else {
|
||||||
val ref = when {
|
when {
|
||||||
klass.isEffectivelyExternal() ->
|
klass.isEffectivelyExternal() -> {
|
||||||
context.getRefForExternalClass(klass)
|
val refForExternalClass = context.getRefForExternalClass(klass)
|
||||||
|
val varargParameterIndex = expression.symbol.owner.varargParameterIndex()
|
||||||
else ->
|
if (varargParameterIndex == -1) {
|
||||||
context.getNameForClass(klass).makeRef()
|
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.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
@@ -120,7 +121,7 @@ fun translateCall(
|
|||||||
return JsInvocation(callRef, jsDispatchReceiver?.let { receiver -> listOf(receiver) + arguments } ?: arguments)
|
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 isExternalVararg = function.isEffectivelyExternal() && varargParameterIndex != -1
|
||||||
|
|
||||||
val symbolName = when (jsDispatchReceiver) {
|
val symbolName = when (jsDispatchReceiver) {
|
||||||
@@ -134,28 +135,12 @@ fun translateCall(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return if (isExternalVararg) {
|
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)
|
// 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(
|
val argumentsAsSingleArray = argumentsAsSingleArray(
|
||||||
arrayConcat,
|
jsExtensionReceiver,
|
||||||
listOfNotNull(jsExtensionReceiver) + arguments.mapIndexed { index, argument ->
|
arguments,
|
||||||
when (index) {
|
varargParameterIndex
|
||||||
|
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (jsDispatchReceiver != null) {
|
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(
|
fun translateCallArguments(
|
||||||
expression: IrMemberAccessExpression<*>,
|
expression: IrMemberAccessExpression<IrFunctionSymbol>,
|
||||||
context: JsGenerationContext,
|
context: JsGenerationContext,
|
||||||
transformer: IrElementToJsExpressionTransformer,
|
transformer: IrElementToJsExpressionTransformer,
|
||||||
): List<JsExpression> {
|
): List<JsExpression> {
|
||||||
val size = expression.valueArgumentsCount
|
val size = expression.valueArgumentsCount
|
||||||
|
|
||||||
|
val varargParameterIndex = expression.symbol.owner.realOverrideTarget.varargParameterIndex()
|
||||||
|
|
||||||
val validWithNullArgs = expression.validWithNullArgs()
|
val validWithNullArgs = expression.validWithNullArgs()
|
||||||
val arguments = (0 until size)
|
val arguments = (0 until size)
|
||||||
.mapTo(ArrayList(size)) { index ->
|
.mapTo(ArrayList(size)) { index ->
|
||||||
@@ -216,6 +248,16 @@ fun translateCallArguments(
|
|||||||
assert(validWithNullArgs)
|
assert(validWithNullArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.mapIndexed { index, result ->
|
||||||
|
val isEmptyExternalVararg = validWithNullArgs &&
|
||||||
|
varargParameterIndex == index &&
|
||||||
|
result is JsArrayLiteral &&
|
||||||
|
result.expressions.isEmpty()
|
||||||
|
|
||||||
|
if (isEmptyExternalVararg) {
|
||||||
|
null
|
||||||
|
} else result
|
||||||
|
}
|
||||||
.dropLastWhile { it == null }
|
.dropLastWhile { it == null }
|
||||||
.map { it ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) }
|
.map { it ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user