JS: decomposition of binary operator

#KT-6437 fixed
 #KT-7359 fixed
This commit is contained in:
Alexey Tsvetkov
2015-04-10 18:13:29 +03:00
parent fd844ad6e3
commit c0111d6e33
10 changed files with 170 additions and 0 deletions
@@ -76,6 +76,58 @@ class ExpressionDecomposer private (
}
}
// TODO: comma operator?
override fun visit(x: JsBinaryOperation, ctx: JsContext<*>): Boolean {
x.arg1 = accept(x.arg1)
when (x.operator) {
JsBinaryOperator.AND,
JsBinaryOperator.OR -> x.processOrAnd(ctx)
else -> x.process()
}
return false
}
private fun JsBinaryOperation.processOrAnd(ctx: JsContext<*>) {
if (arg2 !in containsExtractable) return
val tmp = Temporary(arg1)
addStatement(tmp.variable)
var test = if (operator == JsBinaryOperator.OR) not(tmp.nameRef) else tmp.nameRef
val arg2Eval = withNewAdditionalStatements {
arg2 = accept(arg2)
addStatement(tmp.assign(arg2))
additionalStatements.toStatement()
}
addStatement(JsIf(test, arg2Eval))
ctx.replaceMe(tmp.nameRef)
}
private fun JsBinaryOperation.process() {
if (arg1 !in containsNodeWithSideEffect || arg2 !in containsExtractable) {
// If arg1 does not have side effect, but arg2 contains extractable,
// we should extract from arg2 anyway.
// If arg2 does not contain extractable, it's still ok to visit.
arg2 = accept(arg2)
return
}
if (operator.isAssignment()) {
// Must be (someThingWithSideEffect).x = arg2, because arg1 can have side effect
assert(arg1 is JsNameRef) { "Valid JavaScript left-hand side must be JsNameRef, got: $this" }
val arg1AsRef = arg1 as JsNameRef
arg1AsRef.qualifier = arg1AsRef.qualifier!!.extractToTemporary()
}
else {
arg1 = arg1.extractToTemporary()
}
arg2 = accept(arg2)
}
override fun visit(x: JsArrayLiteral, ctx: JsContext<*>): Boolean {
val elements = x.getExpressions()
processByIndices(elements, elements.indicesOfExtractable)
@@ -56,6 +56,10 @@ public fun JsNode.any(predicate: (JsNode) -> Boolean): Boolean {
return visitor.matched
}
public var JsNameRef.qualifier: JsExpression?
get() = getQualifier()
set(value) = setQualifier(value)
public var JsArrayAccess.index: JsExpression
get() = getIndexExpression()
set(value) = setIndexExpression(value)
@@ -75,3 +79,14 @@ public var JsConditional.then: JsExpression
public var JsConditional.otherwise: JsExpression
get() = getElseExpression()
set(value) = setElseExpression(value)
public var JsBinaryOperation.arg1: JsExpression
get() = getArg1()
set(value) = setArg1(value)
public var JsBinaryOperation.arg2: JsExpression
get() = getArg2()
set(value) = setArg2(value)
public val JsBinaryOperation.operator: JsBinaryOperator
get() = getOperator()
@@ -0,0 +1,14 @@
package foo
class A(var x: Int) {
override fun toString(): String = "A($x)"
}
fun box(): String {
val a = A(10)
fizz(a).x = buzz(20)
assertEquals(20, a.x)
assertEquals("fizz(A(10));buzz(20);", pullLog())
return "OK"
}
@@ -0,0 +1,8 @@
package foo
fun box(): String {
assertEquals(3, fizz(1) + buzz(2))
assertEquals("fizz(1);buzz(2);", pullLog())
return "OK"
}
@@ -0,0 +1,8 @@
package foo
fun box(): String {
assertEquals(10, (fizz(1) + buzz(2)) + (fizz(3) + buzz(4)))
assertEquals("fizz(1);buzz(2);fizz(3);buzz(4);", pullLog())
return "OK"
}
@@ -0,0 +1,14 @@
package foo
fun box(): String {
assertEquals(false, fizz(false) && buzz(false))
assertEquals("fizz(false);", pullLog())
assertEquals(false, fizz(true) && buzz(false))
assertEquals("fizz(true);buzz(false);", pullLog())
assertEquals(true, fizz(true) && buzz(true))
assertEquals("fizz(true);buzz(true);", pullLog())
return "OK"
}
@@ -0,0 +1,8 @@
package foo
fun box(): String {
assertEquals(true, fizz(true) || buzz(true) && (fizz(false) || buzz(true)))
assertEquals("fizz(true);", pullLog())
return "OK"
}
@@ -0,0 +1,14 @@
package foo
fun box(): String {
assertEquals(true, fizz(true) || buzz(false))
assertEquals("fizz(true);", pullLog())
assertEquals(true, fizz(false) || buzz(true))
assertEquals("fizz(false);buzz(true);", pullLog())
assertEquals(false, fizz(false) || buzz(false))
assertEquals("fizz(false);buzz(false);", pullLog())
return "OK"
}
@@ -0,0 +1,20 @@
package foo
class A(val x: Int = fizz(1) + 1) {
val y = buzz(x) + 1
val z: Int
init {
z = fizz(x) + buzz(y)
}
}
fun box(): String {
val a = A()
assertEquals(2, a.x)
assertEquals(3, a.y)
assertEquals(5, a.z)
assertEquals("fizz(1);buzz(2);fizz(2);buzz(3);", pullLog())
return "OK"
}
@@ -0,0 +1,17 @@
package foo
class A {
val x: Int
init {
x = fizz(1) + buzz(2)
}
}
fun box(): String {
val a = A()
assertEquals(3, a.x)
assertEquals("fizz(1);buzz(2);", pullLog())
return "OK"
}