From 92942fd1c5e13957af71778ff2a1d40433d9cd9e Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Fri, 10 Apr 2015 18:04:00 +0300 Subject: [PATCH] JS: decomposition of invocation/"new" --- .../kotlin/js/inline/ExpressionDecomposer.kt | 82 +++++++++++++++++++ .../_commonFiles/utils.kt | 26 ++++++ .../cases/argumentOfCall.kt | 13 +++ .../argumentOfCallMultipleInlineCalls.kt | 13 +++ .../cases/argumentOfInlineCall.kt | 19 +++++ .../cases/argumentOfNew.kt | 16 ++++ .../cases/callQualifier.kt | 13 +++ .../cases/nestedInlineCall.kt | 8 ++ 8 files changed, 190 insertions(+) create mode 100644 js/js.translator/testData/inlineEvaluationOrder/_commonFiles/utils.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCall.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCallMultipleInlineCalls.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfInlineCall.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfNew.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/callQualifier.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/nestedInlineCall.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 e04663b12c1..a3299ad178b 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 @@ -21,6 +21,7 @@ import com.google.dart.compiler.backend.js.ast.metadata.* import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.* import kotlin.platform.platformStatic +import kotlin.properties.Delegates import com.intellij.util.SmartList @@ -73,6 +74,87 @@ class ExpressionDecomposer private ( return decomposer.additionalStatements } } + override fun visit(x: JsInvocation, ctx: JsContext<*>): Boolean { + CallableInvocationAdapter(x).process() + return false + } + + override fun visit(x: JsNew, ctx: JsContext<*>): Boolean { + CallableNewAdapter(x).process() + return false + } + + private abstract class Callable(hasArguments: HasArguments) { + abstract var qualifier: JsExpression + val arguments = hasArguments.getArguments() + } + + private class CallableInvocationAdapter(val invocation: JsInvocation) : Callable(invocation) { + override var qualifier: JsExpression + get() = invocation.getQualifier() + set(value) = invocation.setQualifier(value) + } + + private class CallableNewAdapter(val jsnew: JsNew) : Callable(jsnew) { + override var qualifier: JsExpression + get() = jsnew.getConstructorExpression() + set(value) = jsnew.setConstructorExpression(value) + } + + private fun Callable.process() { + val matchedIndices = arguments.indicesOfExtractable + if (!matchedIndices.hasNext()) return + + qualifier = accept(qualifier) + if (qualifier in containsNodeWithSideEffect) { + qualifier = qualifier.extractToTemporary() + } + + processByIndices(arguments, matchedIndices) + } + + private fun processByIndices(elements: MutableList, matchedIndices: Iterator) { + var prev = 0 + while (matchedIndices.hasNext()) { + val curr = matchedIndices.next() + + for (i in prev..curr-1) { + val arg = elements[i] + if (arg !in containsNodeWithSideEffect) continue + + elements[i] = arg.extractToTemporary() + } + + elements[curr] = accept(elements[curr]) + prev = curr + } + } + + private fun addStatement(statement: JsStatement) = + additionalStatements.add(statement) + + private fun JsExpression.extractToTemporary(): JsExpression { + val tmp = Temporary(this) + addStatement(tmp.variable) + return tmp.nameRef + } + + private inner class Temporary(val value: JsExpression? = null) { + val name: JsName = scope.declareTemporary() + + val variable: JsVars by Delegates.lazy { + newVar(name, value) + } + + val nameRef: JsExpression + get() = name.makeRef() + + fun assign(value: JsExpression): JsStatement = + assignment(nameRef, value).makeStmt() + } + + private val List.indicesOfExtractable: Iterator + get() = indices.filter { get(it) in containsExtractable }.iterator() } /** diff --git a/js/js.translator/testData/inlineEvaluationOrder/_commonFiles/utils.kt b/js/js.translator/testData/inlineEvaluationOrder/_commonFiles/utils.kt new file mode 100644 index 00000000000..46150de3492 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/_commonFiles/utils.kt @@ -0,0 +1,26 @@ +package foo + +// CHECK_NOT_CALLED: buzz + +private var LOG = "" + +fun log(string: String) { + LOG += "$string;" +} + +fun pullLog(): String { + val string = LOG + LOG = "" + return string +} + +fun fizz(x: T): T { + log("fizz($x)") + return x +} + +inline +fun buzz(x: T): T { + log("buzz($x)") + return x +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCall.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCall.kt new file mode 100644 index 00000000000..83db307d56d --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCall.kt @@ -0,0 +1,13 @@ +package foo + +fun sum(x: Int, y: Int): Int { + log("sum($x, $y)") + return x + y +} + +fun box(): String { + assertEquals(3, sum(fizz(1), buzz(2))) + assertEquals("fizz(1);buzz(2);sum(1, 2);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCallMultipleInlineCalls.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCallMultipleInlineCalls.kt new file mode 100644 index 00000000000..d23b3e408a3 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfCallMultipleInlineCalls.kt @@ -0,0 +1,13 @@ +package foo + +fun sum(a: Int, b: Int, c: Int, d: Int): Int { + log("sum($a, $b, $c, $d)") + return a + b + c + d +} + +fun box(): String { + assertEquals(10, sum(fizz(1), buzz(2), fizz(3), buzz(4))) + assertEquals("fizz(1);buzz(2);fizz(3);buzz(4);sum(1, 2, 3, 4);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfInlineCall.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfInlineCall.kt new file mode 100644 index 00000000000..0ea107f69e7 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfInlineCall.kt @@ -0,0 +1,19 @@ +package foo + +// CHECK_NOT_CALLED: max + +inline fun max(a: Int, b: Int): Int { + log("max($a, $b)") + + if (a > b) return a + + return b +} + +fun box(): String { + val test = max(fizz(1), max(fizz(2), buzz(3))) + assertEquals(3, test) + assertEquals("fizz(1);fizz(2);buzz(3);max(2, 3);max(1, 3);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfNew.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfNew.kt new file mode 100644 index 00000000000..8650f00b9ef --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/argumentOfNew.kt @@ -0,0 +1,16 @@ +package foo + +class Sum(x: Int, y: Int) { + init { + log("new Sum($x, $y)") + } + + val value = x + y +} + +fun box(): String { + assertEquals(3, Sum(fizz(1), buzz(2)).value) + assertEquals("fizz(1);buzz(2);new Sum(1, 2);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/callQualifier.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/callQualifier.kt new file mode 100644 index 00000000000..784add18933 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/callQualifier.kt @@ -0,0 +1,13 @@ +package foo + +fun multiplyFun(): (Int, Int)->Int { + log("multiplyFun()") + return { x, y -> x * y } +} + +fun box(): String { + assertEquals(6, multiplyFun()(fizz(2), buzz(3))) + assertEquals("multiplyFun();fizz(2);buzz(3);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/nestedInlineCall.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/nestedInlineCall.kt new file mode 100644 index 00000000000..e79ab199fa2 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/nestedInlineCall.kt @@ -0,0 +1,8 @@ +package foo + +fun box(): String { + assertEquals(3, buzz(fizz(1) + buzz(2))) + assertEquals("fizz(1);buzz(2);buzz(3);", pullLog()) + + return "OK" +} \ No newline at end of file