aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/ast.js8
-rw-r--r--lib/compress.js144
-rw-r--r--lib/scope.js23
-rw-r--r--lib/transform.js17
-rw-r--r--lib/utils.js12
5 files changed, 137 insertions, 67 deletions
diff --git a/lib/ast.js b/lib/ast.js
index 19d9fbcd..3291ed83 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -57,7 +57,9 @@ function DEFNODE(type, props, methods, base) {
var proto = base && new base;
if (proto && proto.initialize || (methods && methods.initialize))
code += "this.initialize();";
- code += " } }";
+ code += " } ";
+ code += "if (!this.$self) this.$self = this;";
+ code += " } ";
var ctor = new Function(code)();
if (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", {
}, null);
-var AST_Node = DEFNODE("Node", "start end", {
+var AST_Node = DEFNODE("Node", "$self start end", {
clone: function() {
return new this.CTOR(this);
},
@@ -592,7 +594,7 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
$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)",
}, AST_Symbol);
diff --git a/lib/compress.js b/lib/compress.js
index 6813925e..a93b39b9 100644
--- a/lib/compress.js
+++ b/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);
diff --git a/lib/scope.js b/lib/scope.js
index d089b9da..833a96ef 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -101,6 +101,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
delete labels[l.name];
return true; // no descend again
}
+ if (node instanceof AST_SymbolDeclaration) {
+ node.init_scope_vars();
+ }
if (node instanceof AST_Symbol) {
node.scope = scope;
}
@@ -130,6 +133,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
}
else if (node instanceof AST_SymbolLambda) {
scope.def_function(node);
+ node.init.push(tw.parent());
}
else if (node instanceof AST_SymbolDefun) {
// 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
// later.
(node.scope = scope.parent_scope).def_function(node);
+ node.init.push(tw.parent());
}
else if (node instanceof AST_SymbolVar) {
scope.def_variable(node);
+ var def = tw.parent();
+ if (def.value) node.init.push(def);
}
else if (node instanceof AST_SymbolCatch) {
// 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(){
this.references = [];
});
@@ -318,7 +329,7 @@ AST_LoopControl.DEFMETHOD("target", function(){
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
// 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
@@ -353,13 +364,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(){
});
this.walk(tw);
- // strangely, if we try to give more frequently used variables
- // shorter name, the size after gzip seems to be higher! so leave
- // this commented out I guess...
- //
- // to_mangle = mergeSort(to_mangle, function(a, b){
- // return b.references.length - a.references.length;
- // });
+ if (sort) to_mangle = mergeSort(to_mangle, function(a, b){
+ return b.references.length - a.references.length;
+ });
to_mangle.forEach(function(def){ def.mangle() });
});
diff --git a/lib/transform.js b/lib/transform.js
index 743dd6e7..df51eac3 100644
--- a/lib/transform.js
+++ b/lib/transform.js
@@ -68,14 +68,17 @@ TreeTransformer.prototype = {
node.DEFMETHOD("transform", function(tw, in_list){
var x, y;
tw.push(this);
- x = tw.before(this, function(){
- descend(x, tw);
- }, in_list);
+ x = tw.before(this, descend, in_list);
if (x === undefined) {
- x = this.clone();
- descend(x, tw);
- y = tw.after(this, in_list);
- if (y !== undefined) x = y;
+ if (!tw.after) {
+ x = this;
+ descend(x, tw);
+ } else {
+ x = this.clone();
+ descend(x, tw);
+ y = tw.after(this, in_list);
+ if (y !== undefined) x = y;
+ }
}
tw.pop();
return x;
diff --git a/lib/utils.js b/lib/utils.js
index f8f4102a..72b525b6 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -190,3 +190,15 @@ function mergeSort(array, cmp) {
};
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;
+ });
+};