checkpoint

- discard statements with no side effects (unsafe? could be)
- safer hoist_vars (needs some revamping of scope/mangling)
This commit is contained in:
Mihai Bazon
2012-09-11 13:15:55 +03:00
parent 1579c0fb97
commit da407d46c6
4 changed files with 110 additions and 31 deletions

View File

@@ -64,8 +64,9 @@ function Compressor(options, false_by_default) {
evaluate : !false_by_default,
booleans : !false_by_default,
loops : !false_by_default,
unused_func : !false_by_default,
hoist_funs : !false_by_default,
//hoist_vars : !false_by_default, // XXX: turns out, this is really bad
hoist_vars : !false_by_default,
warnings : true
});
@@ -461,6 +462,36 @@ function Compressor(options, false_by_default) {
node.DEFMETHOD("negate", func);
});
// determine if expression has side effects
(function(def){
def(AST_Node, function(){ return true });
def(AST_EmptyStatement, function(){ return false });
def(AST_Constant, function(){ return false });
def(AST_This, function(){ return false });
def(AST_Function, function(){ return false });
def(AST_SimpleStatement, function(){
return this.body.has_side_effects();
});
def(AST_Binary, function(){
return this.left.has_side_effects()
|| this.right.has_side_effects ();
});
def(AST_Conditional, function(){
return this.condition.has_side_effects()
|| this.consequent.has_side_effects()
|| this.alternative.has_side_effects();
});
def(AST_Unary, function(){
return this.operator == "delete"
|| this.operator == "++"
|| this.operator == "--";
});
})(function(node, func){
node.DEFMETHOD("has_side_effects", func);
});
/* -----[ node squeezers ]----- */
SQUEEZE(AST_Debugger, function(self, compressor){
@@ -508,49 +539,55 @@ function Compressor(options, false_by_default) {
var self = this;
var hoisted = [];
var defuns = {};
var vars = {}, vars_found = 0;
var vars = {}, vars_found = 0, vardecl = [];
var tw = new TreeWalker(function(node){
if (node !== self) {
if (node instanceof AST_Defun && hoist_funs) {
if (node instanceof AST_Defun && hoist_funs && !node.hoisted) {
hoisted.push(node.clone());
node.hoisted = true;
defuns[node.name.name] = true;
}
if (node instanceof AST_Var && hoist_vars) {
if (node instanceof AST_Var && hoist_vars && !node.hoisted) {
node.definitions.forEach(function(def){
vars[def.name.name] = def;
++vars_found;
});
node.hoisted = true;
vardecl.push(node);
}
if (node instanceof AST_Scope)
return true;
}
});
self.walk(tw);
if (vars_found > 0) {
if (self instanceof AST_Lambda && !self.uses_arguments) {
for (var i in vars) if (HOP(vars, i)) {
var sym = vars[i].name;
if (!find_if(function(arg){ return arg.name == sym.name }, self.argnames)) {
self.argnames.push(sym);
}
}
} else {
var node = make_node(AST_Var, self, {
definitions: Object.keys(vars).map(function(name){
var def = vars[name].clone();
def.value = null;
return def;
})
});
hoisted.unshift(node);
}
if (vars_found > 0 && vardecl.length > 1) {
vardecl.forEach(function(v){ v.hoisted = true });
var node = make_node(AST_Var, self, {
definitions: Object.keys(vars).map(function(name){
var def = vars[name].clone();
def.value = null;
return def;
})
});
hoisted.unshift(node);
}
self.body = hoisted.concat(self.body);
}
});
SQUEEZE(AST_SimpleStatement, function(self, compressor){
self = self.clone();
self.body = self.body.squeeze(compressor);
return self.optimize(compressor);
});
AST_SimpleStatement.DEFMETHOD("optimize", function(compressor){
if (!this.body.has_side_effects()) {
AST_Node.warn("Dropping side-effect-free statement [{line},{col}]", this.start);
return make_node(AST_EmptyStatement, this);
}
return this;
});
SQUEEZE(AST_EmptyStatement, function(self, compressor){
return self;
});
@@ -790,9 +827,10 @@ function Compressor(options, false_by_default) {
AST_Definitions.DEFMETHOD("to_assignments", function(){
var assignments = this.definitions.reduce(function(a, def){
if (def.value) {
var name = make_node(AST_SymbolRef, def.name, def.name);
a.push(make_node(AST_Assign, def, {
operator : "=",
left : def.name,
left : name,
right : def.value
}));
}
@@ -842,7 +880,31 @@ function Compressor(options, false_by_default) {
self.argnames = do_list(self.argnames, compressor);
self.hoist_declarations(compressor);
self.body = tighten_body(self.body, compressor);
return self;
return self.optimize(compressor);
});
AST_Lambda.DEFMETHOD("optimize", function(compressor){
if (compressor.option("unused_func")) {
if (this.name && this.name.unreferenced()) {
this.name = null;
}
}
return this;
});
AST_Defun.DEFMETHOD("optimize", function(compressor){
if (compressor.option("unused_func")) {
if (this.name.unreferenced()
&& !(this.parent_scope instanceof AST_Toplevel)) {
AST_Node.warn("Dropping unused function {name} [{line},{col}]", {
name: this.name.name,
line: this.start.line,
col: this.start.col
});
return make_node(AST_EmptyStatement, this);
}
}
return this;
});
SQUEEZE(AST_Call, function(self, compressor){