started some refactoring (WIP) -- moving squeezer to TreeTransformer
This commit is contained in:
228
lib/compress.js
228
lib/compress.js
@@ -146,31 +146,12 @@ function Compressor(options, false_by_default) {
|
||||
});
|
||||
};
|
||||
|
||||
function do_list(array, compressor, splice_blocks) {
|
||||
return MAP(array, function(node, i){
|
||||
node = node.squeeze(compressor, array, i);
|
||||
if (splice_blocks) {
|
||||
if (node instanceof AST_BlockStatement) {
|
||||
return MAP.splice(eliminate_spurious_blocks(node.body));
|
||||
}
|
||||
if (node instanceof AST_EmptyStatement)
|
||||
return MAP.skip;
|
||||
}
|
||||
return node;
|
||||
function do_list(array, compressor) {
|
||||
return MAP(array, function(node){
|
||||
return node.squeeze(compressor);
|
||||
});
|
||||
};
|
||||
|
||||
function eliminate_spurious_blocks(statements) {
|
||||
return statements.reduce(function(a, stat){
|
||||
if (stat instanceof AST_BlockStatement) {
|
||||
a.push.apply(a, eliminate_spurious_blocks(stat.body));
|
||||
} else if (!(stat instanceof AST_EmptyStatement)) {
|
||||
a.push(stat);
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
};
|
||||
|
||||
function as_statement_array(thing) {
|
||||
if (thing === null) return [];
|
||||
if (thing instanceof AST_BlockStatement) return thing.body;
|
||||
@@ -188,9 +169,9 @@ function Compressor(options, false_by_default) {
|
||||
|
||||
function tighten_body(statements, compressor) {
|
||||
var CHANGED;
|
||||
statements = do_list(statements, compressor, true);
|
||||
do {
|
||||
CHANGED = false;
|
||||
statements = eliminate_spurious_blocks(statements);
|
||||
if (compressor.option("dead_code")) {
|
||||
statements = eliminate_dead_code(statements, compressor);
|
||||
}
|
||||
@@ -203,10 +184,23 @@ function Compressor(options, false_by_default) {
|
||||
if (compressor.option("join_vars")) {
|
||||
statements = join_consecutive_vars(statements, compressor);
|
||||
}
|
||||
statements = eliminate_spurious_blocks(statements);
|
||||
} while (CHANGED);
|
||||
return statements;
|
||||
|
||||
function eliminate_spurious_blocks(statements) {
|
||||
return statements.reduce(function(a, stat){
|
||||
if (stat instanceof AST_BlockStatement) {
|
||||
CHANGED = true;
|
||||
a.push.apply(a, eliminate_spurious_blocks(stat.body));
|
||||
} else if (stat instanceof AST_EmptyStatement) {
|
||||
CHANGED = true;
|
||||
} else {
|
||||
a.push(stat);
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
};
|
||||
|
||||
function handle_if_return(statements, compressor) {
|
||||
var self = compressor.self();
|
||||
var in_lambda = self instanceof AST_Lambda;
|
||||
@@ -289,7 +283,7 @@ function Compressor(options, false_by_default) {
|
||||
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||
|| (ab instanceof AST_Continue && self === ab.target()))) {
|
||||
CHANGED = true;
|
||||
var body = tighten_body(as_statement_array(stat.body).slice(0, -1), compressor);
|
||||
var body = as_statement_array(stat.body).slice(0, -1);
|
||||
stat = stat.clone();
|
||||
stat.condition = stat.condition.negate(compressor);
|
||||
stat.body = make_node(AST_BlockStatement, stat, {
|
||||
@@ -308,10 +302,10 @@ function Compressor(options, false_by_default) {
|
||||
CHANGED = true;
|
||||
stat = stat.clone();
|
||||
stat.body = make_node(AST_BlockStatement, stat.body, {
|
||||
body: tighten_body(as_statement_array(stat.body).concat(ret), compressor)
|
||||
body: as_statement_array(stat.body).concat(ret)
|
||||
});
|
||||
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
|
||||
body: tighten_body(as_statement_array(stat.alternative).slice(0, -1), compressor)
|
||||
body: as_statement_array(stat.alternative).slice(0, -1)
|
||||
});
|
||||
ret = [ stat.squeeze(compressor) ];
|
||||
continue loop;
|
||||
@@ -331,6 +325,7 @@ function Compressor(options, false_by_default) {
|
||||
var has_quit = false;
|
||||
return statements.reduce(function(a, stat){
|
||||
if (has_quit) {
|
||||
CHANGED = true;
|
||||
extract_declarations_from_unreachable_code(compressor, stat, a);
|
||||
} else {
|
||||
a.push(stat);
|
||||
@@ -772,7 +767,7 @@ function Compressor(options, false_by_default) {
|
||||
});
|
||||
|
||||
AST_Directive.DEFMETHOD("optimize", function(compressor){
|
||||
if (this.hoisted || this.scope.has_directive(this.value) !== this.scope) {
|
||||
if (this.scope.has_directive(this.value) !== this.scope) {
|
||||
return make_node(AST_EmptyStatement, this);
|
||||
}
|
||||
return this;
|
||||
@@ -806,11 +801,12 @@ function Compressor(options, false_by_default) {
|
||||
|
||||
SQUEEZE(AST_BlockStatement, function(self, compressor){
|
||||
self = self.clone();
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
self.body = do_list(self.body, compressor);
|
||||
return self.optimize(compressor);
|
||||
});
|
||||
|
||||
AST_BlockStatement.DEFMETHOD("optimize", function(compressor){
|
||||
this.body = tighten_body(this.body, compressor);
|
||||
switch (this.body.length) {
|
||||
case 1: return this.body[0];
|
||||
case 0: return make_node(AST_EmptyStatement, this);
|
||||
@@ -820,15 +816,24 @@ function Compressor(options, false_by_default) {
|
||||
|
||||
SQUEEZE(AST_Block, function(self, compressor){
|
||||
self = self.clone();
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
return self;
|
||||
self.body = do_list(self.body, compressor);
|
||||
return self.optimize(compressor);
|
||||
});
|
||||
|
||||
AST_Block.DEFMETHOD("optimize", function(compressor){
|
||||
this.body = tighten_body(this.body, compressor);
|
||||
return this;
|
||||
});
|
||||
|
||||
SQUEEZE(AST_Scope, function(self, compressor){
|
||||
self = self.clone();
|
||||
self.hoist_declarations(compressor);
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
return self;
|
||||
self = self.clone().hoist_declarations(compressor);
|
||||
self.body = do_list(self.body, compressor);
|
||||
return self.optimize(compressor);
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("optimize", function(compressor){
|
||||
this.body = tighten_body(this.body, compressor);
|
||||
return this;
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||
@@ -937,48 +942,70 @@ function Compressor(options, false_by_default) {
|
||||
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
||||
var hoist_funs = compressor.option("hoist_funs");
|
||||
var hoist_vars = compressor.option("hoist_vars");
|
||||
this.drop_unused(compressor);
|
||||
var self = this;
|
||||
self.drop_unused(compressor);
|
||||
if (hoist_funs || hoist_vars) {
|
||||
var self = this;
|
||||
var dirs = [];
|
||||
var hoisted = [];
|
||||
var defuns = {};
|
||||
var vars = {}, vars_found = 0, vardecl = [];
|
||||
var tw = new TreeWalker(function(node){
|
||||
if (node !== self) {
|
||||
if (node instanceof AST_Directive && (hoist_funs || hoist_vars) && !node.hoisted) {
|
||||
hoisted.unshift(node.clone());
|
||||
node.hoisted = true;
|
||||
}
|
||||
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 && !node.hoisted) {
|
||||
node.definitions.forEach(function(def){
|
||||
vars[def.name.name] = def;
|
||||
++vars_found;
|
||||
});
|
||||
vardecl.push(node);
|
||||
}
|
||||
if (node instanceof AST_Scope)
|
||||
return true;
|
||||
var vars = {}, vars_found = 0, var_decl = 0;
|
||||
// let's count var_decl first, we seem to waste a lot of
|
||||
// space if we hoist `var` when there's only one.
|
||||
self.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_Scope && node !== self)
|
||||
return true;
|
||||
if (node instanceof AST_Var) {
|
||||
++var_decl;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
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);
|
||||
}));
|
||||
hoist_vars = hoist_vars && var_decl > 1;
|
||||
var tt = new TreeTransformer(
|
||||
function before(node) {
|
||||
if (node !== self) {
|
||||
if (node instanceof AST_Directive) {
|
||||
dirs.push(node);
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
if (node instanceof AST_Defun && hoist_funs) {
|
||||
hoisted.push(node);
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
if (node instanceof AST_Var && hoist_vars) {
|
||||
node.definitions.forEach(function(def){
|
||||
vars[def.name.name] = def;
|
||||
++vars_found;
|
||||
});
|
||||
var seq = node.to_assignments();
|
||||
var p = tt.parent();
|
||||
if (p instanceof AST_ForIn && p.init === node) {
|
||||
if (seq == null) return node.definitions[0].name;
|
||||
return seq;
|
||||
}
|
||||
if (p instanceof AST_For && p.init === node) {
|
||||
return seq;
|
||||
}
|
||||
if (!seq) return make_node(AST_EmptyStatement, node);
|
||||
return make_node(AST_SimpleStatement, node, {
|
||||
body: seq
|
||||
});
|
||||
}
|
||||
if (node instanceof AST_Scope)
|
||||
return node; // to avoid descending in nested scopes
|
||||
}
|
||||
}
|
||||
);
|
||||
self = self.transform(tt);
|
||||
|
||||
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
|
||||
definitions: Object.keys(vars).map(function(name){
|
||||
var def = vars[name].clone();
|
||||
def.value = null;
|
||||
return def;
|
||||
})
|
||||
}));
|
||||
self.body = dirs.concat(hoisted, self.body);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
SQUEEZE(AST_SimpleStatement, function(self, compressor){
|
||||
@@ -1260,16 +1287,26 @@ function Compressor(options, false_by_default) {
|
||||
SQUEEZE(AST_Case, function(self, compressor){
|
||||
self = self.clone();
|
||||
self.expression = self.expression.squeeze(compressor);
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
return self;
|
||||
self.body = do_list(self.body, compressor);
|
||||
return self.optimize(compressor);
|
||||
});
|
||||
|
||||
AST_Case.DEFMETHOD("optimize", function(compressor){
|
||||
this.body = tighten_body(this.body, compressor);
|
||||
return this;
|
||||
});
|
||||
|
||||
SQUEEZE(AST_Try, function(self, compressor){
|
||||
self = self.clone();
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
self.body = do_list(self.body, compressor);
|
||||
if (self.bcatch) self.bcatch = self.bcatch.squeeze(compressor);
|
||||
if (self.bfinally) self.bfinally = self.bfinally.squeeze(compressor);
|
||||
return self;
|
||||
return self.optimize(compressor);
|
||||
});
|
||||
|
||||
AST_Try.DEFMETHOD("optimize", function(compressor){
|
||||
this.body = tighten_body(this.body, compressor);
|
||||
return this;
|
||||
});
|
||||
|
||||
AST_Definitions.DEFMETHOD("remove_initializers", function(){
|
||||
@@ -1304,25 +1341,15 @@ function Compressor(options, false_by_default) {
|
||||
});
|
||||
|
||||
SQUEEZE(AST_Definitions, function(self, compressor){
|
||||
if (self.definitions.length == 0)
|
||||
return make_node(AST_EmptyStatement, self);
|
||||
if (self.hoisted) {
|
||||
var seq = self.to_assignments();
|
||||
var p = compressor.parent();
|
||||
if (seq) seq = seq.squeeze(compressor);
|
||||
if (p instanceof AST_ForIn && p.init === self) {
|
||||
if (seq == null) return self.definitions[0].name; //XXX: is this fine?
|
||||
return seq;
|
||||
}
|
||||
if (p instanceof AST_For && p.init === self) {
|
||||
return seq;
|
||||
}
|
||||
if (!seq) return make_node(AST_EmptyStatement, self);
|
||||
return make_node(AST_SimpleStatement, self, { body: seq });
|
||||
}
|
||||
self = self.clone();
|
||||
self.definitions = do_list(self.definitions, compressor);
|
||||
return self;
|
||||
return self.optimize(compressor);
|
||||
});
|
||||
|
||||
AST_Definitions.DEFMETHOD("optimize", function(compressor){
|
||||
if (this.definitions.length == 0)
|
||||
return make_node(AST_EmptyStatement, this);
|
||||
return this;
|
||||
});
|
||||
|
||||
SQUEEZE(AST_VarDef, function(self, compressor){
|
||||
@@ -1332,22 +1359,21 @@ function Compressor(options, false_by_default) {
|
||||
});
|
||||
|
||||
SQUEEZE(AST_Lambda, function(self, compressor){
|
||||
if (self.hoisted) return make_node(AST_EmptyStatement, self);
|
||||
self = self.clone();
|
||||
self = self.clone().hoist_declarations(compressor);
|
||||
if (self.name) self.name = self.name.squeeze(compressor);
|
||||
self.argnames = do_list(self.argnames, compressor);
|
||||
self.hoist_declarations(compressor);
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
self.body = do_list(self.body, compressor);
|
||||
return self.optimize(compressor);
|
||||
});
|
||||
|
||||
AST_Function.DEFMETHOD("optimize", function(compressor){
|
||||
var self = AST_Lambda.prototype.optimize.call(this, compressor);
|
||||
if (compressor.option("unused")) {
|
||||
if (this.name && this.name.unreferenced()) {
|
||||
this.name = null;
|
||||
if (self.name && self.name.unreferenced()) {
|
||||
self.name = null;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
return self;
|
||||
});
|
||||
|
||||
SQUEEZE(AST_Call, function(self, compressor){
|
||||
|
||||
Reference in New Issue
Block a user