compress duplicated variable definitions (#1817)
These are surprisingly common, as people reuse the same variable name within loops or switch branches.
This commit is contained in:
@@ -1813,6 +1813,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
var var_defs_by_id = new Dictionary();
|
||||||
var initializations = new Dictionary();
|
var initializations = new Dictionary();
|
||||||
// pass 1: find out which symbols are directly used in
|
// pass 1: find out which symbols are directly used in
|
||||||
// this scope (not in nested scopes).
|
// this scope (not in nested scopes).
|
||||||
@@ -1832,8 +1833,11 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_Definitions && scope === self) {
|
if (node instanceof AST_Definitions && scope === self) {
|
||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
|
var node_def = def.name.definition();
|
||||||
|
if (def.name instanceof AST_SymbolVar) {
|
||||||
|
var_defs_by_id.add(node_def.id, def);
|
||||||
|
}
|
||||||
if (!drop_vars) {
|
if (!drop_vars) {
|
||||||
var node_def = def.name.definition();
|
|
||||||
if (!(node_def.id in in_use_ids)) {
|
if (!(node_def.id in in_use_ids)) {
|
||||||
in_use_ids[node_def.id] = true;
|
in_use_ids[node_def.id] = true;
|
||||||
in_use.push(node_def);
|
in_use.push(node_def);
|
||||||
@@ -1943,19 +1947,29 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
|
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
|
||||||
var def = node.definitions.filter(function(def){
|
var def = node.definitions.filter(function(def){
|
||||||
if (def.value) def.value = def.value.transform(tt);
|
|
||||||
var sym = def.name.definition();
|
|
||||||
if (sym.id in in_use_ids) return true;
|
|
||||||
if (sym.orig[0] instanceof AST_SymbolCatch) {
|
|
||||||
def.value = def.value && def.value.drop_side_effect_free(compressor);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
var w = {
|
var w = {
|
||||||
name : def.name.name,
|
name : def.name.name,
|
||||||
file : def.name.start.file,
|
file : def.name.start.file,
|
||||||
line : def.name.start.line,
|
line : def.name.start.line,
|
||||||
col : def.name.start.col
|
col : def.name.start.col
|
||||||
};
|
};
|
||||||
|
if (def.value) def.value = def.value.transform(tt);
|
||||||
|
var sym = def.name.definition();
|
||||||
|
if (sym.id in in_use_ids) {
|
||||||
|
if (def.name instanceof AST_SymbolVar) {
|
||||||
|
var var_defs = var_defs_by_id.get(sym.id);
|
||||||
|
if (var_defs.length > 1 && !def.value) {
|
||||||
|
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", w);
|
||||||
|
var_defs.splice(var_defs.indexOf(def), 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (sym.orig[0] instanceof AST_SymbolCatch) {
|
||||||
|
def.value = def.value && def.value.drop_side_effect_free(compressor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (def.value && (def._unused_side_effects = def.value.drop_side_effect_free(compressor))) {
|
if (def.value && (def._unused_side_effects = def.value.drop_side_effect_free(compressor))) {
|
||||||
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
||||||
return true;
|
return true;
|
||||||
@@ -1963,6 +1977,28 @@ merge(Compressor.prototype, {
|
|||||||
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
|
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
if (def.length == 1
|
||||||
|
&& def[0].value
|
||||||
|
&& !def[0]._unused_side_effects
|
||||||
|
&& def[0].name instanceof AST_SymbolVar) {
|
||||||
|
var var_defs = var_defs_by_id.get(def[0].name.definition().id);
|
||||||
|
if (var_defs.length > 1) {
|
||||||
|
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", {
|
||||||
|
name : def[0].name.name,
|
||||||
|
file : def[0].name.start.file,
|
||||||
|
line : def[0].name.start.line,
|
||||||
|
col : def[0].name.start.col
|
||||||
|
});
|
||||||
|
var_defs.splice(var_defs.indexOf(def[0]), 1);
|
||||||
|
return make_node(AST_SimpleStatement, node, {
|
||||||
|
body: make_node(AST_Assign, def[0], {
|
||||||
|
operator: "=",
|
||||||
|
left: make_node(AST_SymbolRef, def[0].name, def[0].name),
|
||||||
|
right: def[0].value
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
// place uninitialized names at the start
|
// place uninitialized names at the start
|
||||||
def = mergeSort(def, function(a, b){
|
def = mergeSort(def, function(a, b){
|
||||||
if (!a.value && b.value) return -1;
|
if (!a.value && b.value) return -1;
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
|
|||||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
this.def_variable(new AST_SymbolVar({
|
this.def_variable(new AST_SymbolConst({
|
||||||
name: "arguments",
|
name: "arguments",
|
||||||
start: this.start,
|
start: this.start,
|
||||||
end: this.end
|
end: this.end
|
||||||
|
|||||||
@@ -1029,3 +1029,30 @@ delete_assign_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop_var: {
|
||||||
|
options = {
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
console.log(a, b);
|
||||||
|
var a = 1, b = 2;
|
||||||
|
console.log(a, b);
|
||||||
|
var a = 3;
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(a, b);
|
||||||
|
var a = 1, b = 2;
|
||||||
|
console.log(a, b);
|
||||||
|
a = 3;
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"undefined undefined",
|
||||||
|
"1 2",
|
||||||
|
"3 2",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1639,7 +1639,7 @@ redefine_arguments_1: {
|
|||||||
return typeof arguments;
|
return typeof arguments;
|
||||||
}
|
}
|
||||||
function g() {
|
function g() {
|
||||||
return"number";
|
return "number";
|
||||||
}
|
}
|
||||||
function h(x) {
|
function h(x) {
|
||||||
var arguments = x;
|
var arguments = x;
|
||||||
@@ -1951,7 +1951,6 @@ pure_getters_2: {
|
|||||||
var a = a && a.b;
|
var a = a && a.b;
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
var a;
|
|
||||||
var a = a && a.b;
|
var a = a && a.b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user