JS: decomposition of binary operator
#KT-6437 fixed #KT-7359 fixed
This commit is contained in:
@@ -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"
|
||||
}
|
||||
+8
@@ -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"
|
||||
}
|
||||
+8
@@ -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"
|
||||
}
|
||||
+20
@@ -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"
|
||||
}
|
||||
+17
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user