JS/Inlining: introduce removal of unused variables
This commit is contained in:
+55
-3
@@ -33,6 +33,7 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
|
|||||||
private val namesToProcess = mutableSetOf<JsName>()
|
private val namesToProcess = mutableSetOf<JsName>()
|
||||||
private val statementsToRemove = mutableSetOf<JsNode>()
|
private val statementsToRemove = mutableSetOf<JsNode>()
|
||||||
private val namesToSubstitute = mutableSetOf<JsName>()
|
private val namesToSubstitute = mutableSetOf<JsName>()
|
||||||
|
private val variablesToRemove = mutableSetOf<JsName>()
|
||||||
|
|
||||||
fun apply(): Boolean {
|
fun apply(): Boolean {
|
||||||
analyze()
|
analyze()
|
||||||
@@ -268,7 +269,13 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
else if (shouldConsiderUnused(name)) {
|
||||||
|
variablesToRemove += name
|
||||||
|
handleTopLevel(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTopLevel(expression)
|
handleTopLevel(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,6 +390,9 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (shouldConsiderUnused(name)) {
|
||||||
|
variablesToRemove += name
|
||||||
|
}
|
||||||
handleTopLevel(initializer)
|
handleTopLevel(initializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,8 +438,33 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
|
|||||||
|
|
||||||
private fun cleanUp() {
|
private fun cleanUp() {
|
||||||
object : JsVisitorWithContextImpl() {
|
object : JsVisitorWithContextImpl() {
|
||||||
override fun visit(x: JsVars, ctx: JsContext<*>): Boolean {
|
override fun visit(x: JsVars, ctx: JsContext<JsNode>): Boolean {
|
||||||
x.vars.removeAll { it in statementsToRemove }
|
x.vars.removeAll(statementsToRemove)
|
||||||
|
|
||||||
|
var lastDroppedIndex = 0
|
||||||
|
for ((index, v) in x.vars.toList().withIndex()) {
|
||||||
|
val name = v.name
|
||||||
|
if (name in variablesToRemove) {
|
||||||
|
hasChanges = true
|
||||||
|
if (index > lastDroppedIndex) {
|
||||||
|
val droppedVars = JsVars(*x.vars.subList(lastDroppedIndex, index).toTypedArray())
|
||||||
|
droppedVars.synthetic = x.synthetic
|
||||||
|
ctx.addPrevious(droppedVars)
|
||||||
|
}
|
||||||
|
val initExpression = v.initExpression
|
||||||
|
if (initExpression != null) {
|
||||||
|
ctx.addPrevious(JsExpressionStatement(initExpression).apply {
|
||||||
|
synthetic = true
|
||||||
|
accept(this)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
lastDroppedIndex = index + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastDroppedIndex > 0) {
|
||||||
|
x.vars.subList(0, lastDroppedIndex).clear()
|
||||||
|
}
|
||||||
|
|
||||||
if (x.vars.isEmpty()) {
|
if (x.vars.isEmpty()) {
|
||||||
ctx.removeMe()
|
ctx.removeMe()
|
||||||
hasChanges = true
|
hasChanges = true
|
||||||
@@ -438,12 +473,27 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
|
|||||||
return super.visit(x, ctx)
|
return super.visit(x, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(x: JsExpressionStatement, ctx: JsContext<*>): Boolean {
|
override fun visit(x: JsExpressionStatement, ctx: JsContext<JsNode>): Boolean {
|
||||||
if (x in statementsToRemove) {
|
if (x in statementsToRemove) {
|
||||||
ctx.removeMe()
|
ctx.removeMe()
|
||||||
hasChanges = true
|
hasChanges = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val assignment = JsAstUtils.decomposeAssignmentToVariable(x.expression)
|
||||||
|
if (assignment != null) {
|
||||||
|
val (name, value) = assignment
|
||||||
|
if (name in variablesToRemove) {
|
||||||
|
hasChanges = true
|
||||||
|
JsExpressionStatement(value).apply {
|
||||||
|
synthetic = true
|
||||||
|
accept(this)
|
||||||
|
ctx.replaceMe(this)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return super.visit(x, ctx)
|
return super.visit(x, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,6 +531,8 @@ internal class TemporaryVariableElimination(private val root: JsStatement) {
|
|||||||
usages[name] = (usages[name] ?: 0) + 1
|
usages[name] = (usages[name] ?: 0) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldConsiderUnused(name: JsName) = (definitions[name] ?: 0) == 1 && (usages[name] ?: 0) == 0 && name in temporary
|
||||||
|
|
||||||
private fun shouldConsiderTemporary(name: JsName): Boolean {
|
private fun shouldConsiderTemporary(name: JsName): Boolean {
|
||||||
if (definitions[name] ?: 0 != 1 || name !in temporary) return false
|
if (definitions[name] ?: 0 != 1 || name !in temporary) return false
|
||||||
|
|
||||||
|
|||||||
+6
@@ -34,4 +34,10 @@ class TemporaryVariableEliminationTest : BasicOptimizerTest("temporary-variable"
|
|||||||
@Test fun nonSideEffect() = box()
|
@Test fun nonSideEffect() = box()
|
||||||
|
|
||||||
@Test fun propertyAccess() = box()
|
@Test fun propertyAccess() = box()
|
||||||
|
|
||||||
|
@Test fun removeUnused() = box()
|
||||||
|
|
||||||
|
@Test fun innerExpressionProcessed() = box()
|
||||||
|
|
||||||
|
@Test fun transitiveNotConsideredTrivial() = box()
|
||||||
}
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
function box() {
|
||||||
|
var a = 2;
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
function box() {
|
||||||
|
var a = 2;
|
||||||
|
var $b = a;
|
||||||
|
var $c = 2 + $b;
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
var global = 0;
|
||||||
|
|
||||||
|
function test1() {
|
||||||
|
global++;
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test2() {
|
||||||
|
global++;
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test3() {
|
||||||
|
global++;
|
||||||
|
var $b = global--;
|
||||||
|
return $b + $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function box() {
|
||||||
|
var result = test1();
|
||||||
|
if (result != 1) return "fail1: " + result;
|
||||||
|
|
||||||
|
result = test2();
|
||||||
|
if (result != 2) return "fail2: " + result;
|
||||||
|
|
||||||
|
result = test3();
|
||||||
|
if (result != 6) return "fail3: " + result;
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
var global = 0;
|
||||||
|
|
||||||
|
function test1() {
|
||||||
|
var $tmp = global++;
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test2() {
|
||||||
|
var $tmp;
|
||||||
|
$tmp = global++;
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test3() {
|
||||||
|
var $a = global++, $b = global--;
|
||||||
|
return $b + $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function box() {
|
||||||
|
var result = test1();
|
||||||
|
if (result != 1) return "fail1: " + result;
|
||||||
|
|
||||||
|
result = test2();
|
||||||
|
if (result != 2) return "fail2: " + result;
|
||||||
|
|
||||||
|
result = test3();
|
||||||
|
if (result != 6) return "fail3: " + result;
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user