aboutsummaryrefslog
aboutsummaryrefslogtreecommitdiff
path: root/lib/compress.js
diff options
context:
space:
mode:
authorMihai Bazon <mihai@bazon.net>2012-09-23 12:47:34 +0300
committerMihai Bazon <mihai@bazon.net>2012-09-23 12:47:34 +0300
commita83b28503f4673aeb744b45306500584a6480e29 (patch)
tree52c34c1e72d32531dd3e1a6c7a1eb935a70c23ec /lib/compress.js
parent76d88b59dcd28b836b2cdaf7908bfd5acaa88ebc (diff)
downloadtracifyjs-a83b28503f4673aeb744b45306500584a6480e29.tar.gz
tracifyjs-a83b28503f4673aeb744b45306500584a6480e29.zip
properly drop mutually-referring declarations that are not otherwise
referenced and have no side effects
Diffstat (limited to 'lib/compress.js')
-rw-r--r--lib/compress.js144
1 files changed, 95 insertions, 49 deletions
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);