From fd844ad6e3c6a7fb43a866155cbb6d28dea946ea Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Fri, 10 Apr 2015 18:09:51 +0300 Subject: [PATCH] JS: decomposition of ternary operator --- .../kotlin/js/inline/ExpressionDecomposer.kt | 48 +++++++++++++++++++ .../kotlin/js/translate/utils/astUtils.kt | 12 +++++ .../cases/conditional.kt | 13 +++++ .../cases/conditionalElvis.kt | 14 ++++++ .../cases/conditionalNested.kt | 23 +++++++++ .../cases/conditionalTestExpression.kt | 13 +++++ .../cases/conditionalTestExpressionElvis.kt | 14 ++++++ 7 files changed, 137 insertions(+) create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/conditional.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/conditionalElvis.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/conditionalNested.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpression.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpressionElvis.kt diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt index f72752bd668..3e47c3d3eee 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt @@ -97,6 +97,35 @@ class ExpressionDecomposer private ( index = accept(index) } + override fun visit(x: JsConditional, ctx: JsContext<*>): Boolean { + x.process(ctx) + return false + } + + private fun JsConditional.process(ctx: JsContext<*>) { + test = accept(test) + if (then !in containsExtractable && otherwise !in containsExtractable) return + + val tmp = Temporary() + addStatement(tmp.variable) + + val thenBlock = withNewAdditionalStatements { + then = accept(then) + addStatement(tmp.assign(then)) + additionalStatements.toStatement() + } + + val elseBlock = withNewAdditionalStatements { + otherwise = accept(otherwise) + addStatement(tmp.assign(otherwise)) + additionalStatements.toStatement() + } + + val lazyEval = JsIf(test, thenBlock, elseBlock) + addStatement(lazyEval) + ctx.replaceMe(tmp.nameRef) + } + override fun visit(x: JsInvocation, ctx: JsContext<*>): Boolean { CallableInvocationAdapter(x).process() return false @@ -153,9 +182,21 @@ class ExpressionDecomposer private ( } } + inline + private fun withNewAdditionalStatements(fn: ()->T): T { + val backup = additionalStatements + additionalStatements = SmartList() + val result = fn() + additionalStatements = backup + return result + } + private fun addStatement(statement: JsStatement) = additionalStatements.add(statement) + private fun addStatements(statements: List) = + additionalStatements.addAll(statements) + private fun JsExpression.extractToTemporary(): JsExpression { val tmp = Temporary(this) addStatement(tmp.variable) @@ -294,3 +335,10 @@ private fun JsNode.withParentsOfNodes(nodes: Set): Set { visitor.accept(this) return visitor.matched } + +private fun List.toStatement(): JsStatement = + when (size()) { + 0 -> JsEmpty + 1 -> get(0) + else -> JsBlock(this) + } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt index c102d3de04e..222d0768dbb 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt @@ -63,3 +63,15 @@ public var JsArrayAccess.index: JsExpression public var JsArrayAccess.array: JsExpression get() = getArrayExpression() set(value) = setArrayExpression(value) + +public var JsConditional.test: JsExpression + get() = getTestExpression() + set(value) = setTestExpression(value) + +public var JsConditional.then: JsExpression + get() = getThenExpression() + set(value) = setThenExpression(value) + +public var JsConditional.otherwise: JsExpression + get() = getElseExpression() + set(value) = setElseExpression(value) diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/conditional.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/conditional.kt new file mode 100644 index 00000000000..82db7e1bc60 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/conditional.kt @@ -0,0 +1,13 @@ +package foo + +fun test(x: Boolean): Boolean = + if (fizz(x)) buzz(true) else buzz(false) + +fun box(): String { + assertEquals(true, test(true)) + assertEquals("fizz(true);buzz(true);", pullLog()) + assertEquals(false, test(false)) + assertEquals("fizz(false);buzz(false);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalElvis.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalElvis.kt new file mode 100644 index 00000000000..24cc20d8575 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalElvis.kt @@ -0,0 +1,14 @@ +package foo + +fun test(x: Boolean?): Boolean = fizz(x) ?: buzz(true) + +fun box(): String { + assertEquals(true, test(null)) + assertEquals("fizz(null);buzz(true);", pullLog()) + assertEquals(false, test(false)) + assertEquals("fizz(false);", pullLog()) + assertEquals(true, test(true)) + assertEquals("fizz(true);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalNested.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalNested.kt new file mode 100644 index 00000000000..13bafb8d10f --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalNested.kt @@ -0,0 +1,23 @@ +package foo + +fun test(x: Boolean, y: Boolean): Int = + if (fizz(x)) + if (fizz(y)) buzz(1) else buzz(2) + else + if (fizz(y)) buzz(3) else buzz(4) + +fun box(): String { + assertEquals(1, test(true, true)) + assertEquals("fizz(true);fizz(true);buzz(1);", pullLog()) + + assertEquals(2, test(true, false)) + assertEquals("fizz(true);fizz(false);buzz(2);", pullLog()) + + assertEquals(3, test(false, true)) + assertEquals("fizz(false);fizz(true);buzz(3);", pullLog()) + + assertEquals(4, test(false, false)) + assertEquals("fizz(false);fizz(false);buzz(4);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpression.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpression.kt new file mode 100644 index 00000000000..ca753f9228d --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpression.kt @@ -0,0 +1,13 @@ +package foo + +fun test(x: Boolean): Boolean = + if (buzz(x)) buzz(true) else fizz(false) + +fun box(): String { + assertEquals(true, test(true)) + assertEquals("buzz(true);buzz(true);", pullLog()) + assertEquals(false, test(false)) + assertEquals("buzz(false);fizz(false);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpressionElvis.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpressionElvis.kt new file mode 100644 index 00000000000..633a564b3d5 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/conditionalTestExpressionElvis.kt @@ -0,0 +1,14 @@ +package foo + +fun test(x: Boolean?): Boolean = buzz(x) ?: fizz(true) + +fun box(): String { + assertEquals(true, test(null)) + assertEquals("buzz(null);fizz(true);", pullLog()) + assertEquals(false, test(false)) + assertEquals("buzz(false);", pullLog()) + assertEquals(true, test(true)) + assertEquals("buzz(true);", pullLog()) + + return "OK" +} \ No newline at end of file