diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ast.js | 7 | ||||
-rw-r--r-- | lib/compress.js | 149 |
2 files changed, 139 insertions, 17 deletions
@@ -2,6 +2,7 @@ function DEFNODE(type, props, methods, base) { if (arguments.length < 4) base = AST_Node; if (!props) props = []; else props = props.split(/\s+/); + var self_props = props; if (base && base.PROPS) props = props.concat(base.PROPS); var code = "return function AST_" + type + "(props){ if (props) { "; @@ -19,6 +20,7 @@ function DEFNODE(type, props, methods, base) { } ctor.prototype.CTOR = ctor; ctor.PROPS = props || null; + ctor.SELF_PROPS = self_props; if (type) { ctor.prototype.TYPE = ctor.TYPE = type; } @@ -563,7 +565,10 @@ TreeWalker.prototype = { if (!ret && descend) { descend.call(node); } - this.stack.pop(node); + this.stack.pop(); return ret; + }, + parent: function(n) { + return this.stack[this.stack.length - 2 - (n || 0)]; } }; diff --git a/lib/compress.js b/lib/compress.js index c28aacce..89c128d0 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -9,13 +9,16 @@ // maintaining various internal state that might be useful for // squeezing nodes. -function Compressor(options) { +function Compressor(options, false_by_default) { options = defaults(options, { - sequences : true, - dead_code : true, - keep_comps : true, - drop_debugger : true, - unsafe : true + sequences : !false_by_default, + properties : !false_by_default, + dead_code : !false_by_default, + keep_comps : !false_by_default, + drop_debugger : !false_by_default, + unsafe : !false_by_default, + + warnings : true }); var stack = []; return { @@ -25,6 +28,10 @@ function Compressor(options) { stack : function() { return stack }, parent : function(n) { return stack[stack.length - 2 - (n || 0)]; + }, + warn : function() { + if (options.warnings) + AST_Node.warn.apply(AST_Node, arguments); } }; }; @@ -35,6 +42,12 @@ function Compressor(options) { return this; }); + function make_node(ctor, orig, props) { + if (!props.start) props.start = orig.start; + if (!props.end) props.end = orig.end; + return new ctor(props); + }; + function SQUEEZE(nodetype, squeeze) { nodetype.DEFMETHOD("squeeze", function(compressor){ compressor.push_node(this); @@ -46,11 +59,6 @@ function Compressor(options) { function do_list(array, compressor) { return MAP(array, function(node){ - if (node instanceof Array) { - sys.debug(node.map(function(node){ - return node.TYPE; - }).join("\n")); - } return node.squeeze(compressor); }); }; @@ -72,15 +80,109 @@ function Compressor(options) { return self; }); + function tighten_body(statements, compressor) { + statements = do_list(statements, compressor); + statements = eliminate_spurious_blocks(statements); + if (compressor.option("dead_code")) { + statements = eliminate_dead_code(statements, compressor); + } + if (compressor.option("sequences")) { + statements = sequencesize(statements); + } + return statements; + }; + + function eliminate_spurious_blocks(statements) { + return statements.reduce(function(a, stat){ + if (stat.TYPE == "BlockStatement") { + // XXX: no instanceof here because we would catch + // AST_Lambda-s and other blocks too. perhaps we + // should refine the hierarchy. + a.push.apply(a, stat.body); + } else { + a.push(stat); + } + return a; + }, []); + } + + function eliminate_dead_code(statements, compressor) { + var has_quit = false; + return statements.reduce(function(a, stat){ + if (has_quit) { + if (stat instanceof AST_Defun) { + a.push(stat); + } + else if (compressor.option("warnings")) { + stat.walk(new TreeWalker(function(node){ + if (node instanceof AST_Definitions + || node instanceof AST_Defun) { + compressor.warn("Declarations in unreachable code! [{line},{col}]", node.start); + if (node instanceof AST_Definitions) { + node = node.clone(); + node.remove_initializers(); + a.push(node); + } + else if (node instanceof AST_Defun) { + a.push(node); + } + return true; + } + if (node instanceof AST_Scope) + return true; + })) + }; + } else { + a.push(stat); + if (stat instanceof AST_Jump) { + has_quit = true; + } + } + return a; + }, []); + } + + function sequencesize(statements) { + var prev = null, last = statements.length - 1; + if (last) statements = statements.reduce(function(a, cur, i){ + if (prev instanceof AST_SimpleStatement + && cur instanceof AST_SimpleStatement) { + var seq = make_node(AST_Seq, prev, { + first: prev.body, + second: cur.body + }); + prev.body = seq; + } + else if (i == last && cur instanceof AST_Exit + && cur.value && a.length == 1) { + // it only makes sense to do this transformation + // if the AST gets to a single statement. + var seq = make_node(AST_Seq, prev, { + first: prev.body, + second: cur.value + }); + cur.value = seq; + return [ cur ]; + } + else { + a.push(cur); + prev = cur; + } + return a; + }, []); + return statements; + } + SQUEEZE(AST_BlockStatement, function(self, compressor){ self = self.clone(); - self.body = do_list(self.body, compressor); + self.body = tighten_body(self.body, compressor); + if (self.body.length == 1 && !self.required) + return self.body[0]; return self; }); SQUEEZE(AST_EmptyStatement, function(self, compressor){ - if (compressor.parent() instanceof AST_BlockStatement) - return MAP.skip; + return self; }); SQUEEZE(AST_DWLoop, function(self, compressor){ @@ -144,7 +246,7 @@ function Compressor(options) { SQUEEZE(AST_Case, function(self, compressor){ self = self.clone(); self.expression = self.expression.squeeze(compressor); - self.body = do_list(self.body, compressor); + self.body = tighten_body(self.body, compressor); return self; }); @@ -156,6 +258,14 @@ function Compressor(options) { return self; }); + AST_Definitions.DEFMETHOD("remove_initializers", function(){ + this.definitions = this.definitions.map(function(def){ + var def = def.clone(); + def.value = null; + return def; + }); + }); + SQUEEZE(AST_Definitions, function(self, compressor){ self = self.clone(); self.definitions = do_list(self.definitions, compressor); @@ -199,7 +309,14 @@ function Compressor(options) { SQUEEZE(AST_Sub, function(self, compressor){ self = self.clone(); self.expression = self.expression.squeeze(compressor); - self.property = self.property.squeeze(compressor); + var prop = self.property = self.property.squeeze(compressor); + if (prop instanceof AST_String && compressor.option("properties")) { + prop = prop.getValue(); + if (is_identifier(prop)) { + self = new AST_Dot(self); + self.property = prop; + } + } return self; }); |