fix corner case in unused
This commit is contained in:
@@ -328,7 +328,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
var body = this.body;
|
||||
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");";
|
||||
wrapped_tl = parse(wrapped_tl);
|
||||
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node) {
|
||||
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function(node) {
|
||||
if (node instanceof AST_Directive && node.value == "$ORIG") {
|
||||
return MAP.splice(body);
|
||||
}
|
||||
|
||||
381
lib/compress.js
381
lib/compress.js
@@ -425,6 +425,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function safe_to_read(tw, def) {
|
||||
if (def.single_use == "m") return false;
|
||||
if (tw.safe_ids[def.id]) {
|
||||
if (def.fixed == null) {
|
||||
var orig = def.orig[0];
|
||||
@@ -705,7 +706,7 @@ merge(Compressor.prototype, {
|
||||
tw.loop_ids[d.id] = tw.in_loop;
|
||||
}
|
||||
var value;
|
||||
if (d.fixed === undefined || !safe_to_read(tw, d) || d.single_use == "m") {
|
||||
if (d.fixed === undefined || !safe_to_read(tw, d)) {
|
||||
d.fixed = false;
|
||||
} else if (d.fixed) {
|
||||
value = this.fixed_value();
|
||||
@@ -3415,202 +3416,200 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
}
|
||||
// pass 3: we should drop declarations not in_use
|
||||
var tt = new TreeTransformer(
|
||||
function before(node, descend, in_list) {
|
||||
var parent = tt.parent();
|
||||
if (drop_vars) {
|
||||
var props = [], sym = assign_as_unused(node, props);
|
||||
if (sym instanceof AST_SymbolRef) {
|
||||
var def = sym.definition();
|
||||
var in_use = def.id in in_use_ids;
|
||||
var value = null;
|
||||
if (node instanceof AST_Assign) {
|
||||
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
|
||||
value = node.right;
|
||||
}
|
||||
} else if (!in_use) {
|
||||
value = make_node(AST_Number, node, {
|
||||
value: 0
|
||||
});
|
||||
var tt = new TreeTransformer(function(node, descend, in_list) {
|
||||
var parent = tt.parent();
|
||||
if (drop_vars) {
|
||||
var props = [], sym = assign_as_unused(node, props);
|
||||
if (sym instanceof AST_SymbolRef) {
|
||||
var def = sym.definition();
|
||||
var in_use = def.id in in_use_ids;
|
||||
var value = null;
|
||||
if (node instanceof AST_Assign) {
|
||||
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
|
||||
value = node.right;
|
||||
}
|
||||
if (value) {
|
||||
props.push(value);
|
||||
return maintain_this_binding(compressor, parent, node, make_sequence(node, props.map(function(prop) {
|
||||
return prop.transform(tt);
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scope !== self) return;
|
||||
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) {
|
||||
var def = node.name.definition();
|
||||
if (!(def.id in in_use_ids)) {
|
||||
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
||||
def.eliminated++;
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && 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 (!drop_vars || sym.id in in_use_ids) {
|
||||
if (def.value && sym.id in fixed_ids && fixed_ids[sym.id] !== def) {
|
||||
def.value = def.value.drop_side_effect_free(compressor);
|
||||
}
|
||||
if (def.name instanceof AST_SymbolVar) {
|
||||
var var_defs = var_defs_by_id.get(sym.id);
|
||||
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
|
||||
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
if (def.value) {
|
||||
var ref = make_node(AST_SymbolRef, def.name, def.name);
|
||||
sym.references.push(ref);
|
||||
var assign = make_node(AST_Assign, def, {
|
||||
operator: "=",
|
||||
left: ref,
|
||||
right: def.value
|
||||
});
|
||||
if (fixed_ids[sym.id] === def) {
|
||||
fixed_ids[sym.id] = assign;
|
||||
}
|
||||
side_effects.push(assign.transform(tt));
|
||||
}
|
||||
remove(var_defs, def);
|
||||
sym.eliminated++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (def.value) {
|
||||
if (side_effects.length > 0) {
|
||||
if (tail.length > 0) {
|
||||
side_effects.push(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) side_effects.push(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));
|
||||
side_effects.push(value);
|
||||
} else {
|
||||
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
}
|
||||
sym.eliminated++;
|
||||
}
|
||||
});
|
||||
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)
|
||||
}));
|
||||
}
|
||||
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
|
||||
} else if (!in_use) {
|
||||
value = make_node(AST_Number, node, {
|
||||
value: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
var block;
|
||||
if (node.init instanceof AST_BlockStatement) {
|
||||
block = node.init;
|
||||
node.init = block.body.pop();
|
||||
block.body.push(node);
|
||||
if (value) {
|
||||
props.push(value);
|
||||
return maintain_this_binding(compressor, parent, node, make_sequence(node, props.map(function(prop) {
|
||||
return prop.transform(tt);
|
||||
})));
|
||||
}
|
||||
if (node.init instanceof AST_SimpleStatement) {
|
||||
node.init = node.init.body;
|
||||
} else if (is_empty(node.init)) {
|
||||
node.init = null;
|
||||
}
|
||||
return !block ? node : in_list ? MAP.splice(block.body) : block;
|
||||
}
|
||||
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) {
|
||||
var save_scope = scope;
|
||||
scope = node;
|
||||
descend(node, this);
|
||||
scope = save_scope;
|
||||
return node;
|
||||
}
|
||||
|
||||
function template(sym) {
|
||||
return {
|
||||
name : sym.name,
|
||||
file : sym.start.file,
|
||||
line : sym.start.line,
|
||||
col : sym.start.col
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
if (scope !== self) return;
|
||||
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) {
|
||||
var def = node.name.definition();
|
||||
if (!(def.id in in_use_ids)) {
|
||||
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
||||
def.eliminated++;
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && 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 (!drop_vars || sym.id in in_use_ids) {
|
||||
if (def.value && sym.id in fixed_ids && fixed_ids[sym.id] !== def) {
|
||||
def.value = def.value.drop_side_effect_free(compressor);
|
||||
}
|
||||
if (def.name instanceof AST_SymbolVar) {
|
||||
var var_defs = var_defs_by_id.get(sym.id);
|
||||
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
|
||||
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
if (def.value) {
|
||||
var ref = make_node(AST_SymbolRef, def.name, def.name);
|
||||
sym.references.push(ref);
|
||||
var assign = make_node(AST_Assign, def, {
|
||||
operator: "=",
|
||||
left: ref,
|
||||
right: def.value
|
||||
});
|
||||
if (fixed_ids[sym.id] === def) {
|
||||
fixed_ids[sym.id] = assign;
|
||||
}
|
||||
side_effects.push(assign.transform(tt));
|
||||
}
|
||||
remove(var_defs, def);
|
||||
sym.eliminated++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (def.value) {
|
||||
if (side_effects.length > 0) {
|
||||
if (tail.length > 0) {
|
||||
side_effects.push(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) side_effects.push(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));
|
||||
side_effects.push(value);
|
||||
} else {
|
||||
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
}
|
||||
sym.eliminated++;
|
||||
}
|
||||
});
|
||||
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)
|
||||
}));
|
||||
}
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
var block;
|
||||
if (node.init instanceof AST_BlockStatement) {
|
||||
block = node.init;
|
||||
node.init = block.body.pop();
|
||||
block.body.push(node);
|
||||
}
|
||||
if (node.init instanceof AST_SimpleStatement) {
|
||||
node.init = node.init.body;
|
||||
} else if (is_empty(node.init)) {
|
||||
node.init = null;
|
||||
}
|
||||
return !block ? node : in_list ? MAP.splice(block.body) : block;
|
||||
}
|
||||
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) {
|
||||
var save_scope = scope;
|
||||
scope = node;
|
||||
descend(node, this);
|
||||
scope = save_scope;
|
||||
return node;
|
||||
}
|
||||
|
||||
function template(sym) {
|
||||
return {
|
||||
name : sym.name,
|
||||
file : sym.start.file,
|
||||
line : sym.start.line,
|
||||
col : sym.start.col
|
||||
};
|
||||
}
|
||||
});
|
||||
self.transform(tt);
|
||||
|
||||
function verify_safe_usage(def, read, modified) {
|
||||
|
||||
Reference in New Issue
Block a user