[JS IR BE] Basic support for external varargs
This commit is contained in:
+1
@@ -391,6 +391,7 @@ class DefaultParameterCleaner constructor(val context: CommonBackendContext) : F
|
||||
// TODO this implementation is exponential
|
||||
private fun IrFunction.needsDefaultArgumentsLowering(skipInlineMethods: Boolean): Boolean {
|
||||
if (isInline && skipInlineMethods) return false
|
||||
if (isEffectivelyExternal()) return false
|
||||
if (valueParameters.any { it.defaultValue != null }) return true
|
||||
|
||||
if (this !is IrSimpleFunction) return false
|
||||
|
||||
+67
-5
@@ -162,9 +162,17 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
val targetName = context.getNameForSymbol(target.symbol)
|
||||
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName))
|
||||
val callRef = JsNameRef(Namer.CALL_FUNCTION, qPrototype)
|
||||
return JsInvocation(callRef, jsDispatchReceiver?.let { listOf(it) + arguments } ?: arguments)
|
||||
return JsInvocation(callRef, jsDispatchReceiver?.let { receiver -> listOf(receiver) + arguments } ?: arguments)
|
||||
}
|
||||
|
||||
val varargParameterIndex = function.valueParameters.indexOfFirst { it.varargElementType != null }
|
||||
val isExternalVararg = function.isEffectivelyExternal() && varargParameterIndex != -1
|
||||
|
||||
val symbolName = context.getNameForSymbol(symbol)
|
||||
val ref = if (function is IrSimpleFunction && jsDispatchReceiver != null) JsNameRef(symbolName, jsDispatchReceiver) else JsNameRef(
|
||||
symbolName
|
||||
)
|
||||
|
||||
return if (function is IrConstructor) {
|
||||
// Inline class primary constructor takes a single value of to
|
||||
// initialize underlying property.
|
||||
@@ -177,12 +185,66 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
// Argument value constructs unboxed inline class instance
|
||||
arguments.single()
|
||||
} else {
|
||||
JsNew(context.getNameForSymbol(symbol).makeRef(), arguments)
|
||||
JsNew(ref, arguments)
|
||||
}
|
||||
} else 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))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (function is IrSimpleFunction && jsDispatchReceiver != null) {
|
||||
// TODO: Do not create IIFE when receiver expression is simple or has no side effects
|
||||
// TODO: Do not create IIFE at all? (Currently there is no reliable way to create temporary variable in current scope)
|
||||
val receiverName = context.currentScope.declareFreshName("\$externalVarargReceiverTmp")
|
||||
val receiverRef = receiverName.makeRef()
|
||||
JsInvocation(
|
||||
// Create scope for temporary variable holding dispatch receiver
|
||||
// It is used both during method reference and passing `this` value to `apply` function.
|
||||
JsFunction(
|
||||
context.currentScope,
|
||||
JsBlock(
|
||||
JsVars(JsVars.JsVar(receiverName, jsDispatchReceiver)),
|
||||
JsReturn(
|
||||
JsInvocation(
|
||||
JsNameRef("apply", JsNameRef(symbolName, receiverRef)),
|
||||
listOf(
|
||||
receiverRef,
|
||||
argumentsAsSingleArray
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
"VarargIIFE"
|
||||
)
|
||||
)
|
||||
} else {
|
||||
JsInvocation(
|
||||
JsNameRef("apply", JsNameRef(symbolName)),
|
||||
listOf(JsNullLiteral(), argumentsAsSingleArray)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val symbolName = context.getNameForSymbol(symbol)
|
||||
val ref = if (jsDispatchReceiver != null) JsNameRef(symbolName, jsDispatchReceiver) else JsNameRef(symbolName)
|
||||
JsInvocation(ref, jsExtensionReceiver?.let { listOf(jsExtensionReceiver) + arguments } ?: arguments)
|
||||
JsInvocation(ref, listOfNotNull(jsExtensionReceiver) + arguments)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1283
|
||||
// MODULE_KIND: AMD
|
||||
@JsModule("bar")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1281
|
||||
import Test.test
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1314
|
||||
package foo
|
||||
|
||||
|
||||
Reference in New Issue
Block a user