checkpoint
- discard statements with no side effects (unsafe? could be) - safer hoist_vars (needs some revamping of scope/mangling)
This commit is contained in:
@@ -69,6 +69,7 @@ var output = UglifyJS.OutputStream({
|
|||||||
|
|
||||||
files = files.map(do_file_1);
|
files = files.map(do_file_1);
|
||||||
files = files.map(do_file_2);
|
files = files.map(do_file_2);
|
||||||
|
UglifyJS.base54.sort();
|
||||||
files.forEach(do_file_3);
|
files.forEach(do_file_3);
|
||||||
if (ARGS.v) {
|
if (ARGS.v) {
|
||||||
sys.error("BASE54 digits: " + UglifyJS.base54.get());
|
sys.error("BASE54 digits: " + UglifyJS.base54.get());
|
||||||
@@ -124,7 +125,7 @@ function do_file_1(file) {
|
|||||||
|
|
||||||
function do_file_2(ast) {
|
function do_file_2(ast) {
|
||||||
time_it("scope", function(){
|
time_it("scope", function(){
|
||||||
//ast.figure_out_scope();
|
ast.figure_out_scope();
|
||||||
ast.compute_char_frequency();
|
ast.compute_char_frequency();
|
||||||
});
|
});
|
||||||
return ast;
|
return ast;
|
||||||
|
|||||||
112
lib/compress.js
112
lib/compress.js
@@ -64,8 +64,9 @@ 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,
|
||||||
hoist_funs : !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
|
warnings : true
|
||||||
});
|
});
|
||||||
@@ -461,6 +462,36 @@ function Compressor(options, false_by_default) {
|
|||||||
node.DEFMETHOD("negate", func);
|
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 ]----- */
|
/* -----[ node squeezers ]----- */
|
||||||
|
|
||||||
SQUEEZE(AST_Debugger, function(self, compressor){
|
SQUEEZE(AST_Debugger, function(self, compressor){
|
||||||
@@ -508,49 +539,55 @@ function Compressor(options, false_by_default) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
var hoisted = [];
|
var hoisted = [];
|
||||||
var defuns = {};
|
var defuns = {};
|
||||||
var vars = {}, vars_found = 0;
|
var vars = {}, vars_found = 0, vardecl = [];
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node !== self) {
|
if (node !== self) {
|
||||||
if (node instanceof AST_Defun && hoist_funs) {
|
if (node instanceof AST_Defun && hoist_funs && !node.hoisted) {
|
||||||
hoisted.push(node.clone());
|
hoisted.push(node.clone());
|
||||||
node.hoisted = true;
|
node.hoisted = true;
|
||||||
defuns[node.name.name] = 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){
|
node.definitions.forEach(function(def){
|
||||||
vars[def.name.name] = def;
|
vars[def.name.name] = def;
|
||||||
++vars_found;
|
++vars_found;
|
||||||
});
|
});
|
||||||
node.hoisted = true;
|
vardecl.push(node);
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope)
|
if (node instanceof AST_Scope)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
if (vars_found > 0) {
|
if (vars_found > 0 && vardecl.length > 1) {
|
||||||
if (self instanceof AST_Lambda && !self.uses_arguments) {
|
vardecl.forEach(function(v){ v.hoisted = true });
|
||||||
for (var i in vars) if (HOP(vars, i)) {
|
var node = make_node(AST_Var, self, {
|
||||||
var sym = vars[i].name;
|
definitions: Object.keys(vars).map(function(name){
|
||||||
if (!find_if(function(arg){ return arg.name == sym.name }, self.argnames)) {
|
var def = vars[name].clone();
|
||||||
self.argnames.push(sym);
|
def.value = null;
|
||||||
}
|
return def;
|
||||||
}
|
})
|
||||||
} else {
|
});
|
||||||
var node = make_node(AST_Var, self, {
|
hoisted.unshift(node);
|
||||||
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);
|
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){
|
SQUEEZE(AST_EmptyStatement, function(self, compressor){
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
@@ -790,9 +827,10 @@ function Compressor(options, false_by_default) {
|
|||||||
AST_Definitions.DEFMETHOD("to_assignments", function(){
|
AST_Definitions.DEFMETHOD("to_assignments", function(){
|
||||||
var assignments = this.definitions.reduce(function(a, def){
|
var assignments = this.definitions.reduce(function(a, def){
|
||||||
if (def.value) {
|
if (def.value) {
|
||||||
|
var name = make_node(AST_SymbolRef, def.name, def.name);
|
||||||
a.push(make_node(AST_Assign, def, {
|
a.push(make_node(AST_Assign, def, {
|
||||||
operator : "=",
|
operator : "=",
|
||||||
left : def.name,
|
left : name,
|
||||||
right : def.value
|
right : def.value
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -842,7 +880,31 @@ function Compressor(options, false_by_default) {
|
|||||||
self.argnames = do_list(self.argnames, compressor);
|
self.argnames = do_list(self.argnames, compressor);
|
||||||
self.hoist_declarations(compressor);
|
self.hoist_declarations(compressor);
|
||||||
self.body = tighten_body(self.body, 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){
|
SQUEEZE(AST_Call, function(self, compressor){
|
||||||
|
|||||||
16
lib/scope.js
16
lib/scope.js
@@ -50,7 +50,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// times on the same tree.
|
// times on the same tree.
|
||||||
|
|
||||||
// pass 1: setup scope chaining and handle definitions
|
// pass 1: setup scope chaining and handle definitions
|
||||||
var scope = this.parent_scope;
|
var self = this;
|
||||||
|
var scope = self.parent_scope = null;
|
||||||
var labels = {};
|
var labels = {};
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
@@ -110,7 +111,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
node.reference(sym);
|
node.reference(sym);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
self.walk(tw);
|
||||||
|
|
||||||
// pass 2: find back references and eval
|
// pass 2: find back references and eval
|
||||||
var func = null;
|
var func = null;
|
||||||
@@ -137,7 +138,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
self.walk(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("init_scope_vars", function(){
|
AST_Scope.DEFMETHOD("init_scope_vars", function(){
|
||||||
@@ -364,6 +365,14 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
|
|||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node instanceof AST_Constant)
|
if (node instanceof AST_Constant)
|
||||||
base54.consider(node.print_to_string());
|
base54.consider(node.print_to_string());
|
||||||
|
else if (node instanceof AST_Return)
|
||||||
|
base54.consider("return");
|
||||||
|
else if (node instanceof AST_Throw)
|
||||||
|
base54.consider("throw");
|
||||||
|
else if (node instanceof AST_Continue)
|
||||||
|
base54.consider("continue");
|
||||||
|
else if (node instanceof AST_Break)
|
||||||
|
base54.consider("break");
|
||||||
else if (node instanceof AST_Debugger)
|
else if (node instanceof AST_Debugger)
|
||||||
base54.consider("debugger");
|
base54.consider("debugger");
|
||||||
else if (node instanceof AST_Directive)
|
else if (node instanceof AST_Directive)
|
||||||
@@ -420,7 +429,6 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
|
|||||||
base54.consider(node.property);
|
base54.consider(node.property);
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
base54.sort();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var base54 = (function() {
|
var base54 = (function() {
|
||||||
|
|||||||
@@ -10,5 +10,13 @@ var code = fs.readFileSync(filename, "utf8");
|
|||||||
|
|
||||||
var ast = UglifyJS.parse(code);
|
var ast = UglifyJS.parse(code);
|
||||||
ast.figure_out_scope();
|
ast.figure_out_scope();
|
||||||
|
ast = ast.squeeze(UglifyJS.Compressor());
|
||||||
|
|
||||||
ast.compute_char_frequency();
|
ast.compute_char_frequency();
|
||||||
console.log(UglifyJS.base54.get().join(","));
|
UglifyJS.base54.sort();
|
||||||
|
|
||||||
|
ast.figure_out_scope();
|
||||||
|
ast.mangle_names();
|
||||||
|
|
||||||
|
sys.error(UglifyJS.base54.get());
|
||||||
|
sys.print(ast.print_to_string({ beautify: true }));
|
||||||
|
|||||||
Reference in New Issue
Block a user