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 {
|
override fun visit(x: JsArrayLiteral, ctx: JsContext<*>): Boolean {
|
||||||
val elements = x.getExpressions()
|
val elements = x.getExpressions()
|
||||||
processByIndices(elements, elements.indicesOfExtractable)
|
processByIndices(elements, elements.indicesOfExtractable)
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ public fun JsNode.any(predicate: (JsNode) -> Boolean): Boolean {
|
|||||||
return visitor.matched
|
return visitor.matched
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var JsNameRef.qualifier: JsExpression?
|
||||||
|
get() = getQualifier()
|
||||||
|
set(value) = setQualifier(value)
|
||||||
|
|
||||||
public var JsArrayAccess.index: JsExpression
|
public var JsArrayAccess.index: JsExpression
|
||||||
get() = getIndexExpression()
|
get() = getIndexExpression()
|
||||||
set(value) = setIndexExpression(value)
|
set(value) = setIndexExpression(value)
|
||||||
@@ -75,3 +79,14 @@ public var JsConditional.then: JsExpression
|
|||||||
public var JsConditional.otherwise: JsExpression
|
public var JsConditional.otherwise: JsExpression
|
||||||
get() = getElseExpression()
|
get() = getElseExpression()
|
||||||
set(value) = setElseExpression(value)
|
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