From cfdce8eacad6c173afe61ceeee1e64feba2a72b3 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 23 Mar 2016 11:54:58 +0300 Subject: [PATCH] Rewrite temporary variable elimination --- .../clean/TemporaryVariableElimination.kt | 496 +++++++++++++----- .../kotlin/js/inline/util/collectUtils.kt | 16 +- .../TemporaryVariableEliminationTest.kt | 8 + .../temporary-variable/ifBranch.optimized.js | 17 + .../temporary-variable/ifBranch.original.js | 17 + .../methodCall.optimized.js | 77 +++ .../temporary-variable/methodCall.original.js | 84 +++ .../nonSideEffect.optimized.js | 10 + .../nonSideEffect.original.js | 13 + .../temporary-variable/tryCatch.optimized.js | 24 + .../temporary-variable/tryCatch.original.js | 24 + 11 files changed, 642 insertions(+), 144 deletions(-) create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.optimized.js create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.original.js create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/methodCall.optimized.js create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/methodCall.original.js create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.optimized.js create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.original.js create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.optimized.js create mode 100644 js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.original.js diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt index 6c2400e5f0e..537b4bebf04 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.inline.clean import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.metadata.synthetic -import org.jetbrains.kotlin.js.inline.util.canHaveSideEffect import org.jetbrains.kotlin.js.inline.util.collectDefinedNames import org.jetbrains.kotlin.js.inline.util.collectFreeVariables import org.jetbrains.kotlin.js.translate.utils.JsAstUtils @@ -26,196 +25,415 @@ import org.jetbrains.kotlin.js.translate.utils.JsAstUtils internal class TemporaryVariableElimination(private val root: JsStatement) { private val definitions = mutableMapOf() private val usages = mutableMapOf() + private val inconsistent = mutableSetOf() private val definedValues = mutableMapOf() private val temporary = mutableSetOf() private var hasChanges = false private val namesToProcess = mutableSetOf() + private val statementsToRemove = mutableSetOf() + private val namesToSubstitute = mutableSetOf() fun apply(): Boolean { analyze() perform() + cleanUp() return hasChanges } private fun analyze() { - namesToProcess.addAll(collectDefinedNames(root)) + namesToProcess += collectDefinedNames(root) - object : JsVisitorWithContextImpl() { - val lastAssignedVars = mutableListOf() + object : RecursiveJsVisitor() { + val currentScope = mutableSetOf() + var localVars = mutableSetOf() - override fun visit(x: JsNameRef, ctx: JsContext<*>): Boolean { - val name = x.name - if (name != null && x.qualifier == null) { - val expr = definedValues[name] - useVariable(name) - if (lastAssignedVars.lastOrNull() == name && name in namesToProcess) { - lastAssignedVars.removeAt(lastAssignedVars.lastIndex) - } - else if (expr == null || expr.canHaveSideEffect()) { - temporary -= name - } - } - return super.visit(x, ctx) - } - - override fun visit(x: JsTry, ctx: JsContext<*>): Boolean { - lastAssignedVars.clear() - return super.visit(x, ctx) - } - - override fun visit(x: JsWhile, ctx: JsContext<*>): Boolean { - lastAssignedVars.clear() - return super.visit(x, ctx) - } - - override fun visit(x: JsIf, ctx: JsContext<*>): Boolean { - accept(x.ifExpression) - lastAssignedVars.clear() - - accept(x.thenStatement) - x.elseStatement?.let { accept(it) } - - return false - } - - override fun visit(x: JsInvocation, ctx: JsContext<*>): Boolean { - x.arguments.asReversed().forEach { accept(it) } - accept(x.qualifier) - return false - } - - override fun visit(x: JsArrayLiteral, ctx: JsContext<*>): Boolean { - x.expressions.asReversed().forEach { accept(it) } - return false - } - - override fun visit(x: JsBreak, ctx: JsContext<*>) = false - - override fun visit(x: JsContinue, ctx: JsContext<*>) = false - - override fun visit(x: JsExpressionStatement, ctx: JsContext): Boolean { + override fun visitExpressionStatement(x: JsExpressionStatement) { val assignment = JsAstUtils.decomposeAssignmentToVariable(x.expression) if (assignment != null) { val (name, value) = assignment - assignVariable(name, value) - accept(value) - if (x.synthetic && name in namesToProcess) { - temporary += name - } - else if (value.canHaveSideEffect()) { - lastAssignedVars.clear() - } - lastAssignedVars += name - return false - } - - var statement = x - val result = accept(x.expression) - if (result != x.expression) { - statement = JsExpressionStatement(result) - ctx.replaceMe(statement) - } - - if (statement.expression.canHaveSideEffect()) { - lastAssignedVars.clear() - } - return false - } - - override fun visit(x: JsBinaryOperation, ctx: JsContext<*>): Boolean { - accept(x.arg2) - accept(x.arg1) - return false - } - - override fun visit(x: JsVars, ctx: JsContext<*>): Boolean { - for (v in x.vars) { - val name = v.name - val value = v.initExpression - if (value != null) { + if (name in namesToProcess) { assignVariable(name, value) + addVar(name) + accept(value) + if (x.synthetic) { + temporary += name + } + } + return + } + super.visitExpressionStatement(x) + } + + override fun visitVars(x: JsVars) { + for (v in x.vars) { + val name = v.name + val value = v.initExpression + if (value != null && name in namesToProcess) { + assignVariable(name, value) + addVar(name) accept(value) if (x.synthetic) { temporary += name } - lastAssignedVars += name } } - return false } - override fun visit(x: JsFunction, ctx: JsContext<*>): Boolean { - x.collectFreeVariables().forEach { useVariable(it); useVariable(it) } - return super.visit(x, ctx) + override fun visitNameRef(nameRef: JsNameRef) { + val name = nameRef.name + if (name != null && nameRef.qualifier == null && name in namesToProcess) { + useVariable(name) + if (name !in currentScope) { + inconsistent += name + } + return + } + super.visitNameRef(nameRef) } - override fun visit(x: JsConditional, ctx: JsContext<*>): Boolean { - accept(x.testExpression) - lastAssignedVars.clear() + override fun visitBreak(x: JsBreak) { } - accept(x.thenExpression) - accept(x.elseExpression) + override fun visitContinue(x: JsContinue) { } - return false + override fun visitObjectLiteral(x: JsObjectLiteral) { + for (initializer in x.propertyInitializers) { + accept(initializer.valueExpr) + } + } + + override fun visitFor(x: JsFor) = withNewScope { super.visitFor(x) } + + override fun visitForIn(x: JsForIn) = withNewScope { super.visitForIn(x) } + + override fun visitWhile(x: JsWhile) = withNewScope { super.visitWhile(x) } + + override fun visitDoWhile(x: JsDoWhile) = withNewScope { super.visitDoWhile(x) } + + override fun visitIf(x: JsIf) { + accept(x.ifExpression) + withNewScope { accept(x.thenStatement) } + x.elseStatement?.let { withNewScope { accept(it) } } + } + + override fun visitCase(x: JsCase) = withNewScope { super.visitCase(x) } + + override fun visitDefault(x: JsDefault) = withNewScope { super.visitDefault(x) } + + override fun visitCatch(x: JsCatch) = withNewScope { super.visitCatch(x) } + + override fun visitFunction(x: JsFunction) { + for (freeVar in x.collectFreeVariables()) { + useVariable(freeVar) + useVariable(freeVar) + } + } + + private inline fun withNewScope(block: () -> T): T { + val localVarsBackup = localVars + try { + localVars = mutableSetOf() + return block() + } + finally { + currentScope -= localVars + localVars = localVarsBackup + } + } + + private fun addVar(name: JsName) { + currentScope += name + localVars.add(name) } }.accept(root) } private fun perform() { - object : JsVisitorWithContextImpl() { - override fun visit(x: JsExpressionStatement, ctx: JsContext<*>): Boolean { - val assignment = JsAstUtils.decomposeAssignmentToVariable(x.expression) + object : RecursiveJsVisitor() { + val lastAssignedVars = mutableListOf>() + var lastProperlyUsedIndex = -1 + var firstUsedIndex = 0 + var nextExpectedIndex = -1 + var sideEffectOccurred = false + + override fun visitNameRef(nameRef: JsNameRef) { + val name = nameRef.name + if (name != null && nameRef.qualifier == null && shouldConsiderTemporary(name)) { + val index = lastAssignedVars.indexOfFirst { it.first == name } + if (index >= 0) { + if (sideEffectOccurred) { + lastProperlyUsedIndex = -1 + } + if (index == nextExpectedIndex) { + ++nextExpectedIndex + } + else { + lastProperlyUsedIndex = index + nextExpectedIndex = index + 1 + } + firstUsedIndex = Math.min(firstUsedIndex, index) + } + return + } + super.visitNameRef(nameRef) + } + + override fun visitInvocation(invocation: JsInvocation) { + super.visitInvocation(invocation) + sideEffectOccurred = true + } + + override fun visitNew(x: JsNew) { + super.visitNew(x) + sideEffectOccurred = true + } + + override fun visitPrefixOperation(x: JsPrefixOperation) { + super.visitPrefixOperation(x) + when (x.operator) { + JsUnaryOperator.INC, JsUnaryOperator.DEC -> sideEffectOccurred = true + else -> {} + } + } + + override fun visitPostfixOperation(x: JsPostfixOperation) { + super.visitPostfixOperation(x) + when (x.operator) { + JsUnaryOperator.INC, JsUnaryOperator.DEC -> sideEffectOccurred = true + else -> {} + } + } + + override fun visitBinaryExpression(x: JsBinaryOperation) { + super.visitBinaryExpression(x) + if (x.operator == JsBinaryOperator.ASG) { + sideEffectOccurred = true + } + } + + override fun visitArray(x: JsArrayLiteral) { + super.visitArray(x) + sideEffectOccurred = true + } + + override fun visitExpressionStatement(x: JsExpressionStatement) { + val expression = x.expression + val assignment = JsAstUtils.decomposeAssignmentToVariable(expression) if (assignment != null) { - if (shouldConsiderTemporary(assignment.first)) { - hasChanges = true - ctx.removeMe() - return false + val (name, value) = assignment + if (shouldConsiderTemporary(name)) { + handleTopLevel(value, false) + if (isTrivial(value) && name !in inconsistent) { + statementsToRemove += x + namesToSubstitute += name + } + else { + lastAssignedVars += Pair(name, x) + sideEffectOccurred = false + } + return } } - - return super.visit(x, ctx) + handleTopLevel(expression) } - override fun visit(x: JsVars, ctx: JsContext<*>): Boolean { - val filteredVars = x.vars.filter { it.initExpression == null || !shouldConsiderTemporary(it.name) } - if (x.vars.size != filteredVars.size) { - hasChanges = true - x.vars.clear() - x.vars.addAll(filteredVars) + override fun visitIf(x: JsIf) { + handleTopLevel(x.ifExpression) + flush() + accept(x.thenStatement) + flush() + x.elseStatement?.let { accept(it); flush() } + } + + override fun visitConditional(x: JsConditional) { + accept(x.testExpression) + + val lastAssignedVarsBackup = lastAssignedVars.toMutableList() + lastAssignedVars.clear() + accept(x.thenExpression) + accept(x.elseExpression) + lastAssignedVars.clear() + lastAssignedVars += lastAssignedVarsBackup + + sideEffectOccurred = true + } + + override fun visitReturn(x: JsReturn) { + x.expression?.let { handleTopLevel(it) } + flush() + } + + override fun visitThrow(x: JsThrow) { + handleTopLevel(x.expression) + flush() + } + + override fun visit(x: JsSwitch) { + handleTopLevel(x.expression) + flush() + x.cases.forEach { accept(it); flush() } + } + + override fun visitObjectLiteral(x: JsObjectLiteral) { + for (initializer in x.propertyInitializers) { + accept(initializer.valueExpr) } + } + + override fun visitWhile(x: JsWhile) { + flush() + super.visitWhile(x) + flush() + } + + override fun visitDoWhile(x: JsDoWhile) { + flush() + super.visitDoWhile(x) + flush() + } + + override fun visitForIn(x: JsForIn) { + handleTopLevel(x.objectExpression) + flush() + accept(x.body) + flush() + } + + override fun visitFor(x: JsFor) { + x.initVars?.let { accept(it) } + x.initExpression?.let { handleTopLevel(it) } + + flush() + + x.condition?.let { accept(it) } + x.incrementExpression?.let { accept(it) } + + flush() + accept(x.body) + flush() + } + + override fun visitTry(x: JsTry) { + flush() + super.visitTry(x) + flush() + } + + override fun visitCatch(x: JsCatch) { + flush() + super.visitCatch(x) + flush() + } + + override fun visitLabel(x: JsLabel) { + flush() + super.visitLabel(x) + flush() + } + + override fun visitVars(x: JsVars) { + for (v in x.vars) { + val initializer = v.initExpression + if (initializer != null) { + val name = v.name + if (shouldConsiderTemporary(name)) { + handleTopLevel(initializer, false) + if (isTrivial(initializer) && name !in inconsistent) { + statementsToRemove += v + namesToSubstitute += name + } + else { + lastAssignedVars += Pair(name, v) + sideEffectOccurred = false + } + } + else { + handleTopLevel(initializer) + } + } + } + } + + override fun visitFunction(x: JsFunction) { } + + override fun visitBreak(x: JsBreak) { } + + override fun visitContinue(x: JsContinue) { } + + private fun flush() { + lastAssignedVars.clear() + } + + private fun handleTopLevel(expression: JsExpression, respectSideEffects: Boolean = true) { + lastProperlyUsedIndex = -1 + firstUsedIndex = lastAssignedVars.size + nextExpectedIndex = -1 + sideEffectOccurred = false + + accept(expression) + + if (lastProperlyUsedIndex >= 0 && nextExpectedIndex == lastAssignedVars.size) { + lastAssignedVars.asSequence() + .drop(lastProperlyUsedIndex) + .forEach { + val (name, statement) = it + statementsToRemove += statement + namesToSubstitute += name + } + } + + if (respectSideEffects && sideEffectOccurred) { + lastAssignedVars.clear() + } + else { + lastAssignedVars.subList(firstUsedIndex, lastAssignedVars.size).clear() + } + } + }.accept(root) + } + + private fun cleanUp() { + object : JsVisitorWithContextImpl() { + override fun visit(x: JsVars, ctx: JsContext<*>): Boolean { + x.vars.removeAll { it in statementsToRemove } if (x.vars.isEmpty()) { ctx.removeMe() - } - - return super.visit(x, ctx) - } - - override fun visit(x: JsNameRef, ctx: JsContext): Boolean { - val name = x.name - if (x.qualifier == null && name != null && shouldConsiderTemporary(name)) { hasChanges = true - val newExpr = definedValues[name] ?: JsLiteral.UNDEFINED - ctx.replaceMe(accept(newExpr)) - usages[name] = usages[name]!! - 1 return false } return super.visit(x, ctx) } - override fun visit(x: JsBreak, ctx: JsContext<*>) = false - - override fun visit(x: JsContinue, ctx: JsContext<*>) = false - - override fun visit(x: JsReturn, ctx: JsContext<*>): Boolean { - val returnValue = x.expression - if (returnValue is JsNameRef) { - val name = returnValue.name - if (returnValue.qualifier == null && name != null && name in temporary && definitions[name] ?: 0 == 0) { - x.expression = null - } + override fun visit(x: JsExpressionStatement, ctx: JsContext<*>): Boolean { + if (x in statementsToRemove) { + ctx.removeMe() + hasChanges = true + return false } return super.visit(x, ctx) } + + override fun visit(x: JsObjectLiteral, ctx: JsContext<*>): Boolean { + for (initializer in x.propertyInitializers) { + accept(initializer.valueExpr) + } + return super.visit(x, ctx) + } + + override fun visit(x: JsNameRef, ctx: JsContext): Boolean { + val name = x.name + if (name != null && x.qualifier == null && name in namesToSubstitute) { + val replacement = accept(definedValues[name]!!) + ctx.replaceMe(replacement) + return false + } + return super.visit(x, ctx) + } + + override fun visit(x: JsFunction, ctx: JsContext<*>) = false + + override fun visit(x: JsBreak, ctx: JsContext<*>) = false + + override fun visit(x: JsContinue, ctx: JsContext<*>) = false }.accept(root) } diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt index 5ff65cc01ae..b705ab4473c 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt @@ -27,7 +27,7 @@ import java.util.* fun collectFunctionReferencesInside(scope: JsNode): List = collectReferencedNames(scope).filter { it.staticRef is JsFunction } -fun collectReferencedNames(scope: JsNode): Set { +private fun collectReferencedNames(scope: JsNode): Set { val references = IdentitySet() object : JsVisitorWithContextImpl() { @@ -98,13 +98,19 @@ fun collectDefinedNames(scope: JsNode): Set { addNameIfNeeded(x.name) } - override fun visitFunction(x: JsFunction) { - val name = x.name - if (name != null) { - addNameIfNeeded(x.name) + override fun visitExpressionStatement(x: JsExpressionStatement) { + val expression = x.expression + if (expression is JsFunction) { + val name = expression.name + if (name != null) { + addNameIfNeeded(name) + } } + super.visitExpressionStatement(x) } + override fun visitFunction(x: JsFunction) { } + private fun addNameIfNeeded(name: JsName) { val ident = name.ident val nameCollected = names[ident] diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/TemporaryVariableEliminationTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/TemporaryVariableEliminationTest.kt index 4a8d69c03c1..a735bc935b8 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/TemporaryVariableEliminationTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/TemporaryVariableEliminationTest.kt @@ -24,4 +24,12 @@ class TemporaryVariableEliminationTest : BasicOptimizerTest("temporary-variable" @Test fun assignment() = box() @Test fun skipsGlobalDeclarations() = box() + + @Test fun methodCall() = box() + + @Test fun ifBranch() = box() + + @Test fun tryCatch() = box() + + @Test fun nonSideEffect() = box() } \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.optimized.js b/js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.optimized.js new file mode 100644 index 00000000000..f898cdfd9d8 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.optimized.js @@ -0,0 +1,17 @@ +function test1(n) { + var $tmp; + if (n > 0) { + $tmp = 23; + } + return typeof $tmp; +} + +function box() { + var result = test1(5); + if (result != "number") return "fail1: " + result; + + result = test1(-5); + if (result != "undefined") return "fail2: " + result; + + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.original.js b/js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.original.js new file mode 100644 index 00000000000..f898cdfd9d8 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/ifBranch.original.js @@ -0,0 +1,17 @@ +function test1(n) { + var $tmp; + if (n > 0) { + $tmp = 23; + } + return typeof $tmp; +} + +function box() { + var result = test1(5); + if (result != "number") return "fail1: " + result; + + result = test1(-5); + if (result != "undefined") return "fail2: " + result; + + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/methodCall.optimized.js b/js/js.translator/testData/js-optimizer/temporary-variable/methodCall.optimized.js new file mode 100644 index 00000000000..bea2aa02aa0 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/methodCall.optimized.js @@ -0,0 +1,77 @@ +var log = ""; + + +function init() { + log = ""; +} + +function foo(n) { + log += "{" + n + "}"; + return n; +} + +function test1() { + init(); + + return foo(foo(1) + foo(2)); +} + +function test2() { + init(); + + var $tmp2 = foo(2); + var $tmp1 = foo(1); + + return foo($tmp1 + $tmp2); +} + +function test3() { + init(); + + return foo(foo(1) + foo(2) + foo(foo(3))); +} + +function test4() { + init(); + + var $tmp1 = foo(1); + var $tmp2 = foo(2); + var $tmp3 = foo(3); + + return foo(foo($tmp1) + $tmp2 + $tmp3); +} + +function test5() { + init(); + + var $tmp1 = foo(1); + var $tmp2 = foo(2); + var $tmp3 = foo(3); + foo(4); + + return $tmp1 + $tmp2 + $tmp3; +} + +function box() { + var result = test1(); + if (result != 3) return "fail1a: " + result; + if (log != "{1}{2}{3}") return "fail1b: " + log; + + result = test2(); + if (result != 3) return "fail2a: " + result; + if (log != "{2}{1}{3}") return "fail2b: " + log; + + result = test3(); + if (result != 6) return "fail3a: " + result; + if (log != "{1}{2}{3}{3}{6}") return "fail3b: " + log; + + result = test4(); + if (result != 6) return "fail4a: " + result; + if (log != "{1}{2}{3}{1}{6}") return "fail4b: " + log; + + result = test5(); + if (result != 6) return "fail5a: " + result; + if (log != "{1}{2}{3}{4}") return "fail5b: " + log; + + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/methodCall.original.js b/js/js.translator/testData/js-optimizer/temporary-variable/methodCall.original.js new file mode 100644 index 00000000000..630d26f9fbe --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/methodCall.original.js @@ -0,0 +1,84 @@ +var log = ""; + + +function init() { + log = ""; +} + +function foo(n) { + log += "{" + n + "}"; + return n; +} + +function test1() { + init(); + + var $tmp1 = foo(1); + var $tmp2 = foo(2); + + return foo($tmp1 + $tmp2); +} + +function test2() { + init(); + + var $tmp2 = foo(2); + var $tmp1 = foo(1); + + return foo($tmp1 + $tmp2); +} + +function test3() { + init(); + + var $tmp1 = foo(1); + var $tmp2 = foo(2); + var $tmp3 = foo(3); + + return foo($tmp1 + $tmp2 + foo($tmp3)); +} + +function test4() { + init(); + + var $tmp1 = foo(1); + var $tmp2 = foo(2); + var $tmp3 = foo(3); + + return foo(foo($tmp1) + $tmp2 + $tmp3); +} + +function test5() { + init(); + + var $tmp1 = foo(1); + var $tmp2 = foo(2); + var $tmp3 = foo(3); + foo(4); + + return $tmp1 + $tmp2 + $tmp3; +} + +function box() { + var result = test1(); + if (result != 3) return "fail1a: " + result; + if (log != "{1}{2}{3}") return "fail1b: " + log; + + result = test2(); + if (result != 3) return "fail2a: " + result; + if (log != "{2}{1}{3}") return "fail2b: " + log; + + result = test3(); + if (result != 6) return "fail3a: " + result; + if (log != "{1}{2}{3}{3}{6}") return "fail3b: " + log; + + result = test4(); + if (result != 6) return "fail4a: " + result; + if (log != "{1}{2}{3}{1}{6}") return "fail4b: " + log; + + result = test5(); + if (result != 6) return "fail5a: " + result; + if (log != "{1}{2}{3}{4}") return "fail5b: " + log; + + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.optimized.js b/js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.optimized.js new file mode 100644 index 00000000000..26207f301c2 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.optimized.js @@ -0,0 +1,10 @@ +function foo(x) { + return x; +} + +function box() { + var result = foo(2) + 1; + if (result != 3) return "fail: " + result; + + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.original.js b/js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.original.js new file mode 100644 index 00000000000..b46b1c6e61a --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/nonSideEffect.original.js @@ -0,0 +1,13 @@ +function foo(x) { + return x; +} + +function box() { + var $tmp1 = 1; + var $tmp2 = foo(2); + + var result = $tmp2 + $tmp1; + if (result != 3) return "fail: " + result; + + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.optimized.js b/js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.optimized.js new file mode 100644 index 00000000000..4defe6fa256 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.optimized.js @@ -0,0 +1,24 @@ +function foo() { + throw "foo"; +} + +function bar() { + var $tmp = foo(); + try { + return "result: " + $tmp; + } catch (e) { + return "error"; + } +} + +function box() { + try { + bar() + } catch (e) { + if (e == "foo") { + return "OK" + } + return e; + } + return "Exception expected"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.original.js b/js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.original.js new file mode 100644 index 00000000000..4defe6fa256 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/temporary-variable/tryCatch.original.js @@ -0,0 +1,24 @@ +function foo() { + throw "foo"; +} + +function bar() { + var $tmp = foo(); + try { + return "result: " + $tmp; + } catch (e) { + return "error"; + } +} + +function box() { + try { + bar() + } catch (e) { + if (e == "foo") { + return "OK" + } + return e; + } + return "Exception expected"; +} \ No newline at end of file