properly drop mutually-referring declarations that are not otherwise
referenced and have no side effects
This commit is contained in:
144
lib/compress.js
144
lib/compress.js
@@ -64,8 +64,7 @@ function Compressor(options, false_by_default) {
|
||||
evaluate : !false_by_default,
|
||||
booleans : !false_by_default,
|
||||
loops : !false_by_default,
|
||||
unused_func : !false_by_default,
|
||||
unused_vars : !false_by_default,
|
||||
unused : !false_by_default,
|
||||
hoist_funs : !false_by_default,
|
||||
hoist_vars : !false_by_default,
|
||||
if_return : !false_by_default,
|
||||
@@ -816,50 +815,113 @@ function Compressor(options, false_by_default) {
|
||||
return self;
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("drop_unused_vars", function(compressor){
|
||||
if (compressor.option("unused_vars")) {
|
||||
var self = this;
|
||||
var tw = new TreeWalker(function(node){
|
||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||
var self = this;
|
||||
if (compressor.option("unused")
|
||||
&& !(self instanceof AST_Toplevel)
|
||||
&& !self.uses_eval
|
||||
) {
|
||||
var in_use = [];
|
||||
// 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_Scope)
|
||||
if (node instanceof AST_Defun) {
|
||||
return true; // don't go in nested scopes
|
||||
if (node instanceof AST_Definitions) {
|
||||
if (!(tw.parent() instanceof AST_ForIn)) {
|
||||
var a = node.definitions;
|
||||
for (var i = a.length; --i >= 0;) {
|
||||
var def = a[i];
|
||||
var sym = def.name;
|
||||
if (sym.unreferenced()) {
|
||||
var warn = {
|
||||
name: sym.name,
|
||||
file: sym.start.file,
|
||||
line: sym.start.line,
|
||||
col: sym.start.col
|
||||
};
|
||||
if (def.value && def.value.has_side_effects()) {
|
||||
compressor.warn("Side effects in initialization of unreferenced variable {name} [{file}:{line},{col}]", warn);
|
||||
} else {
|
||||
compressor.warn("Dropping unreferenced variable {name} [{file}:{line},{col}]", warn);
|
||||
a.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
node.definitions.forEach(function(def){
|
||||
if (def.value && def.value.has_side_effects()) {
|
||||
def.value.walk(tw);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (!(node instanceof AST_Statement)) {
|
||||
return true; // pointless to visit expressions
|
||||
if (node instanceof AST_SymbolRef && !(node instanceof AST_LabelRef)) {
|
||||
push_uniq(in_use, node.definition());
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
var save_scope = scope;
|
||||
scope = node;
|
||||
descend();
|
||||
scope = save_scope;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.walk(tw);
|
||||
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
|
||||
if (decl instanceof AST_SymbolDeclaration) {
|
||||
decl.init.forEach(function(init){
|
||||
var tw = new TreeWalker(function(node){
|
||||
if (node instanceof AST_SymbolRef
|
||||
&& node.definition().scope.$self === self.$self) {
|
||||
push_uniq(in_use, node.definition());
|
||||
}
|
||||
});
|
||||
init.walk(tw);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// pass 3: we should drop declarations not in_use
|
||||
var tt = new TreeTransformer(
|
||||
function before(node, descend) {
|
||||
if (node instanceof AST_Defun && node !== self) {
|
||||
if (!member(node.name.definition(), in_use)) {
|
||||
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
||||
name : node.name.name,
|
||||
file : node.name.start.file,
|
||||
line : node.name.start.line,
|
||||
col : node.name.start.col
|
||||
});
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
|
||||
var def = node.definitions.filter(function(def){
|
||||
if (member(def.name.definition(), in_use)) return true;
|
||||
var w = {
|
||||
name : def.name.name,
|
||||
file : def.name.start.file,
|
||||
line : def.name.start.line,
|
||||
col : def.name.start.col
|
||||
};
|
||||
if (def.value && def.value.has_side_effects()) {
|
||||
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
|
||||
return true;
|
||||
}
|
||||
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
|
||||
return false;
|
||||
});
|
||||
if (def.length == 0) {
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
if (def.length != node.definitions.length) {
|
||||
node.definitions = def;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Scope && node !== self)
|
||||
return node;
|
||||
}
|
||||
);
|
||||
self.transform(tt);
|
||||
}
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
||||
var hoist_funs = compressor.option("hoist_funs");
|
||||
var hoist_vars = compressor.option("hoist_vars");
|
||||
this.drop_unused_vars(compressor);
|
||||
this.drop_unused(compressor);
|
||||
if (hoist_funs || hoist_vars) {
|
||||
var self = this;
|
||||
var hoisted = [];
|
||||
@@ -1264,7 +1326,7 @@ function Compressor(options, false_by_default) {
|
||||
});
|
||||
|
||||
AST_Function.DEFMETHOD("optimize", function(compressor){
|
||||
if (compressor.option("unused_func")) {
|
||||
if (compressor.option("unused")) {
|
||||
if (this.name && this.name.unreferenced()) {
|
||||
this.name = null;
|
||||
}
|
||||
@@ -1272,22 +1334,6 @@ function Compressor(options, false_by_default) {
|
||||
return this;
|
||||
});
|
||||
|
||||
AST_Defun.DEFMETHOD("optimize", function(compressor){
|
||||
if (compressor.option("unused_func")) {
|
||||
if (this.name.unreferenced()
|
||||
&& !(this.parent_scope instanceof AST_Toplevel)) {
|
||||
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
||||
name: this.name.name,
|
||||
file: this.start.file,
|
||||
line: this.start.line,
|
||||
col: this.start.col
|
||||
});
|
||||
return make_node(AST_EmptyStatement, this);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
SQUEEZE(AST_Call, function(self, compressor){
|
||||
self = self.clone();
|
||||
self.expression = self.expression.squeeze(compressor);
|
||||
|
||||
Reference in New Issue
Block a user