properly drop mutually-referring declarations that are not otherwise
referenced and have no side effects
This commit is contained in:
@@ -57,7 +57,9 @@ function DEFNODE(type, props, methods, base) {
|
|||||||
var proto = base && new base;
|
var proto = base && new base;
|
||||||
if (proto && proto.initialize || (methods && methods.initialize))
|
if (proto && proto.initialize || (methods && methods.initialize))
|
||||||
code += "this.initialize();";
|
code += "this.initialize();";
|
||||||
code += " } }";
|
code += " } ";
|
||||||
|
code += "if (!this.$self) this.$self = this;";
|
||||||
|
code += " } ";
|
||||||
var ctor = new Function(code)();
|
var ctor = new Function(code)();
|
||||||
if (proto) {
|
if (proto) {
|
||||||
ctor.prototype = proto;
|
ctor.prototype = proto;
|
||||||
@@ -89,7 +91,7 @@ function DEFNODE(type, props, methods, base) {
|
|||||||
var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
|
var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
var AST_Node = DEFNODE("Node", "start end", {
|
var AST_Node = DEFNODE("Node", "$self start end", {
|
||||||
clone: function() {
|
clone: function() {
|
||||||
return new this.CTOR(this);
|
return new this.CTOR(this);
|
||||||
},
|
},
|
||||||
@@ -592,7 +594,7 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
|||||||
$documentation: "Base class for all symbols",
|
$documentation: "Base class for all symbols",
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", null, {
|
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
||||||
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
|
|||||||
140
lib/compress.js
140
lib/compress.js
@@ -64,8 +64,7 @@ function Compressor(options, false_by_default) {
|
|||||||
evaluate : !false_by_default,
|
evaluate : !false_by_default,
|
||||||
booleans : !false_by_default,
|
booleans : !false_by_default,
|
||||||
loops : !false_by_default,
|
loops : !false_by_default,
|
||||||
unused_func : !false_by_default,
|
unused : !false_by_default,
|
||||||
unused_vars : !false_by_default,
|
|
||||||
hoist_funs : !false_by_default,
|
hoist_funs : !false_by_default,
|
||||||
hoist_vars : !false_by_default,
|
hoist_vars : !false_by_default,
|
||||||
if_return : !false_by_default,
|
if_return : !false_by_default,
|
||||||
@@ -816,50 +815,113 @@ function Compressor(options, false_by_default) {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("drop_unused_vars", function(compressor){
|
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||||
if (compressor.option("unused_vars")) {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var tw = new TreeWalker(function(node){
|
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 !== self) {
|
||||||
if (node instanceof AST_Scope)
|
if (node instanceof AST_Defun) {
|
||||||
return true; // don't go in nested scopes
|
return true; // don't go in nested scopes
|
||||||
if (node instanceof AST_Definitions) {
|
}
|
||||||
if (!(tw.parent() instanceof AST_ForIn)) {
|
if (node instanceof AST_Definitions && scope === self) {
|
||||||
var a = node.definitions;
|
node.definitions.forEach(function(def){
|
||||||
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()) {
|
if (def.value && def.value.has_side_effects()) {
|
||||||
compressor.warn("Side effects in initialization of unreferenced variable {name} [{file}:{line},{col}]", warn);
|
def.value.walk(tw);
|
||||||
} else {
|
|
||||||
compressor.warn("Dropping unreferenced variable {name} [{file}:{line},{col}]", warn);
|
|
||||||
a.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!(node instanceof AST_Statement)) {
|
if (node instanceof AST_SymbolRef && !(node instanceof AST_LabelRef)) {
|
||||||
return true; // pointless to visit expressions
|
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){
|
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
||||||
var hoist_funs = compressor.option("hoist_funs");
|
var hoist_funs = compressor.option("hoist_funs");
|
||||||
var hoist_vars = compressor.option("hoist_vars");
|
var hoist_vars = compressor.option("hoist_vars");
|
||||||
this.drop_unused_vars(compressor);
|
this.drop_unused(compressor);
|
||||||
if (hoist_funs || hoist_vars) {
|
if (hoist_funs || hoist_vars) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var hoisted = [];
|
var hoisted = [];
|
||||||
@@ -1264,7 +1326,7 @@ function Compressor(options, false_by_default) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Function.DEFMETHOD("optimize", function(compressor){
|
AST_Function.DEFMETHOD("optimize", function(compressor){
|
||||||
if (compressor.option("unused_func")) {
|
if (compressor.option("unused")) {
|
||||||
if (this.name && this.name.unreferenced()) {
|
if (this.name && this.name.unreferenced()) {
|
||||||
this.name = null;
|
this.name = null;
|
||||||
}
|
}
|
||||||
@@ -1272,22 +1334,6 @@ function Compressor(options, false_by_default) {
|
|||||||
return this;
|
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){
|
SQUEEZE(AST_Call, function(self, compressor){
|
||||||
self = self.clone();
|
self = self.clone();
|
||||||
self.expression = self.expression.squeeze(compressor);
|
self.expression = self.expression.squeeze(compressor);
|
||||||
|
|||||||
23
lib/scope.js
23
lib/scope.js
@@ -101,6 +101,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
delete labels[l.name];
|
delete labels[l.name];
|
||||||
return true; // no descend again
|
return true; // no descend again
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_SymbolDeclaration) {
|
||||||
|
node.init_scope_vars();
|
||||||
|
}
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
node.scope = scope;
|
node.scope = scope;
|
||||||
}
|
}
|
||||||
@@ -130,6 +133,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolLambda) {
|
else if (node instanceof AST_SymbolLambda) {
|
||||||
scope.def_function(node);
|
scope.def_function(node);
|
||||||
|
node.init.push(tw.parent());
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolDefun) {
|
else if (node instanceof AST_SymbolDefun) {
|
||||||
// Careful here, the scope where this should be defined is
|
// Careful here, the scope where this should be defined is
|
||||||
@@ -138,9 +142,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// instanceof AST_Scope) but we get to the symbol a bit
|
// instanceof AST_Scope) but we get to the symbol a bit
|
||||||
// later.
|
// later.
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
(node.scope = scope.parent_scope).def_function(node);
|
||||||
|
node.init.push(tw.parent());
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolVar) {
|
else if (node instanceof AST_SymbolVar) {
|
||||||
scope.def_variable(node);
|
scope.def_variable(node);
|
||||||
|
var def = tw.parent();
|
||||||
|
if (def.value) node.init.push(def);
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
// XXX: this is wrong according to ECMA-262 (12.4). the
|
||||||
@@ -233,6 +240,10 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
||||||
|
this.init = [];
|
||||||
|
});
|
||||||
|
|
||||||
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
||||||
this.references = [];
|
this.references = [];
|
||||||
});
|
});
|
||||||
@@ -318,7 +329,7 @@ AST_LoopControl.DEFMETHOD("target", function(){
|
|||||||
return this.loopcontrol_target;
|
return this.loopcontrol_target;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("mangle_names", function(){
|
AST_Toplevel.DEFMETHOD("mangle_names", function(sort){
|
||||||
// We only need to mangle declaration nodes. Special logic wired
|
// We only need to mangle declaration nodes. Special logic wired
|
||||||
// into the code generator will display the mangled name if it's
|
// into the code generator will display the mangled name if it's
|
||||||
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
||||||
@@ -353,13 +364,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(){
|
|||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
|
|
||||||
// strangely, if we try to give more frequently used variables
|
if (sort) to_mangle = mergeSort(to_mangle, function(a, b){
|
||||||
// shorter name, the size after gzip seems to be higher! so leave
|
return b.references.length - a.references.length;
|
||||||
// this commented out I guess...
|
});
|
||||||
//
|
|
||||||
// to_mangle = mergeSort(to_mangle, function(a, b){
|
|
||||||
// return b.references.length - a.references.length;
|
|
||||||
// });
|
|
||||||
|
|
||||||
to_mangle.forEach(function(def){ def.mangle() });
|
to_mangle.forEach(function(def){ def.mangle() });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -68,15 +68,18 @@ TreeTransformer.prototype = {
|
|||||||
node.DEFMETHOD("transform", function(tw, in_list){
|
node.DEFMETHOD("transform", function(tw, in_list){
|
||||||
var x, y;
|
var x, y;
|
||||||
tw.push(this);
|
tw.push(this);
|
||||||
x = tw.before(this, function(){
|
x = tw.before(this, descend, in_list);
|
||||||
descend(x, tw);
|
|
||||||
}, in_list);
|
|
||||||
if (x === undefined) {
|
if (x === undefined) {
|
||||||
|
if (!tw.after) {
|
||||||
|
x = this;
|
||||||
|
descend(x, tw);
|
||||||
|
} else {
|
||||||
x = this.clone();
|
x = this.clone();
|
||||||
descend(x, tw);
|
descend(x, tw);
|
||||||
y = tw.after(this, in_list);
|
y = tw.after(this, in_list);
|
||||||
if (y !== undefined) x = y;
|
if (y !== undefined) x = y;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
tw.pop();
|
tw.pop();
|
||||||
return x;
|
return x;
|
||||||
});
|
});
|
||||||
|
|||||||
12
lib/utils.js
12
lib/utils.js
@@ -190,3 +190,15 @@ function mergeSort(array, cmp) {
|
|||||||
};
|
};
|
||||||
return _ms(array);
|
return _ms(array);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function set_difference(a, b) {
|
||||||
|
return a.filter(function(el){
|
||||||
|
return b.indexOf(el) < 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function set_intersection(a, b) {
|
||||||
|
return a.filter(function(el){
|
||||||
|
return b.indexOf(el) >= 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user