drop unused: toplevel, assign-only
- assign statement does not count towards variable usage by default - only works with assignments on the same scope level as declaration - can be disabled with `unused` set to "keep_assign" - `toplevel` to drop unused top-level variables and/or functions - `top_retain` to whitelist top-level exceptions closes #1450
This commit is contained in:
@@ -60,6 +60,8 @@ function Compressor(options, false_by_default) {
|
||||
booleans : !false_by_default,
|
||||
loops : !false_by_default,
|
||||
unused : !false_by_default,
|
||||
toplevel : !!options["top_retain"],
|
||||
top_retain : null,
|
||||
hoist_funs : !false_by_default,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
@@ -80,6 +82,21 @@ function Compressor(options, false_by_default) {
|
||||
global_defs : {},
|
||||
passes : 1,
|
||||
}, true);
|
||||
var top_retain = this.options["top_retain"];
|
||||
if (top_retain instanceof RegExp) {
|
||||
this.top_retain = function(def) {
|
||||
return top_retain.test(def.name);
|
||||
};
|
||||
} else if (typeof top_retain === "function") {
|
||||
this.top_retain = top_retain;
|
||||
} else if (top_retain) {
|
||||
if (typeof top_retain === "string") {
|
||||
top_retain = top_retain.split(/,/);
|
||||
}
|
||||
this.top_retain = function(def) {
|
||||
return top_retain.indexOf(def.name) >= 0;
|
||||
};
|
||||
}
|
||||
var sequences = this.options["sequences"];
|
||||
this.sequences_limit = sequences == 1 ? 200 : sequences | 0;
|
||||
this.warnings_produced = {};
|
||||
@@ -1409,13 +1426,27 @@ merge(Compressor.prototype, {
|
||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||
var self = this;
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
var toplevel = compressor.option("toplevel");
|
||||
if (compressor.option("unused")
|
||||
&& !(self instanceof AST_Toplevel)
|
||||
&& (!(self instanceof AST_Toplevel) || toplevel)
|
||||
&& !self.uses_eval
|
||||
&& !self.uses_with
|
||||
) {
|
||||
&& !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 = {}; // 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 initializations = new Dictionary();
|
||||
// pass 1: find out which symbols are directly used in
|
||||
// this scope (not in nested scopes).
|
||||
@@ -1423,11 +1454,25 @@ merge(Compressor.prototype, {
|
||||
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
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
node.definitions.forEach(function(def){
|
||||
if (!drop_vars) {
|
||||
var node_def = def.name.definition();
|
||||
if (!(node_def.id in in_use_ids)) {
|
||||
in_use_ids[node_def.id] = true;
|
||||
in_use.push(node_def);
|
||||
}
|
||||
}
|
||||
if (def.value) {
|
||||
initializations.add(def.name.name, def.value);
|
||||
if (def.value.has_side_effects(compressor)) {
|
||||
@@ -1437,6 +1482,14 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
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)) {
|
||||
@@ -1496,7 +1549,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Defun && node !== self) {
|
||||
if (drop_funcs && node instanceof AST_Defun && node !== self) {
|
||||
if (!(node.name.definition().id in in_use_ids)) {
|
||||
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
||||
name : node.name.name,
|
||||
@@ -1508,7 +1561,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (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){
|
||||
if (def.name.definition().id in in_use_ids) return true;
|
||||
var w = {
|
||||
@@ -1571,6 +1624,15 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
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 node.right;
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_For) {
|
||||
descend(node, this);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user