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;
|
||||
};
|
||||
}
|
||||
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"];
|
||||
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
||||
this.warnings_produced = {};
|
||||
@@ -117,6 +125,12 @@ function Compressor(options, false_by_default) {
|
||||
Compressor.prototype = new TreeTransformer;
|
||||
merge(Compressor.prototype, {
|
||||
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) {
|
||||
if (this.option("expression")) {
|
||||
node.process_expression(true);
|
||||
@@ -249,7 +263,6 @@ merge(Compressor.prototype, {
|
||||
|
||||
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) {
|
||||
var reduce_vars = rescan && compressor.option("reduce_vars");
|
||||
var toplevel = compressor.option("toplevel");
|
||||
var safe_ids = Object.create(null);
|
||||
var suppressor = new TreeWalker(function(node) {
|
||||
if (node instanceof AST_Symbol) {
|
||||
@@ -310,7 +323,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (node instanceof AST_Defun) {
|
||||
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;
|
||||
} else {
|
||||
d.fixed = node;
|
||||
@@ -469,7 +482,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
def.fixed = false;
|
||||
@@ -643,7 +656,6 @@ merge(Compressor.prototype, {
|
||||
// in the statement with the var value and then erase the var definition.
|
||||
|
||||
var scope = compressor.find_parent(AST_Scope);
|
||||
var toplevel = compressor.option("toplevel");
|
||||
var stat_index;
|
||||
var prev_stat_index;
|
||||
var def_stat_index;
|
||||
@@ -694,7 +706,8 @@ merge(Compressor.prototype, {
|
||||
// Only interested in cases with just one reference to the variable.
|
||||
var def = var_decl.name.definition();
|
||||
if (def.references.length !== 1
|
||||
|| var_name == "arguments" || (!toplevel && def.global)) {
|
||||
|| var_name == "arguments"
|
||||
|| def.global && !compressor.toplevel(def)) {
|
||||
side_effects_encountered = true;
|
||||
continue;
|
||||
}
|
||||
@@ -1825,289 +1838,283 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||
if (!compressor.option("unused")) return;
|
||||
if (compressor.has_directive("use asm")) return;
|
||||
var self = this;
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
var toplevel = compressor.option("toplevel");
|
||||
if (compressor.option("unused")
|
||||
&& (!(self instanceof AST_Toplevel) || toplevel)
|
||||
&& !self.uses_eval
|
||||
&& !self.uses_with) {
|
||||
var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
|
||||
var drop_funcs = /funcs/.test(toplevel);
|
||||
var drop_vars = /vars/.test(toplevel);
|
||||
if (!(self instanceof AST_Toplevel) || toplevel == true) {
|
||||
drop_funcs = drop_vars = true;
|
||||
}
|
||||
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) {
|
||||
if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
|
||||
in_use_ids[def.id] = true;
|
||||
in_use.push(def);
|
||||
if (self.uses_eval || self.uses_with) return;
|
||||
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
|
||||
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
|
||||
if (!drop_funcs && !drop_vars) return;
|
||||
var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
|
||||
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) {
|
||||
if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
|
||||
in_use_ids[def.id] = true;
|
||||
in_use.push(def);
|
||||
}
|
||||
});
|
||||
}
|
||||
var var_defs_by_id = new Dictionary();
|
||||
var initializations = new Dictionary();
|
||||
// pass 1: find out which symbols are directly used in
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
var var_defs_by_id = new Dictionary();
|
||||
var initializations = new Dictionary();
|
||||
// pass 1: find out which symbols are directly used in
|
||||
// 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();
|
||||
initializations.add(node.name.name, node);
|
||||
return true; // don't go in nested scopes
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
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;
|
||||
in_use.push(node_def);
|
||||
}
|
||||
}
|
||||
initializations.add(node.name.name, node);
|
||||
return true; // don't go in nested scopes
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
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 (def.value) {
|
||||
initializations.add(def.name.name, def.value);
|
||||
if (def.value.has_side_effects(compressor)) {
|
||||
def.value.walk(tw);
|
||||
}
|
||||
if (!drop_vars) {
|
||||
if (!(node_def.id in in_use_ids)) {
|
||||
in_use_ids[node_def.id] = true;
|
||||
in_use.push(node_def);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
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) {
|
||||
initializations.add(def.name.name, def.value);
|
||||
if (def.value.has_side_effects(compressor)) {
|
||||
def.value.walk(tw);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
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 (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 = [];
|
||||
}
|
||||
if (def.value) {
|
||||
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);
|
||||
tail.push(def);
|
||||
} else {
|
||||
var value = def.value && def.value.drop_side_effect_free(compressor);
|
||||
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));
|
||||
}
|
||||
head.push(def);
|
||||
}
|
||||
});
|
||||
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
|
||||
var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
|
||||
if (var_defs.length > 1) {
|
||||
var def = tail.pop();
|
||||
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
|
||||
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
|
||||
}));
|
||||
} 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 {
|
||||
var value = def.value && def.value.drop_side_effect_free(compressor);
|
||||
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));
|
||||
}
|
||||
}
|
||||
if (head.length > 0 || tail.length > 0) {
|
||||
node.definitions = head.concat(tail);
|
||||
body.push(node);
|
||||
}
|
||||
if (side_effects.length > 0) {
|
||||
body.push(make_node(AST_SimpleStatement, node, {
|
||||
body: make_sequence(node, side_effects)
|
||||
});
|
||||
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
|
||||
var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
|
||||
if (var_defs.length > 1) {
|
||||
var def = tail.pop();
|
||||
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
|
||||
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
|
||||
&& 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));
|
||||
}
|
||||
if (head.length > 0 || tail.length > 0) {
|
||||
node.definitions = head.concat(tail);
|
||||
body.push(node);
|
||||
}
|
||||
// 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 (side_effects.length > 0) {
|
||||
body.push(make_node(AST_SimpleStatement, node, {
|
||||
body: make_sequence(node, side_effects)
|
||||
}));
|
||||
}
|
||||
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
|
||||
};
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
self.transform(tt);
|
||||
}
|
||||
if (drop_vars && assign_as_unused
|
||||
&& 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){
|
||||
@@ -3683,7 +3690,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
var name = d.name.length;
|
||||
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;
|
||||
}
|
||||
d.should_replace = value <= name + overhead ? fn : false;
|
||||
|
||||
Reference in New Issue
Block a user