From 6d29aab5e23b1650ff02a72a720418a2204525a8 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Thu, 10 May 2018 15:03:37 +0300 Subject: [PATCH] [JS IR BE] Implement vararg in JS BE --- .../js/lower/BlockDecomposerLowering.kt | 41 ++++++++++++++++ .../IrElementToJsExpressionTransformer.kt | 47 +++++++++++++++++++ .../kotlin/ir/backend/js/utils/Namer.kt | 3 ++ 3 files changed, 91 insertions(+) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt index 9739a864c9a..b2bf86dfee9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt @@ -526,6 +526,47 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : FunctionLowerin return KeptResult } + override fun visitVararg(expression: IrVararg, data: VisitData): VisitResult { + var decomposed = 0 + + val arguments = expression.elements.map { + val expr = (it as? IrSpreadElement)?.expression ?: it as IrExpression + Pair(it, expr.accept(this, data).applyIfChanged { decomposed++ }) + } + + if (decomposed == 0) return KeptResult + + val newStatements = mutableListOf() + val newArguments = arguments.map { (original, result) -> + if (decomposed == 0) original + else { + val originalExpression = (original as? IrSpreadElement)?.expression ?: original as IrExpression + val newExpression = result.runIfChangedOrDefault(originalExpression) { + newStatements += statements + decomposed-- + resultValue + } + val wrapVar = makeTempVar(expression.varargElementType) + newStatements += JsIrBuilder.buildVar(wrapVar).apply { initializer = newExpression } + val newValue = JsIrBuilder.buildGetValue(wrapVar) + if (original is IrSpreadElement) { + IrSpreadElementImpl(original.startOffset, original.endOffset, newValue) + } else { + newValue + } + } + } + + val newExpression = IrVarargImpl( + expression.startOffset, + expression.endOffset, + expression.type, + expression.varargElementType, + newArguments) + + return DecomposedResult(newStatements, newExpression) + } + override fun visitStringConcatenation(expression: IrStringConcatenation, data: VisitData): VisitResult { var decomposed = 0 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index ecc484d1f2c..b1752cc30e7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -19,6 +19,53 @@ import org.jetbrains.kotlin.util.OperatorNameConventions class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { + override fun visitVararg(expression: IrVararg, context: JsGenerationContext): JsExpression { + // TODO: perform the dark magic below in the separated lowering + if (expression.elements.size == 1) { + val element = expression.elements[0] + if (element is IrSpreadElement) { + // special case, invoke slice() + val expr = element.expression.accept(this, context) + return JsInvocation(JsNameRef(Namer.SLICE_FUNCTION, expr)) + } + } + + var arrayLiteralElements = mutableListOf() + val concatArguments = mutableListOf() + var qualifier: JsExpression? = null + + expression.elements.forEach { + if (it is IrSpreadElement) { + val expr = it.expression.accept(this, context) + if (qualifier == null) { + if (arrayLiteralElements.isEmpty()) { + qualifier = JsNameRef(Namer.CONCAT_FUNCTION, expr) + } else { + val dispatch = JsArrayLiteral(arrayLiteralElements) + arrayLiteralElements = mutableListOf() + qualifier = JsNameRef(Namer.CONCAT_FUNCTION, dispatch) + concatArguments.add(expr) + } + } else { + if (arrayLiteralElements.isNotEmpty()) { + concatArguments.add(JsArrayLiteral(arrayLiteralElements)) + arrayLiteralElements = mutableListOf() + } + concatArguments.add(expr) + } + } else { + arrayLiteralElements.add(it.accept(this, context)) + } + } + + return qualifier?.let { + if (arrayLiteralElements.isNotEmpty()) { + concatArguments.add(JsArrayLiteral(arrayLiteralElements)) + } + return JsInvocation(it, concatArguments) + } ?: JsArrayLiteral(arrayLiteralElements) + } + override fun visitExpressionBody(body: IrExpressionBody, context: JsGenerationContext): JsExpression = body.expression.accept(this, context) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt index 6d240f581f1..799cd91f7b2 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -34,6 +34,9 @@ object Namer { val CALL_FUNCTION = "call" val APPLY_FUNCTION = "apply" + val SLICE_FUNCTION = "slice" + val CONCAT_FUNCTION = "concat" + val UNREACHABLE_NAME = "\$unreachable" val OUTER_FIELD_NAME = "\$outer"