compressor successfully moved to TreeTransformer
This commit is contained in:
507
lib/compress.js
507
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;
|
||||
return self;
|
||||
});
|
||||
|
||||
/* -----[ 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);
|
||||
});
|
||||
|
||||
SQUEEZE(AST_ObjectProperty, function(self, compressor){
|
||||
self.value = self.value.squeeze(compressor);
|
||||
AST_Node.DEFMETHOD("squeeze", function(compressor){
|
||||
return this.transform(compressor);
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user