JS: decomposition of invocation/"new"
This commit is contained in:
@@ -21,6 +21,7 @@ import com.google.dart.compiler.backend.js.ast.metadata.*
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
|
||||
|
||||
import kotlin.platform.platformStatic
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
import com.intellij.util.SmartList
|
||||
|
||||
@@ -73,6 +74,87 @@ class ExpressionDecomposer private (
|
||||
return decomposer.additionalStatements
|
||||
}
|
||||
}
|
||||
override fun visit(x: JsInvocation, ctx: JsContext<*>): Boolean {
|
||||
CallableInvocationAdapter(x).process()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun visit(x: JsNew, ctx: JsContext<*>): Boolean {
|
||||
CallableNewAdapter(x).process()
|
||||
return false
|
||||
}
|
||||
|
||||
private abstract class Callable(hasArguments: HasArguments) {
|
||||
abstract var qualifier: JsExpression
|
||||
val arguments = hasArguments.getArguments()
|
||||
}
|
||||
|
||||
private class CallableInvocationAdapter(val invocation: JsInvocation) : Callable(invocation) {
|
||||
override var qualifier: JsExpression
|
||||
get() = invocation.getQualifier()
|
||||
set(value) = invocation.setQualifier(value)
|
||||
}
|
||||
|
||||
private class CallableNewAdapter(val jsnew: JsNew) : Callable(jsnew) {
|
||||
override var qualifier: JsExpression
|
||||
get() = jsnew.getConstructorExpression()
|
||||
set(value) = jsnew.setConstructorExpression(value)
|
||||
}
|
||||
|
||||
private fun Callable.process() {
|
||||
val matchedIndices = arguments.indicesOfExtractable
|
||||
if (!matchedIndices.hasNext()) return
|
||||
|
||||
qualifier = accept(qualifier)
|
||||
if (qualifier in containsNodeWithSideEffect) {
|
||||
qualifier = qualifier.extractToTemporary()
|
||||
}
|
||||
|
||||
processByIndices(arguments, matchedIndices)
|
||||
}
|
||||
|
||||
private fun processByIndices(elements: MutableList<JsExpression>, matchedIndices: Iterator<Int>) {
|
||||
var prev = 0
|
||||
while (matchedIndices.hasNext()) {
|
||||
val curr = matchedIndices.next()
|
||||
|
||||
for (i in prev..curr-1) {
|
||||
val arg = elements[i]
|
||||
if (arg !in containsNodeWithSideEffect) continue
|
||||
|
||||
elements[i] = arg.extractToTemporary()
|
||||
}
|
||||
|
||||
elements[curr] = accept(elements[curr])
|
||||
prev = curr
|
||||
}
|
||||
}
|
||||
|
||||
private fun addStatement(statement: JsStatement) =
|
||||
additionalStatements.add(statement)
|
||||
|
||||
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()
|
||||
|
||||
val variable: JsVars by Delegates.lazy {
|
||||
newVar(name, value)
|
||||
}
|
||||
|
||||
val nameRef: JsExpression
|
||||
get() = name.makeRef()
|
||||
|
||||
fun assign(value: JsExpression): JsStatement =
|
||||
assignment(nameRef, value).makeStmt()
|
||||
}
|
||||
|
||||
private val List<JsNode>.indicesOfExtractable: Iterator<Int>
|
||||
get() = indices.filter { get(it) in containsExtractable }.iterator()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package foo
|
||||
|
||||
// CHECK_NOT_CALLED: buzz
|
||||
|
||||
private var LOG = ""
|
||||
|
||||
fun log(string: String) {
|
||||
LOG += "$string;"
|
||||
}
|
||||
|
||||
fun pullLog(): String {
|
||||
val string = LOG
|
||||
LOG = ""
|
||||
return string
|
||||
}
|
||||
|
||||
fun fizz<T>(x: T): T {
|
||||
log("fizz($x)")
|
||||
return x
|
||||
}
|
||||
|
||||
inline
|
||||
fun buzz<T>(x: T): T {
|
||||
log("buzz($x)")
|
||||
return x
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package foo
|
||||
|
||||
fun sum(x: Int, y: Int): Int {
|
||||
log("sum($x, $y)")
|
||||
return x + y
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(3, sum(fizz(1), buzz(2)))
|
||||
assertEquals("fizz(1);buzz(2);sum(1, 2);", pullLog())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package foo
|
||||
|
||||
fun sum(a: Int, b: Int, c: Int, d: Int): Int {
|
||||
log("sum($a, $b, $c, $d)")
|
||||
return a + b + c + d
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(10, sum(fizz(1), buzz(2), fizz(3), buzz(4)))
|
||||
assertEquals("fizz(1);buzz(2);fizz(3);buzz(4);sum(1, 2, 3, 4);", pullLog())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package foo
|
||||
|
||||
// CHECK_NOT_CALLED: max
|
||||
|
||||
inline fun max(a: Int, b: Int): Int {
|
||||
log("max($a, $b)")
|
||||
|
||||
if (a > b) return a
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val test = max(fizz(1), max(fizz(2), buzz(3)))
|
||||
assertEquals(3, test)
|
||||
assertEquals("fizz(1);fizz(2);buzz(3);max(2, 3);max(1, 3);", pullLog())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package foo
|
||||
|
||||
class Sum(x: Int, y: Int) {
|
||||
init {
|
||||
log("new Sum($x, $y)")
|
||||
}
|
||||
|
||||
val value = x + y
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(3, Sum(fizz(1), buzz(2)).value)
|
||||
assertEquals("fizz(1);buzz(2);new Sum(1, 2);", pullLog())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package foo
|
||||
|
||||
fun multiplyFun(): (Int, Int)->Int {
|
||||
log("multiplyFun()")
|
||||
return { x, y -> x * y }
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(6, multiplyFun()(fizz(2), buzz(3)))
|
||||
assertEquals("multiplyFun();fizz(2);buzz(3);", pullLog())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package foo
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(3, buzz(fizz(1) + buzz(2)))
|
||||
assertEquals("fizz(1);buzz(2);buzz(3);", pullLog())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user