From 7413899ad48720c826c604fb7627d6bededdb4ce Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Wed, 1 Feb 2017 21:33:42 +0800 Subject: [PATCH] minor tweak to sequence manipulations - loop through `AST_Seq` chain instead of recursion - avoid deep-cloning in `lift_sequences()` - `AST_Seq.car` should never be `null` or `AST_Seq` - `AST_Seq.cdr` should never be `null` --- lib/ast.js | 61 +++++++++++++++++++------------------ lib/compress.js | 66 +++++++++++++++++++++++------------------ lib/output.js | 12 ++++---- test/compress/typeof.js | 6 ++-- 4 files changed, 77 insertions(+), 68 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index 42506cb2..18053895 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -589,6 +589,15 @@ var AST_Seq = DEFNODE("Seq", "car cdr", { cdr: "[AST_Node] second element in sequence" }, $cons: function(x, y) { + if (!x) { + throw new Error('AST_Seq.car missing'); + } + if (!y) { + throw new Error('AST_Seq.cdr missing'); + } + if (x instanceof AST_Seq) { + throw new Error('AST_Seq.car cannot be AST_Seq'); + } var seq = new AST_Seq(x); seq.car = x; seq.cdr = y; @@ -596,54 +605,48 @@ var AST_Seq = DEFNODE("Seq", "car cdr", { }, $from_array: function(array) { if (array.length == 0) return null; - if (array.length == 1) return array[0].clone(); - var list = null; - for (var i = array.length; --i >= 0;) { + if (array.length == 1) return array[0]; + var i = array.length - 1; + var list = array[i]; + while (--i >= 0) { list = AST_Seq.cons(array[i], list); } - var p = list; - while (p) { - if (p.cdr && !p.cdr.cdr) { - p.cdr = p.cdr.car; - break; - } - p = p.cdr; - } return list; }, to_array: function() { - var p = this, a = []; - while (p) { + var a = []; + var p = this; + do { a.push(p.car); - if (p.cdr && !(p.cdr instanceof AST_Seq)) { - a.push(p.cdr); - break; - } p = p.cdr; - } + } while (p instanceof AST_Seq); + a.push(p); return a; }, - add: function(node) { + tail: function() { var p = this; - while (p) { - if (!(p.cdr instanceof AST_Seq)) { - var cell = AST_Seq.cons(p.cdr, node); - return p.cdr = cell; - } + while (p.cdr instanceof AST_Seq) { p = p.cdr; } + return p; + }, + add: function(node) { + var p = this.tail(); + p.cdr = AST_Seq.cons(p.cdr, node); }, len: function() { - if (this.cdr instanceof AST_Seq) { - return this.cdr.len() + 1; - } else { - return 2; + var i = 2; + var p = this.cdr; + while (p instanceof AST_Seq) { + p = p.cdr; + i++; } + return i; }, _walk: function(visitor) { return visitor._visit(this, function(){ this.car._walk(visitor); - if (this.cdr) this.cdr._walk(visitor); + this.cdr._walk(visitor); }); } }); diff --git a/lib/compress.js b/lib/compress.js index 4e45df92..9105401f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -938,7 +938,7 @@ merge(Compressor.prototype, { return this.operator == "=" && this.right.is_boolean(); }); def(AST_Seq, function(){ - return this.cdr.is_boolean(); + return this.tail().cdr.is_boolean(); }); def(AST_True, return_true); def(AST_False, return_true); @@ -961,7 +961,7 @@ merge(Compressor.prototype, { return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor); }); def(AST_Seq, function(compressor){ - return this.cdr.is_string(compressor); + return this.tail().cdr.is_string(compressor); }); def(AST_Conditional, function(compressor){ return this.consequent.is_string(compressor) && this.alternative.is_string(compressor); @@ -1340,8 +1340,12 @@ merge(Compressor.prototype, { return !compressor.option("pure_getters"); }); def(AST_Seq, function(compressor){ - return this.car.has_side_effects(compressor) - || this.cdr.has_side_effects(compressor); + var p = this; + do { + if (p.car.has_side_effects(compressor)) return true; + p = p.cdr; + } while (p instanceof AST_Seq); + return p.has_side_effects(compressor); }); })(function(node, func){ node.DEFMETHOD("has_side_effects", func); @@ -2353,12 +2357,11 @@ merge(Compressor.prototype, { AST_Unary.DEFMETHOD("lift_sequences", function(compressor){ if (compressor.option("sequences")) { - if (this.expression instanceof AST_Seq) { - var seq = this.expression; - var x = seq.to_array(); - this.expression = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); + var seq = this.expression; + if (seq instanceof AST_Seq) { + var p = seq.tail(); + this.expression = p.cdr; + p.cdr = this.transform(compressor); return seq; } } @@ -2371,8 +2374,9 @@ merge(Compressor.prototype, { OPT(AST_UnaryPrefix, function(self, compressor){ self = self.lift_sequences(compressor); - var e = self.expression; + if (!(self instanceof AST_UnaryPrefix)) return self; if (compressor.option("booleans") && compressor.in_boolean_context()) { + var e = self.expression; switch (self.operator) { case "!": if (e instanceof AST_UnaryPrefix && e.operator == "!") { @@ -2409,22 +2413,20 @@ merge(Compressor.prototype, { AST_Binary.DEFMETHOD("lift_sequences", function(compressor){ if (compressor.option("sequences")) { - if (this.left instanceof AST_Seq) { - var seq = this.left; - var x = seq.to_array(); - this.left = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); + var seq = this.left; + if (seq instanceof AST_Seq) { + var p = seq.tail(); + this.left = p.cdr; + p.cdr = this.transform(compressor); return seq; } - if (this.right instanceof AST_Seq + seq = this.right; + if (seq instanceof AST_Seq && this instanceof AST_Assign && !has_side_effects_or_prop_access(this.left, compressor)) { - var seq = this.right; - var x = seq.to_array(); - this.right = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); + var p = seq.tail(); + this.right = p.cdr; + p.cdr = this.transform(compressor); return seq; } } @@ -2492,6 +2494,7 @@ merge(Compressor.prototype, { } } self = self.lift_sequences(compressor); + if (!(self instanceof AST_Binary)) return self; if (compressor.option("comparisons")) switch (self.operator) { case "===": case "!==": @@ -2735,6 +2738,7 @@ merge(Compressor.prototype, { var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ]; OPT(AST_Assign, function(self, compressor){ self = self.lift_sequences(compressor); + if (!(self instanceof AST_Assign)) return self; if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) { // x = expr1 OP expr2 if (self.right.left instanceof AST_SymbolRef @@ -2757,12 +2761,16 @@ merge(Compressor.prototype, { }); OPT(AST_Conditional, function(self, compressor){ - if (!compressor.option("conditionals")) return self; - if (self.condition instanceof AST_Seq) { - var car = self.condition.car; - self.condition = self.condition.cdr; - return AST_Seq.cons(car, self); + if (compressor.option("sequences")) { + var seq = self.condition; + if (seq instanceof AST_Seq) { + var p = seq.tail(); + self.condition = p.cdr; + p.cdr = self.transform(compressor); + return seq; + } } + if (!compressor.option("conditionals")) return self; var cond = self.condition.evaluate(compressor); if (cond.length > 1) { if (cond[1]) { @@ -2844,7 +2852,7 @@ merge(Compressor.prototype, { && consequent.equivalent_to(alternative)) { var consequent_value = consequent.evaluate(compressor)[0]; if (self.condition.has_side_effects(compressor)) { - return AST_Seq.from_array([self.condition, consequent_value]); + return AST_Seq.cons(self.condition, consequent_value); } else { return consequent_value; } diff --git a/lib/output.js b/lib/output.js index 50e5aa43..c3374a0f 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1082,14 +1082,12 @@ function OutputStream(options) { AST_Seq.DEFMETHOD("_do_print", function(output){ this.car.print(output); - if (this.cdr) { - output.comma(); - if (output.should_break()) { - output.newline(); - output.indent(); - } - this.cdr.print(output); + output.comma(); + if (output.should_break()) { + output.newline(); + output.indent(); } + this.cdr.print(output); }); DEFPRINT(AST_Seq, function(self, output){ self._do_print(output); diff --git a/test/compress/typeof.js b/test/compress/typeof.js index fb391573..cc281011 100644 --- a/test/compress/typeof.js +++ b/test/compress/typeof.js @@ -29,6 +29,7 @@ typeof_in_boolean_context: { booleans : true, evaluate : true, conditionals : true, + sequences : true, }; input: { function f1(x) { return typeof x ? "yes" : "no"; } @@ -40,8 +41,7 @@ typeof_in_boolean_context: { expect: { function f1(x) { return "yes"; } function f2() { return g(), "Yes"; } - foo(); - !(console.log(1), !0); - var a = !(console.log(2), !0); + foo(), console.log(1), !1; + var a = (console.log(2), !1); } }