From e80059e4a287a19bafa9ac603eeec612ff3e4769 Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Fri, 10 Apr 2015 18:35:00 +0300 Subject: [PATCH] JS: decomposition of "while" and "do while" --- .../kotlin/js/inline/ExpressionDecomposer.kt | 50 +++++++++++++++++-- .../kotlin/js/translate/utils/astUtils.kt | 8 +++ .../inlineEvaluationOrder/cases/doWhile.kt | 14 ++++++ .../cases/doWhileComplex.kt | 14 ++++++ .../inlineEvaluationOrder/cases/while.kt | 14 ++++++ .../cases/whileComplex.kt | 14 ++++++ 6 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/doWhile.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/doWhileComplex.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/while.kt create mode 100644 js/js.translator/testData/inlineEvaluationOrder/cases/whileComplex.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 1e65fb0bdc7..cf3a0f8da1c 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 @@ -77,6 +77,40 @@ class ExpressionDecomposer private ( } + override fun visit(x: JsWhile, ctx: JsContext<*>): Boolean { + x.process(true) + return false + } + + override fun visit(x: JsDoWhile, ctx: JsContext<*>): Boolean { + x.process(false) + return false + } + + private fun JsWhile.process(addBreakToBegin: Boolean) { + if (test !in containsExtractable) return + + withNewAdditionalStatements { + test = accept(test) + val breakIfNotTest = JsIf(not(test), JsBreak()) + // Body can be JsBlock or other JsStatement. + // Convert to statements list to avoid nested block. + val bodyStatements = flattenStatement(body) + + if (addBreakToBegin) { + addStatement(breakIfNotTest) + addStatements(bodyStatements) + } + else { + addStatements(0, bodyStatements) + addStatement(breakIfNotTest) + } + + body = additionalStatements.toStatement() + test = JsLiteral.TRUE + } + } + // TODO: comma operator? override fun visit(x: JsBinaryOperation, ctx: JsContext<*>): Boolean { x.arg1 = accept(x.arg1) @@ -249,12 +283,15 @@ class ExpressionDecomposer private ( private fun addStatements(statements: List) = additionalStatements.addAll(statements) + private fun addStatements(index: Int, statements: List) = + additionalStatements.addAll(index, statements) + 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() @@ -320,8 +357,15 @@ private open class JsExpressionVisitor() : JsVisitorWithContextImpl() { return false } - override fun visit(x: JsWhile, ctx: JsContext<*>): Boolean = false - override fun visit(x: JsDoWhile, ctx: JsContext<*>): Boolean = false + override fun visit(x: JsWhile, ctx: JsContext<*>): Boolean { + x.test = accept(x.test) + return false + } + + override fun visit(x: JsDoWhile, ctx: JsContext<*>): Boolean { + x.test = accept(x.test) + return false + } override fun visit(x: JsArrayAccess, ctx: JsContext<*>): Boolean = true override fun visit(x: JsArrayLiteral, ctx: JsContext<*>): Boolean = true 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 9687796d6b2..a95bb6b21b2 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 @@ -60,6 +60,14 @@ public var JsNameRef.qualifier: JsExpression? get() = getQualifier() set(value) = setQualifier(value) +public var JsWhile.test: JsExpression + get() = getCondition() + set(value) = setCondition(value) + +public var JsWhile.body: JsStatement + get() = getBody() + set(value) = setBody(value) + public var JsArrayAccess.index: JsExpression get() = getIndexExpression() set(value) = setIndexExpression(value) diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/doWhile.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/doWhile.kt new file mode 100644 index 00000000000..59d0b17571f --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/doWhile.kt @@ -0,0 +1,14 @@ +package foo + +fun box(): String { + var c = 2 + + do { + if (c > 4) throw Exception("Timeout!") + c++ + } while (buzz(c) < 4) + + assertEquals("buzz(3);buzz(4);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/doWhileComplex.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/doWhileComplex.kt new file mode 100644 index 00000000000..fe0886e1d99 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/doWhileComplex.kt @@ -0,0 +1,14 @@ +package foo + +fun box(): String { + var c = 1 + + do { + if (c > 4) throw Exception("Timeout!") + c++ + } while (fizz(c) % 2 == 0 || buzz(c) % 3 == 0) + + assertEquals("fizz(2);fizz(3);buzz(3);fizz(4);fizz(5);buzz(5);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/while.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/while.kt new file mode 100644 index 00000000000..e85a8e41e9c --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/while.kt @@ -0,0 +1,14 @@ +package foo + +fun box(): String { + var c = 2 + + while (buzz(c) <= 4) { + if (c > 4) throw Exception("Timeout!") + c++ + } + + assertEquals("buzz(2);buzz(3);buzz(4);buzz(5);", pullLog()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/whileComplex.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/whileComplex.kt new file mode 100644 index 00000000000..06652746d16 --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/whileComplex.kt @@ -0,0 +1,14 @@ +package foo + +fun box(): String { + var c = 2 + + while (fizz(c) % 2 == 0 || buzz(c) % 3 == 0) { + if (c > 4) throw Exception("Timeout!") + c++ + } + + assertEquals("fizz(2);fizz(3);buzz(3);fizz(4);fizz(5);buzz(5);", pullLog()) + + return "OK" +} \ No newline at end of file