enforce toplevel on other compress options (#1855)
Respect "funcs" and "vars" properly. fixes #1850
This commit is contained in:
537
lib/compress.js
537
lib/compress.js
@@ -109,6 +109,14 @@ function Compressor(options, false_by_default) {
|
|||||||
return top_retain.indexOf(def.name) >= 0;
|
return top_retain.indexOf(def.name) >= 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
var toplevel = this.options["toplevel"];
|
||||||
|
if (typeof toplevel == "string") {
|
||||||
|
this.toplevel.funcs = /funcs/.test(toplevel);
|
||||||
|
this.toplevel.vars = /vars/.test(toplevel);
|
||||||
|
} else {
|
||||||
|
this.toplevel = toplevel ? return_true : return_false;
|
||||||
|
this.toplevel.funcs = this.toplevel.vars = toplevel;
|
||||||
|
}
|
||||||
var sequences = this.options["sequences"];
|
var sequences = this.options["sequences"];
|
||||||
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
||||||
this.warnings_produced = {};
|
this.warnings_produced = {};
|
||||||
@@ -117,6 +125,12 @@ function Compressor(options, false_by_default) {
|
|||||||
Compressor.prototype = new TreeTransformer;
|
Compressor.prototype = new TreeTransformer;
|
||||||
merge(Compressor.prototype, {
|
merge(Compressor.prototype, {
|
||||||
option: function(key) { return this.options[key] },
|
option: function(key) { return this.options[key] },
|
||||||
|
toplevel: function(def) {
|
||||||
|
for (var i = 0, len = def.orig.length; i < len; i++)
|
||||||
|
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
compress: function(node) {
|
compress: function(node) {
|
||||||
if (this.option("expression")) {
|
if (this.option("expression")) {
|
||||||
node.process_expression(true);
|
node.process_expression(true);
|
||||||
@@ -249,7 +263,6 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) {
|
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) {
|
||||||
var reduce_vars = rescan && compressor.option("reduce_vars");
|
var reduce_vars = rescan && compressor.option("reduce_vars");
|
||||||
var toplevel = compressor.option("toplevel");
|
|
||||||
var safe_ids = Object.create(null);
|
var safe_ids = Object.create(null);
|
||||||
var suppressor = new TreeWalker(function(node) {
|
var suppressor = new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
@@ -310,7 +323,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_Defun) {
|
if (node instanceof AST_Defun) {
|
||||||
var d = node.name.definition();
|
var d = node.name.definition();
|
||||||
if (!toplevel && d.global || safe_to_read(d)) {
|
if (d.global && !compressor.toplevel(d) || safe_to_read(d)) {
|
||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
} else {
|
} else {
|
||||||
d.fixed = node;
|
d.fixed = node;
|
||||||
@@ -469,7 +482,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reset_def(def) {
|
function reset_def(def) {
|
||||||
if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) {
|
if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) {
|
||||||
def.fixed = undefined;
|
def.fixed = undefined;
|
||||||
} else {
|
} else {
|
||||||
def.fixed = false;
|
def.fixed = false;
|
||||||
@@ -643,7 +656,6 @@ merge(Compressor.prototype, {
|
|||||||
// in the statement with the var value and then erase the var definition.
|
// in the statement with the var value and then erase the var definition.
|
||||||
|
|
||||||
var scope = compressor.find_parent(AST_Scope);
|
var scope = compressor.find_parent(AST_Scope);
|
||||||
var toplevel = compressor.option("toplevel");
|
|
||||||
var stat_index;
|
var stat_index;
|
||||||
var prev_stat_index;
|
var prev_stat_index;
|
||||||
var def_stat_index;
|
var def_stat_index;
|
||||||
@@ -694,7 +706,8 @@ merge(Compressor.prototype, {
|
|||||||
// Only interested in cases with just one reference to the variable.
|
// Only interested in cases with just one reference to the variable.
|
||||||
var def = var_decl.name.definition();
|
var def = var_decl.name.definition();
|
||||||
if (def.references.length !== 1
|
if (def.references.length !== 1
|
||||||
|| var_name == "arguments" || (!toplevel && def.global)) {
|
|| var_name == "arguments"
|
||||||
|
|| def.global && !compressor.toplevel(def)) {
|
||||||
side_effects_encountered = true;
|
side_effects_encountered = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1825,289 +1838,283 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||||
|
if (!compressor.option("unused")) return;
|
||||||
|
if (compressor.has_directive("use asm")) return;
|
||||||
var self = this;
|
var self = this;
|
||||||
if (compressor.has_directive("use asm")) return self;
|
if (self.uses_eval || self.uses_with) return;
|
||||||
var toplevel = compressor.option("toplevel");
|
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
|
||||||
if (compressor.option("unused")
|
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
|
||||||
&& (!(self instanceof AST_Toplevel) || toplevel)
|
if (!drop_funcs && !drop_vars) return;
|
||||||
&& !self.uses_eval
|
var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
|
||||||
&& !self.uses_with) {
|
var in_use = [];
|
||||||
var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
|
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
|
||||||
var drop_funcs = /funcs/.test(toplevel);
|
if (self instanceof AST_Toplevel && compressor.top_retain) {
|
||||||
var drop_vars = /vars/.test(toplevel);
|
self.variables.each(function(def) {
|
||||||
if (!(self instanceof AST_Toplevel) || toplevel == true) {
|
if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
|
||||||
drop_funcs = drop_vars = true;
|
in_use_ids[def.id] = true;
|
||||||
}
|
in_use.push(def);
|
||||||
var in_use = [];
|
}
|
||||||
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
|
});
|
||||||
if (self instanceof AST_Toplevel && compressor.top_retain) {
|
}
|
||||||
self.variables.each(function(def) {
|
var var_defs_by_id = new Dictionary();
|
||||||
if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
|
var initializations = new Dictionary();
|
||||||
in_use_ids[def.id] = true;
|
// pass 1: find out which symbols are directly used in
|
||||||
in_use.push(def);
|
// this scope (not in nested scopes).
|
||||||
|
var scope = this;
|
||||||
|
var tw = new TreeWalker(function(node, descend){
|
||||||
|
if (node !== self) {
|
||||||
|
if (node instanceof AST_Defun) {
|
||||||
|
if (!drop_funcs && scope === self) {
|
||||||
|
var node_def = node.name.definition();
|
||||||
|
if (!(node_def.id in in_use_ids)) {
|
||||||
|
in_use_ids[node_def.id] = true;
|
||||||
|
in_use.push(node_def);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
initializations.add(node.name.name, node);
|
||||||
}
|
return true; // don't go in nested scopes
|
||||||
var var_defs_by_id = new Dictionary();
|
}
|
||||||
var initializations = new Dictionary();
|
if (node instanceof AST_Definitions && scope === self) {
|
||||||
// pass 1: find out which symbols are directly used in
|
node.definitions.forEach(function(def){
|
||||||
// this scope (not in nested scopes).
|
var node_def = def.name.definition();
|
||||||
var scope = this;
|
if (def.name instanceof AST_SymbolVar) {
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var_defs_by_id.add(node_def.id, def);
|
||||||
if (node !== self) {
|
}
|
||||||
if (node instanceof AST_Defun) {
|
if (!drop_vars) {
|
||||||
if (!drop_funcs && scope === self) {
|
|
||||||
var node_def = node.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initializations.add(node.name.name, node);
|
if (def.value) {
|
||||||
return true; // don't go in nested scopes
|
initializations.add(def.name.name, def.value);
|
||||||
}
|
if (def.value.has_side_effects(compressor)) {
|
||||||
if (node instanceof AST_Definitions && scope === self) {
|
def.value.walk(tw);
|
||||||
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 (!(node_def.id in in_use_ids)) {
|
});
|
||||||
in_use_ids[node_def.id] = true;
|
return true;
|
||||||
in_use.push(node_def);
|
}
|
||||||
|
if (assign_as_unused
|
||||||
|
&& node instanceof AST_Assign
|
||||||
|
&& node.operator == "="
|
||||||
|
&& node.left instanceof AST_SymbolRef
|
||||||
|
&& scope === self) {
|
||||||
|
node.right.walk(tw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_SymbolRef) {
|
||||||
|
var node_def = node.definition();
|
||||||
|
if (!(node_def.id in in_use_ids)) {
|
||||||
|
in_use_ids[node_def.id] = true;
|
||||||
|
in_use.push(node_def);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Scope) {
|
||||||
|
var save_scope = scope;
|
||||||
|
scope = node;
|
||||||
|
descend();
|
||||||
|
scope = save_scope;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.walk(tw);
|
||||||
|
// pass 2: for every used symbol we need to walk its
|
||||||
|
// initialization code to figure out if it uses other
|
||||||
|
// symbols (that may not be in_use).
|
||||||
|
for (var i = 0; i < in_use.length; ++i) {
|
||||||
|
in_use[i].orig.forEach(function(decl){
|
||||||
|
// undeclared globals will be instanceof AST_SymbolRef
|
||||||
|
var init = initializations.get(decl.name);
|
||||||
|
if (init) init.forEach(function(init){
|
||||||
|
var tw = new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_SymbolRef) {
|
||||||
|
var node_def = node.definition();
|
||||||
|
if (!(node_def.id in in_use_ids)) {
|
||||||
|
in_use_ids[node_def.id] = true;
|
||||||
|
in_use.push(node_def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
init.walk(tw);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// pass 3: we should drop declarations not in_use
|
||||||
|
var tt = new TreeTransformer(
|
||||||
|
function before(node, descend, in_list) {
|
||||||
|
if (node instanceof AST_Function
|
||||||
|
&& node.name
|
||||||
|
&& !compressor.option("keep_fnames")) {
|
||||||
|
var def = node.name.definition();
|
||||||
|
// any declarations with same name will overshadow
|
||||||
|
// name of this anonymous function and can therefore
|
||||||
|
// never be used anywhere
|
||||||
|
if (!(def.id in in_use_ids) || def.orig.length > 1)
|
||||||
|
node.name = null;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||||
|
var trim = !compressor.option("keep_fargs");
|
||||||
|
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||||
|
var sym = a[i];
|
||||||
|
if (!(sym.definition().id in in_use_ids)) {
|
||||||
|
sym.__unused = true;
|
||||||
|
if (trim) {
|
||||||
|
a.pop();
|
||||||
|
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
trim = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (drop_funcs && node instanceof AST_Defun && node !== self) {
|
||||||
|
if (!(node.name.definition().id in in_use_ids)) {
|
||||||
|
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
||||||
|
return make_node(AST_EmptyStatement, node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
|
||||||
|
// place uninitialized names at the start
|
||||||
|
var body = [], head = [], tail = [];
|
||||||
|
// for unused names whose initialization has
|
||||||
|
// side effects, we can cascade the init. code
|
||||||
|
// into the next one, or next statement.
|
||||||
|
var side_effects = [];
|
||||||
|
node.definitions.forEach(function(def) {
|
||||||
|
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}]", template(def.name));
|
||||||
|
var_defs.splice(var_defs.indexOf(def), 1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (def.value) {
|
if (def.value) {
|
||||||
initializations.add(def.name.name, def.value);
|
if (side_effects.length > 0) {
|
||||||
if (def.value.has_side_effects(compressor)) {
|
if (tail.length > 0) {
|
||||||
def.value.walk(tw);
|
merge_sequence(side_effects, def.value);
|
||||||
}
|
def.value = make_sequence(def.value, side_effects);
|
||||||
}
|
} else {
|
||||||
});
|
body.push(make_node(AST_SimpleStatement, node, {
|
||||||
return true;
|
body: make_sequence(node, side_effects)
|
||||||
}
|
}));
|
||||||
if (assign_as_unused
|
|
||||||
&& node instanceof AST_Assign
|
|
||||||
&& node.operator == "="
|
|
||||||
&& node.left instanceof AST_SymbolRef
|
|
||||||
&& scope === self) {
|
|
||||||
node.right.walk(tw);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (node instanceof AST_SymbolRef) {
|
|
||||||
var node_def = node.definition();
|
|
||||||
if (!(node_def.id in in_use_ids)) {
|
|
||||||
in_use_ids[node_def.id] = true;
|
|
||||||
in_use.push(node_def);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Scope) {
|
|
||||||
var save_scope = scope;
|
|
||||||
scope = node;
|
|
||||||
descend();
|
|
||||||
scope = save_scope;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.walk(tw);
|
|
||||||
// pass 2: for every used symbol we need to walk its
|
|
||||||
// initialization code to figure out if it uses other
|
|
||||||
// symbols (that may not be in_use).
|
|
||||||
for (var i = 0; i < in_use.length; ++i) {
|
|
||||||
in_use[i].orig.forEach(function(decl){
|
|
||||||
// undeclared globals will be instanceof AST_SymbolRef
|
|
||||||
var init = initializations.get(decl.name);
|
|
||||||
if (init) init.forEach(function(init){
|
|
||||||
var tw = new TreeWalker(function(node){
|
|
||||||
if (node instanceof AST_SymbolRef) {
|
|
||||||
var node_def = node.definition();
|
|
||||||
if (!(node_def.id in in_use_ids)) {
|
|
||||||
in_use_ids[node_def.id] = true;
|
|
||||||
in_use.push(node_def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
init.walk(tw);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// pass 3: we should drop declarations not in_use
|
|
||||||
var tt = new TreeTransformer(
|
|
||||||
function before(node, descend, in_list) {
|
|
||||||
if (node instanceof AST_Function
|
|
||||||
&& node.name
|
|
||||||
&& !compressor.option("keep_fnames")) {
|
|
||||||
var def = node.name.definition();
|
|
||||||
// any declarations with same name will overshadow
|
|
||||||
// name of this anonymous function and can therefore
|
|
||||||
// never be used anywhere
|
|
||||||
if (!(def.id in in_use_ids) || def.orig.length > 1)
|
|
||||||
node.name = null;
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
|
||||||
var trim = !compressor.option("keep_fargs");
|
|
||||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
|
||||||
var sym = a[i];
|
|
||||||
if (!(sym.definition().id in in_use_ids)) {
|
|
||||||
sym.__unused = true;
|
|
||||||
if (trim) {
|
|
||||||
a.pop();
|
|
||||||
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trim = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (drop_funcs && node instanceof AST_Defun && node !== self) {
|
|
||||||
if (!(node.name.definition().id in in_use_ids)) {
|
|
||||||
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
|
||||||
return make_node(AST_EmptyStatement, node);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
|
|
||||||
// place uninitialized names at the start
|
|
||||||
var body = [], head = [], tail = [];
|
|
||||||
// for unused names whose initialization has
|
|
||||||
// side effects, we can cascade the init. code
|
|
||||||
// into the next one, or next statement.
|
|
||||||
var side_effects = [];
|
|
||||||
node.definitions.forEach(function(def) {
|
|
||||||
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}]", template(def.name));
|
|
||||||
var_defs.splice(var_defs.indexOf(def), 1);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
side_effects = [];
|
||||||
}
|
}
|
||||||
if (def.value) {
|
tail.push(def);
|
||||||
if (side_effects.length > 0) {
|
|
||||||
if (tail.length > 0) {
|
|
||||||
merge_sequence(side_effects, def.value);
|
|
||||||
def.value = make_sequence(def.value, side_effects);
|
|
||||||
} else {
|
|
||||||
body.push(make_node(AST_SimpleStatement, node, {
|
|
||||||
body: make_sequence(node, side_effects)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
side_effects = [];
|
|
||||||
}
|
|
||||||
tail.push(def);
|
|
||||||
} else {
|
|
||||||
head.push(def);
|
|
||||||
}
|
|
||||||
} else if (sym.orig[0] instanceof AST_SymbolCatch) {
|
|
||||||
var value = def.value && def.value.drop_side_effect_free(compressor);
|
|
||||||
if (value) merge_sequence(side_effects, value);
|
|
||||||
def.value = null;
|
|
||||||
head.push(def);
|
|
||||||
} else {
|
} else {
|
||||||
var value = def.value && def.value.drop_side_effect_free(compressor);
|
head.push(def);
|
||||||
if (value) {
|
|
||||||
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
|
|
||||||
merge_sequence(side_effects, value);
|
|
||||||
} else {
|
|
||||||
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
} else if (sym.orig[0] instanceof AST_SymbolCatch) {
|
||||||
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
|
var value = def.value && def.value.drop_side_effect_free(compressor);
|
||||||
var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
|
if (value) merge_sequence(side_effects, value);
|
||||||
if (var_defs.length > 1) {
|
def.value = null;
|
||||||
var def = tail.pop();
|
head.push(def);
|
||||||
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
|
} else {
|
||||||
var_defs.splice(var_defs.indexOf(def), 1);
|
var value = def.value && def.value.drop_side_effect_free(compressor);
|
||||||
side_effects.unshift(make_node(AST_Assign, def, {
|
if (value) {
|
||||||
operator: "=",
|
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||||
left: make_node(AST_SymbolRef, def.name, def.name),
|
merge_sequence(side_effects, value);
|
||||||
right: def.value
|
} else {
|
||||||
}));
|
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (head.length > 0 || tail.length > 0) {
|
});
|
||||||
node.definitions = head.concat(tail);
|
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
|
||||||
body.push(node);
|
var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
|
||||||
}
|
if (var_defs.length > 1) {
|
||||||
if (side_effects.length > 0) {
|
var def = tail.pop();
|
||||||
body.push(make_node(AST_SimpleStatement, node, {
|
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
|
||||||
body: make_sequence(node, side_effects)
|
var_defs.splice(var_defs.indexOf(def), 1);
|
||||||
|
side_effects.unshift(make_node(AST_Assign, def, {
|
||||||
|
operator: "=",
|
||||||
|
left: make_node(AST_SymbolRef, def.name, def.name),
|
||||||
|
right: def.value
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
switch (body.length) {
|
|
||||||
case 0:
|
|
||||||
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
|
||||||
case 1:
|
|
||||||
return body[0];
|
|
||||||
default:
|
|
||||||
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (drop_vars && assign_as_unused
|
if (head.length > 0 || tail.length > 0) {
|
||||||
&& node instanceof AST_Assign
|
node.definitions = head.concat(tail);
|
||||||
&& node.operator == "="
|
body.push(node);
|
||||||
&& node.left instanceof AST_SymbolRef) {
|
|
||||||
var def = node.left.definition();
|
|
||||||
if (!(def.id in in_use_ids)
|
|
||||||
&& self.variables.get(def.name) === def) {
|
|
||||||
return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// certain combination of unused name + side effect leads to:
|
if (side_effects.length > 0) {
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/44
|
body.push(make_node(AST_SimpleStatement, node, {
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/1830
|
body: make_sequence(node, side_effects)
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/1838
|
}));
|
||||||
// that's an invalid AST.
|
|
||||||
// We fix it at this stage by moving the `var` outside the `for`.
|
|
||||||
if (node instanceof AST_For) {
|
|
||||||
descend(node, this);
|
|
||||||
if (node.init instanceof AST_BlockStatement) {
|
|
||||||
var block = node.init;
|
|
||||||
node.init = block.body.pop();
|
|
||||||
block.body.push(node);
|
|
||||||
return in_list ? MAP.splice(block.body) : block;
|
|
||||||
} else if (node.init instanceof AST_SimpleStatement) {
|
|
||||||
node.init = node.init.body;
|
|
||||||
} else if (is_empty(node.init)) {
|
|
||||||
node.init = null;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
|
switch (body.length) {
|
||||||
descend(node, this);
|
case 0:
|
||||||
if (node.body instanceof AST_BlockStatement) {
|
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
||||||
var block = node.body;
|
case 1:
|
||||||
node.body = block.body.pop();
|
return body[0];
|
||||||
block.body.push(node);
|
default:
|
||||||
return in_list ? MAP.splice(block.body) : block;
|
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
||||||
}
|
body: body
|
||||||
return node;
|
});
|
||||||
}
|
|
||||||
if (node instanceof AST_Scope && node !== self)
|
|
||||||
return node;
|
|
||||||
|
|
||||||
function template(sym) {
|
|
||||||
return {
|
|
||||||
name : sym.name,
|
|
||||||
file : sym.start.file,
|
|
||||||
line : sym.start.line,
|
|
||||||
col : sym.start.col
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
if (drop_vars && assign_as_unused
|
||||||
self.transform(tt);
|
&& node instanceof AST_Assign
|
||||||
}
|
&& node.operator == "="
|
||||||
|
&& node.left instanceof AST_SymbolRef) {
|
||||||
|
var def = node.left.definition();
|
||||||
|
if (!(def.id in in_use_ids)
|
||||||
|
&& self.variables.get(def.name) === def) {
|
||||||
|
return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// certain combination of unused name + side effect leads to:
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/44
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/1830
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/1838
|
||||||
|
// that's an invalid AST.
|
||||||
|
// We fix it at this stage by moving the `var` outside the `for`.
|
||||||
|
if (node instanceof AST_For) {
|
||||||
|
descend(node, this);
|
||||||
|
if (node.init instanceof AST_BlockStatement) {
|
||||||
|
var block = node.init;
|
||||||
|
node.init = block.body.pop();
|
||||||
|
block.body.push(node);
|
||||||
|
return in_list ? MAP.splice(block.body) : block;
|
||||||
|
} else if (node.init instanceof AST_SimpleStatement) {
|
||||||
|
node.init = node.init.body;
|
||||||
|
} else if (is_empty(node.init)) {
|
||||||
|
node.init = null;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
|
||||||
|
descend(node, this);
|
||||||
|
if (node.body instanceof AST_BlockStatement) {
|
||||||
|
var block = node.body;
|
||||||
|
node.body = block.body.pop();
|
||||||
|
block.body.push(node);
|
||||||
|
return in_list ? MAP.splice(block.body) : block;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Scope && node !== self)
|
||||||
|
return node;
|
||||||
|
|
||||||
|
function template(sym) {
|
||||||
|
return {
|
||||||
|
name : sym.name,
|
||||||
|
file : sym.start.file,
|
||||||
|
line : sym.start.line,
|
||||||
|
col : sym.start.col
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
self.transform(tt);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
||||||
@@ -3683,7 +3690,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
var name = d.name.length;
|
var name = d.name.length;
|
||||||
var overhead = 0;
|
var overhead = 0;
|
||||||
if (compressor.option("unused") && (!d.global || compressor.option("toplevel"))) {
|
if (compressor.option("unused") && (!d.global || compressor.toplevel(d))) {
|
||||||
overhead = (name + 2 + value) / d.references.length;
|
overhead = (name + 2 + value) / d.references.length;
|
||||||
}
|
}
|
||||||
d.should_replace = value <= name + overhead ? fn : false;
|
d.should_replace = value <= name + overhead ? fn : false;
|
||||||
|
|||||||
@@ -2327,3 +2327,93 @@ iife_assign: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "1"
|
expect_stdout: "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_1850_1: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: false,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
var a = 1;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
var a = 1;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1850_2: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: "funcs",
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
var a = 1;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
(function() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1850_3: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: "vars",
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
var a = 1;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
var a = 1;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1850_4: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
var a = 1;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
(function() {
|
||||||
|
console.log(a, a, a);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user