JS: decomposition of invocation/"new"

This commit is contained in:
Alexey Tsvetkov
2015-04-10 18:04:00 +03:00
parent b7e26c3bf7
commit 92942fd1c5
8 changed files with 190 additions and 0 deletions
@@ -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"
}
@@ -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"
}