diff options
-rw-r--r-- | lib/compress.js | 507 | ||||
-rw-r--r-- | lib/transform.js | 4 | ||||
-rw-r--r-- | lib/utils.js | 7 |
3 files changed, 184 insertions, 334 deletions
diff --git a/lib/compress.js b/lib/compress.js index 4161842d..5c47e13d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -41,17 +41,6 @@ ***********************************************************************/ -// The layout of the compressor follows the code generator (see -// output.js). Basically each node will have a "squeeze" method -// that will apply all known compression rules for that node, and -// return a new node (or the original node if there was no -// compression). We can't quite use the TreeWalker for this -// because it's too simplistic. - -// The Compressor object is for storing the options and for -// maintaining various internal state that might be useful for -// squeezing nodes. - function Compressor(options, false_by_default) { if (!(this instanceof Compressor)) return new Compressor(options, false_by_default); @@ -79,18 +68,33 @@ function Compressor(options, false_by_default) { }; Compressor.prototype = new TreeTransformer; -defaults(Compressor.prototype, { +merge(Compressor.prototype, { option: function(key) { return this.options[key] }, warn: function() { if (this.options.warnings) AST_Node.warn.apply(AST_Node, arguments); + }, + before: function(node, descend, in_list) { + node = node.clone(); + if (node instanceof AST_Scope) { + node = node.hoist_declarations(this); + } + descend(node, this); + node = node.optimize(this); + return node; } }); (function(undefined){ - AST_Node.DEFMETHOD("optimize", function(){ - return this; + function OPT(node, optimizer) { + node.DEFMETHOD("optimize", function(compressor){ + return optimizer(this, compressor); + }); + }; + + OPT(AST_Node, function(self, compressor){ + return self; }); AST_Node.DEFMETHOD("equivalent_to", function(node){ @@ -599,11 +603,13 @@ defaults(Compressor.prototype, { }); def(AST_Binary, function(compressor){ var self = this.clone(), op = this.operator; - if (compressor.option("comparations")) switch (op) { - case "<=" : self.operator = ">" ; return self; - case "<" : self.operator = ">=" ; return self; - case ">=" : self.operator = "<" ; return self; - case ">" : self.operator = "<=" ; return self; + if (compressor.option("comparations") && compressor.option("unsafe")) { + switch (op) { + case "<=" : self.operator = ">" ; return self; + case "<" : self.operator = ">=" ; return self; + case ">=" : self.operator = "<" ; return self; + case ">" : self.operator = "<=" ; return self; + } } switch (op) { case "==" : self.operator = "!="; return self; @@ -715,40 +721,40 @@ defaults(Compressor.prototype, { /* -----[ optimizers ]----- */ - AST_Directive.DEFMETHOD("optimize", function(compressor){ - if (this.scope.has_directive(this.value) !== this.scope) { - return make_node(AST_EmptyStatement, this); + OPT(AST_Directive, function(self, compressor){ + if (self.scope.has_directive(self.value) !== self.scope) { + return make_node(AST_EmptyStatement, self); } - return this; + return self; }); - AST_Debugger.DEFMETHOD("optimize", function(compressor){ + OPT(AST_Debugger, function(self, compressor){ if (compressor.option("drop_debugger")) - return make_node(AST_EmptyStatement, this); - return this; + return make_node(AST_EmptyStatement, self); + return self; }); - AST_LabeledStatement.DEFMETHOD("optimize", function(){ - return this.label.references.length == 0 ? this.body : this; + OPT(AST_LabeledStatement, function(self, compressor){ + return self.label.references.length == 0 ? self.body : self; }); - 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); + OPT(AST_BlockStatement, function(self, compressor){ + self.body = tighten_body(self.body, compressor); + switch (self.body.length) { + case 1: return self.body[0]; + case 0: return make_node(AST_EmptyStatement, self); } - return this; + return self; }); - AST_Block.DEFMETHOD("optimize", function(compressor){ - this.body = tighten_body(this.body, compressor); - return this; + OPT(AST_Block, function(self, compressor){ + self.body = tighten_body(self.body, compressor); + return self; }); - AST_Scope.DEFMETHOD("optimize", function(compressor){ - this.body = tighten_body(this.body, compressor); - return this; + OPT(AST_Scope, function(self, compressor){ + self.body = tighten_body(self.body, compressor); + return self; }); AST_Scope.DEFMETHOD("drop_unused", function(compressor){ @@ -922,16 +928,15 @@ defaults(Compressor.prototype, { return self; }); - AST_SimpleStatement.DEFMETHOD("optimize", function(compressor){ - if (!this.body.has_side_effects()) { - compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", this.start); - return make_node(AST_EmptyStatement, this); + OPT(AST_SimpleStatement, function(self, compressor){ + if (!self.body.has_side_effects()) { + compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start); + return make_node(AST_EmptyStatement, self); } - return this; + return self; }); - AST_DWLoop.DEFMETHOD("optimize", function(compressor){ - var self = this; + OPT(AST_DWLoop, function(self, compressor){ var cond = self.condition.evaluate(compressor); self.condition = cond[0]; if (!compressor.option("loops")) return self; @@ -957,8 +962,8 @@ defaults(Compressor.prototype, { // // not helpful, it seems (output is a bit bigger after gzip) // - // AST_While.DEFMETHOD("optimize", function(compressor){ - // var self = AST_DWLoop.prototype.optimize.call(this, compressor); + // OPT(AST_While, function(self, compressor){ + // var self = AST_DWLoop.prototype.optimize.call(self, compressor); // if (self instanceof AST_While) { // self = make_node(AST_For, self, { // condition: self.condition, @@ -968,35 +973,34 @@ defaults(Compressor.prototype, { // return self; // }); - AST_For.DEFMETHOD("optimize", function(compressor){ - var cond = this.condition; + OPT(AST_For, function(self, compressor){ + var cond = self.condition; if (cond) { cond = cond.evaluate(compressor); - this.condition = cond[0]; + self.condition = cond[0]; } - if (!compressor.option("loops")) return this; + if (!compressor.option("loops")) return self; if (cond) { if (cond.length > 1 && !cond[1]) { if (compressor.option("dead_code")) { var a = []; - if (this.init instanceof AST_Statement) { - a.push(this.init); + if (self.init instanceof AST_Statement) { + a.push(self.init); } - else if (this.init) { - a.push(make_node(AST_SimpleStatement, this.init, { - body: this.init + else if (self.init) { + a.push(make_node(AST_SimpleStatement, self.init, { + body: self.init })); } - extract_declarations_from_unreachable_code(compressor, this.body, a); - return make_node(AST_BlockStatement, this, { body: a }); + extract_declarations_from_unreachable_code(compressor, self.body, a); + return make_node(AST_BlockStatement, self, { body: a }); } } } - return this; + return self; }); - AST_If.DEFMETHOD("optimize", function(compressor){ - var self = this; + OPT(AST_If, function(self, compressor){ if (!compressor.option("conditionals")) return self; // if condition can be statically determined, warn and drop // one of the blocks. note, statically determined implies @@ -1119,24 +1123,24 @@ defaults(Compressor.prototype, { return self; }); - AST_Switch.DEFMETHOD("optimize", function(compressor){ - var last_branch = this.body.body[this.body.body.length - 1]; + OPT(AST_Switch, function(self, compressor){ + var last_branch = self.body.body[self.body.body.length - 1]; if (last_branch) { var stat = last_branch.body[last_branch.body.length - 1]; // last statement if (stat instanceof AST_Break && !stat.label) last_branch.body.pop(); } - return this; + return self; }); - AST_Case.DEFMETHOD("optimize", function(compressor){ - this.body = tighten_body(this.body, compressor); - return this; + OPT(AST_Case, function(self, compressor){ + self.body = tighten_body(self.body, compressor); + return self; }); - AST_Try.DEFMETHOD("optimize", function(compressor){ - this.body = tighten_body(this.body, compressor); - return this; + OPT(AST_Try, function(self, compressor){ + self.body = tighten_body(self.body, compressor); + return self; }); AST_Definitions.DEFMETHOD("remove_initializers", function(){ @@ -1170,14 +1174,14 @@ defaults(Compressor.prototype, { })(assignments); }); - AST_Definitions.DEFMETHOD("optimize", function(compressor){ - if (this.definitions.length == 0) - return make_node(AST_EmptyStatement, this); - return this; + OPT(AST_Definitions, function(self, compressor){ + if (self.definitions.length == 0) + return make_node(AST_EmptyStatement, self); + return self; }); - AST_Function.DEFMETHOD("optimize", function(compressor){ - var self = AST_Lambda.prototype.optimize.call(this, compressor); + OPT(AST_Function, function(self, compressor){ + self = AST_Lambda.prototype.optimize.call(self, compressor); if (compressor.option("unused")) { if (self.name && self.name.unreferenced()) { self.name = null; @@ -1186,47 +1190,47 @@ defaults(Compressor.prototype, { return self; }); - AST_Call.DEFMETHOD("optimize", function(compressor){ + OPT(AST_Call, function(self, compressor){ if (compressor.option("unsafe")) { - var exp = this.expression; + var exp = self.expression; if (exp instanceof AST_SymbolRef && exp.undeclared()) { switch (exp.name) { case "Array": - if (this.args.length != 1) { - return make_node(AST_Array, this, { - elements: this.args + if (self.args.length != 1) { + return make_node(AST_Array, self, { + elements: self.args }).optimize(compressor); } break; case "Object": - if (this.args.length == 0) { - return make_node(AST_Object, this, { + if (self.args.length == 0) { + return make_node(AST_Object, self, { properties: [] }).optimize(compressor); } break; case "String": - return make_node(AST_Binary, this, { - left: this.args[0], + return make_node(AST_Binary, self, { + left: self.args[0], operator: "+", - right: make_node(AST_String, this, { value: "" }) + right: make_node(AST_String, self, { value: "" }) }); } } - else if (exp instanceof AST_Dot && exp.property == "toString" && this.args.length == 0) { - return make_node(AST_Binary, this, { + else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) { + return make_node(AST_Binary, self, { left: exp.expression, operator: "+", - right: make_node(AST_String, this, { value: "" }) + right: make_node(AST_String, self, { value: "" }) }); } } - return this; + return self; }); - AST_New.DEFMETHOD("optimize", function(compressor){ + OPT(AST_New, function(self, compressor){ if (compressor.option("unsafe")) { - var exp = this.expression; + var exp = self.expression; if (exp instanceof AST_SymbolRef && exp.undeclared()) { switch (exp.name) { case "Object": @@ -1234,15 +1238,14 @@ defaults(Compressor.prototype, { case "Function": case "Error": case "Array": - return make_node(AST_Call, this, this).optimize(compressor); + return make_node(AST_Call, self, self).optimize(compressor); } } } - return this; + return self; }); - AST_Seq.DEFMETHOD("optimize", function(compressor){ - var self = this; + OPT(AST_Seq, function(self, compressor){ if (compressor.option("cascade")) { if (self.cdr instanceof AST_Seq) self.cdr = self.cdr.optimize(compressor); @@ -1260,8 +1263,7 @@ defaults(Compressor.prototype, { return self; }); - AST_UnaryPrefix.DEFMETHOD("optimize", function(compressor){ - var self = this; + OPT(AST_UnaryPrefix, function(self, compressor){ var e = self.expression; if (compressor.option("booleans") && compressor.in_boolean_context()) { switch (self.operator) { @@ -1284,48 +1286,48 @@ defaults(Compressor.prototype, { return self.evaluate(compressor)[0]; }); - AST_Binary.DEFMETHOD("optimize", function(compressor){ - if (compressor.option("comparations")) switch (this.operator) { + OPT(AST_Binary, function(self, compressor){ + if (compressor.option("comparations")) switch (self.operator) { case "===": case "!==": - if ((this.left.is_string() && this.right.is_string()) || - (this.left.is_boolean() && this.right.is_boolean())) { - this.operator = this.operator.substr(0, 2); + if ((self.left.is_string() && self.right.is_string()) || + (self.left.is_boolean() && self.right.is_boolean())) { + self.operator = self.operator.substr(0, 2); } // XXX: intentionally falling down to the next case case "==": case "!=": - if (this.left instanceof AST_UnaryPrefix - && this.left.operator == "typeof" - && this.right instanceof AST_String - && this.right.value == "undefined") { - if (!(this.left.expression instanceof AST_SymbolRef) - || !this.left.expression.undeclared()) { - this.left = this.left.expression; - this.right = make_node(AST_Undefined, this.right).optimize(compressor); - if (this.operator.length == 2) this.operator += "="; + if (self.left instanceof AST_UnaryPrefix + && self.left.operator == "typeof" + && self.right instanceof AST_String + && self.right.value == "undefined") { + if (!(self.left.expression instanceof AST_SymbolRef) + || !self.left.expression.undeclared()) { + self.left = self.left.expression; + self.right = make_node(AST_Undefined, self.right).optimize(compressor); + if (self.operator.length == 2) self.operator += "="; } } - else if (this.left instanceof AST_String - && this.left.value == "undefined" - && this.right instanceof AST_UnaryPrefix - && this.right.operator == "typeof") { - if (!(this.right.expression instanceof AST_SymbolRef) - || !this.right.expression.undeclared()) { - this.left = this.right.expression; - this.right = make_node(AST_Undefined, this.left).optimize(compressor); - if (this.operator.length == 2) this.operator += "="; + else if (self.left instanceof AST_String + && self.left.value == "undefined" + && self.right instanceof AST_UnaryPrefix + && self.right.operator == "typeof") { + if (!(self.right.expression instanceof AST_SymbolRef) + || !self.right.expression.undeclared()) { + self.left = self.right.expression; + self.right = make_node(AST_Undefined, self.left).optimize(compressor); + if (self.operator.length == 2) self.operator += "="; } } break; } - if (compressor.option("booleans") && compressor.in_boolean_context()) switch (this.operator) { + if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) { case "&&": - var ll = this.left.evaluate(compressor), left = ll[0]; - var rr = this.right.evaluate(compressor), right = rr[0]; + var ll = self.left.evaluate(compressor), left = ll[0]; + var rr = self.right.evaluate(compressor), right = rr[0]; if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) { - compressor.warn("Boolean && always false [{file}:{line},{col}]", this.start); - return make_node(AST_False, this).optimize(compressor); + compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); + return make_node(AST_False, self).optimize(compressor); } if (ll.length > 1 && ll[1]) { return rr[0]; @@ -1335,11 +1337,11 @@ defaults(Compressor.prototype, { } break; case "||": - var ll = this.left.evaluate(compressor), left = ll[0]; - var rr = this.right.evaluate(compressor), right = rr[0]; + var ll = self.left.evaluate(compressor), left = ll[0]; + var rr = self.right.evaluate(compressor), right = rr[0]; if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) { - compressor.warn("Boolean || always true [{file}:{line},{col}]", this.start); - return make_node(AST_True, this).optimize(compressor); + compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); + return make_node(AST_True, self).optimize(compressor); } if (ll.length > 1 && !ll[1]) { return rr[0]; @@ -1349,21 +1351,20 @@ defaults(Compressor.prototype, { } break; case "+": - var ll = this.left.evaluate(compressor), left = ll[0]; - var rr = this.right.evaluate(compressor), right = rr[0]; + var ll = self.left.evaluate(compressor), left = ll[0]; + var rr = self.right.evaluate(compressor), right = rr[0]; if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) || (rr.length > 1 && rr[0] instanceof AST_String && rr[1])) { - compressor.warn("+ in boolean context always true [{file}:{line},{col}]", this.start); - return make_node(AST_True, this).optimize(compressor); + compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); + return make_node(AST_True, self).optimize(compressor); } break; } - var exp = this.evaluate(compressor); + var exp = self.evaluate(compressor); if (exp.length > 1) { - if (best_of(exp[0], this) !== this) + if (best_of(exp[0], self) !== self) return exp[0]; } - var self = this; if (compressor.option("comparations")) { if (!(compressor.parent() instanceof AST_Binary)) { var negated = make_node(AST_UnaryPrefix, self, { @@ -1386,22 +1387,22 @@ defaults(Compressor.prototype, { return self; }); - AST_SymbolRef.DEFMETHOD("optimize", function(compressor){ - if (this.undeclared()) switch (this.name) { + OPT(AST_SymbolRef, function(self, compressor){ + if (self.undeclared()) switch (self.name) { case "undefined": - return make_node(AST_Undefined, this).optimize(compressor); + return make_node(AST_Undefined, self).optimize(compressor); case "NaN": - return make_node(AST_NaN, this).optimize(compressor); + return make_node(AST_NaN, self).optimize(compressor); } - return this; + return self; }); - AST_Undefined.DEFMETHOD("optimize", function(compressor){ + OPT(AST_Undefined, function(self, compressor){ if (compressor.option("unsafe")) { var scope = compressor.find_parent(AST_Scope); var undef = scope.find_variable("undefined"); if (undef) { - var ref = make_node(AST_SymbolRef, this, { + var ref = make_node(AST_SymbolRef, self, { name : "undefined", scope : scope, thedef : undef @@ -1410,25 +1411,24 @@ defaults(Compressor.prototype, { return ref; } } - return this; + return self; }); var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ]; - AST_Assign.DEFMETHOD("optimize", function(compressor){ - if (this.operator == "=" - && this.left instanceof AST_SymbolRef - && this.right instanceof AST_Binary - && this.right.left instanceof AST_SymbolRef - && this.right.left.name == this.left.name - && member(this.right.operator, ASSIGN_OPS)) { - this.operator = this.right.operator + "="; - this.right = this.right.right; + OPT(AST_Assign, function(self, compressor){ + if (self.operator == "=" + && self.left instanceof AST_SymbolRef + && self.right instanceof AST_Binary + && self.right.left instanceof AST_SymbolRef + && self.right.left.name == self.left.name + && member(self.right.operator, ASSIGN_OPS)) { + self.operator = self.right.operator + "="; + self.right = self.right.right; } - return this; + return self; }); - AST_Conditional.DEFMETHOD("optimize", function(compressor){ - var self = this; + OPT(AST_Conditional, function(self, compressor){ if (!compressor.option("conditionals")) return self; if (self.condition instanceof AST_Seq) { var car = self.condition.car; @@ -1479,209 +1479,50 @@ defaults(Compressor.prototype, { return self; }); - AST_Boolean.DEFMETHOD("optimize", function(compressor){ + OPT(AST_Boolean, function(self, compressor){ if (compressor.option("booleans")) { var p = compressor.parent(); if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) { compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { operator : p.operator, - value : this.value, + value : self.value, file : p.start.file, line : p.start.line, col : p.start.col, }); - return make_node(AST_Number, this, { - value: +this.value + return make_node(AST_Number, self, { + value: +self.value }); } - return make_node(AST_UnaryPrefix, this, { + return make_node(AST_UnaryPrefix, self, { operator: "!", - expression: make_node(AST_Number, this, { - value: 1 - this.value + expression: make_node(AST_Number, self, { + value: 1 - self.value }) }); } - return this; + return self; }); - AST_Sub.DEFMETHOD("optimize", function(compressor){ - var prop = this.property; + OPT(AST_Sub, function(self, compressor){ + var prop = self.property; if (prop instanceof AST_String && compressor.option("properties")) { prop = prop.getValue(); if (is_identifier(prop)) { - return make_node(AST_Dot, this, { - expression : this.expression, + return make_node(AST_Dot, self, { + expression : self.expression, property : prop }); } } - return this; - }); - - /* -----[ node squeezers ]----- */ - - function SQUEEZE(nodetype, squeeze) { - nodetype.DEFMETHOD("squeeze", function(compressor){ - compressor.push(this); - var self = this.clone(), opt; - opt = squeeze(self, compressor); - if (opt !== undefined) self = opt; - opt = self.optimize(compressor); - if (opt !== undefined) self = opt; - compressor.pop(); - return self; - }); - }; - - function do_list(array, compressor) { - return MAP(array, function(node){ - return node.squeeze(compressor); - }); - }; - - SQUEEZE(AST_Node, noop); - - SQUEEZE(AST_LabeledStatement, function(self, compressor){ - self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_Statement, function(self, compressor){ - if (self.body) self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_BlockStatement, function(self, compressor){ - self.body = do_list(self.body, compressor); - }); - - SQUEEZE(AST_Block, function(self, compressor){ - self.body = do_list(self.body, compressor); - }); - - SQUEEZE(AST_SimpleStatement, function(self, compressor){ - self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_DWLoop, function(self, compressor){ - self.condition = self.condition.squeeze(compressor); - self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_For, function(self, compressor){ - if (self.init) self.init = self.init.squeeze(compressor); - if (self.condition) self.condition = self.condition.squeeze(compressor); - if (self.step) self.step = self.step.squeeze(compressor); - self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_ForIn, function(self, compressor){ - self.init = self.init.squeeze(compressor); - self.object = self.object.squeeze(compressor); - self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_With, function(self, compressor){ - self.expression = self.expression.squeeze(compressor); - self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_Exit, function(self, compressor){ - if (self.value) self.value = self.value.squeeze(compressor); - }); - - SQUEEZE(AST_LoopControl, function(self, compressor){ - if (self.label) self.label = self.label.squeeze(compressor); - }); - - SQUEEZE(AST_If, function(self, compressor){ - self.condition = self.condition.squeeze(compressor); - self.body = self.body.squeeze(compressor); - if (self.alternative) - self.alternative = self.alternative.squeeze(compressor); - }); - - SQUEEZE(AST_Switch, function(self, compressor){ - self.expression = self.expression.squeeze(compressor); - self.body = self.body.squeeze(compressor); - }); - - SQUEEZE(AST_Case, function(self, compressor){ - self.expression = self.expression.squeeze(compressor); - self.body = do_list(self.body, compressor); - }); - - SQUEEZE(AST_Try, function(self, 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); - }); - - SQUEEZE(AST_Definitions, function(self, compressor){ - self.definitions = do_list(self.definitions, compressor); - }); - - SQUEEZE(AST_VarDef, function(self, compressor){ - if (self.value) self.value = self.value.squeeze(compressor); - }); - - SQUEEZE(AST_Scope, function(self, compressor){ - self = self.hoist_declarations(compressor); - self.body = do_list(self.body, compressor); - return self; - }); - - SQUEEZE(AST_Lambda, function(self, compressor){ - self = self.hoist_declarations(compressor); - if (self.name) self.name = self.name.squeeze(compressor); - self.argnames = do_list(self.argnames, compressor); - self.body = do_list(self.body, compressor); return self; }); - SQUEEZE(AST_Call, function(self, compressor){ - self.expression = self.expression.squeeze(compressor); - self.args = do_list(self.args, compressor); - }); - - SQUEEZE(AST_Seq, function(self, compressor){ - self.car = self.car.squeeze(compressor); - self.cdr = self.cdr.squeeze(compressor); - }); - - SQUEEZE(AST_Dot, function(self, compressor){ - self.expression = self.expression.squeeze(compressor); - }); - - SQUEEZE(AST_Sub, function(self, compressor){ - self.expression = self.expression.squeeze(compressor); - self.property = self.property.squeeze(compressor); - }); - - SQUEEZE(AST_Unary, function(self, compressor){ - self.expression = self.expression.squeeze(compressor); - }); - - SQUEEZE(AST_Binary, function(self, compressor){ - self.left = self.left.squeeze(compressor); - self.right = self.right.squeeze(compressor); - }); - - SQUEEZE(AST_Conditional, function(self, compressor){ - self.condition = self.condition.squeeze(compressor); - self.consequent = self.consequent.squeeze(compressor); - self.alternative = self.alternative.squeeze(compressor); - }); - - SQUEEZE(AST_Array, function(self, compressor){ - self.elements = do_list(self.elements, compressor); - }); - - SQUEEZE(AST_Object, function(self, compressor){ - self.properties = do_list(self.properties, compressor); - }); + /* -----[ node squeezers ]----- */ - SQUEEZE(AST_ObjectProperty, function(self, compressor){ - self.value = self.value.squeeze(compressor); + AST_Node.DEFMETHOD("squeeze", function(compressor){ + return this.transform(compressor); }); })(); diff --git a/lib/transform.js b/lib/transform.js index 6f89679d..0d15e63f 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -65,7 +65,7 @@ TreeTransformer.prototype = new TreeWalker; } else { x = this.clone(); descend(x, tw); - y = tw.after(this, in_list); + y = tw.after(x, in_list); if (y !== undefined) x = y; } } @@ -148,6 +148,8 @@ TreeTransformer.prototype = new TreeWalker; _(AST_Try, function(self, tw){ self.body = do_list(self.body, tw); + if (self.bcatch) self.bcatch = self.bcatch.transform(tw); + if (self.bfinally) self.bfinally = self.bfinally.transform(tw); }); _(AST_Catch, function(self, tw){ diff --git a/lib/utils.js b/lib/utils.js index 72b525b6..5de83fbd 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -106,6 +106,13 @@ function defaults(args, defs) { return ret; }; +function merge(obj, ext) { + for (var i in ext) if (HOP(ext, i)) { + obj[i] = ext[i]; + } + return obj; +}; + function noop() {}; var MAP = (function(){ |