[JS IR BE] Basic support for external varargs

This commit is contained in:
Svyatoslav Kuzmich
2019-02-22 20:40:46 +03:00
parent 0d9218b017
commit 27005d7c2e
5 changed files with 68 additions and 8 deletions
@@ -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
@@ -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
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1314
package foo