[Pkg-javascript-commits] [uglifyjs] 01/491: convert `AST_Seq` from binary tree to array (#1460)

Jonas Smedegaard dr at jones.dk
Wed Feb 14 19:51:16 UTC 2018


This is an automated email from the git hooks/post-receive script.

js pushed a commit to annotated tag debian/3.3.10-1
in repository uglifyjs.

commit 2244743545e8e5a75b4cce219605588cd29581b1
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Wed Apr 12 21:56:27 2017 +0800

    convert `AST_Seq` from binary tree to array (#1460)
    
    - rename `AST_Seq` to `AST_Sequence`
    - raise default sequences_limit from 200 to 800
---
 bin/extract-props.js          |   4 +-
 bin/uglifyjs                  |   4 +-
 lib/ast.js                    |  64 +------
 lib/compress.js               | 375 +++++++++++++++++++++++-------------------
 lib/mozilla-ast.js            |  10 +-
 lib/output.js                 |  23 +--
 lib/parse.js                  |  19 ++-
 lib/propmangle.js             |   9 +-
 lib/transform.js              |   5 +-
 lib/utils.js                  |   2 +-
 test/compress/conditionals.js |  24 +--
 test/compress/evaluate.js     |  24 +--
 test/compress/issue-640.js    |   6 +-
 test/compress/sequences.js    | 118 ++++++++++---
 14 files changed, 378 insertions(+), 309 deletions(-)

diff --git a/bin/extract-props.js b/bin/extract-props.js
index a5b6145..7ce7d31 100755
--- a/bin/extract-props.js
+++ b/bin/extract-props.js
@@ -50,8 +50,8 @@ function getProps(filename) {
         try {
             (function walk(node){
                 node.walk(new U2.TreeWalker(function(node){
-                    if (node instanceof U2.AST_Seq) {
-                        walk(node.cdr);
+                    if (node instanceof U2.AST_Sequence) {
+                        walk(node.expressions[node.expressions.length - 1]);
                         return true;
                     }
                     if (node instanceof U2.AST_String) {
diff --git a/bin/uglifyjs b/bin/uglifyjs
index 635ca36..ef77649 100755
--- a/bin/uglifyjs
+++ b/bin/uglifyjs
@@ -569,7 +569,7 @@ function getOptions(flag, constants) {
             }
         }
         ast.walk(new UglifyJS.TreeWalker(function(node){
-            if (node instanceof UglifyJS.AST_Seq) return; // descend
+            if (node instanceof UglifyJS.AST_Sequence) return; // descend
             if (node instanceof UglifyJS.AST_Assign) {
                 var name = node.left.print_to_string().replace(/-/g, "_");
                 var value = node.right;
@@ -583,7 +583,7 @@ function getOptions(flag, constants) {
                 ret[name] = true;
                 return true;    // no descend
             }
-            print_error(node.TYPE)
+            print_error(node.TYPE);
             print_error("Error parsing arguments for flag `" + flag + "': " + x);
             process.exit(1);
         }));
diff --git a/lib/ast.js b/lib/ast.js
index ba1330f..f78ac57 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -613,68 +613,16 @@ var AST_New = DEFNODE("New", null, {
     $documentation: "An object instantiation.  Derives from a function call since it has exactly the same properties"
 }, AST_Call);
 
-var AST_Seq = DEFNODE("Seq", "car cdr", {
-    $documentation: "A sequence expression (two comma-separated expressions)",
+var AST_Sequence = DEFNODE("Sequence", "expressions", {
+    $documentation: "A sequence expression (comma-separated expressions)",
     $propdoc: {
-        car: "[AST_Node] first element in sequence",
-        cdr: "[AST_Node] second element in sequence"
-    },
-    $cons: function(x, y) {
-        var seq = new AST_Seq(x);
-        seq.car = x;
-        seq.cdr = y;
-        return seq;
-    },
-    $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;) {
-            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) {
-            a.push(p.car);
-            if (p.cdr && !(p.cdr instanceof AST_Seq)) {
-                a.push(p.cdr);
-                break;
-            }
-            p = p.cdr;
-        }
-        return a;
-    },
-    add: function(node) {
-        var p = this;
-        while (p) {
-            if (!(p.cdr instanceof AST_Seq)) {
-                var cell = AST_Seq.cons(p.cdr, node);
-                return p.cdr = cell;
-            }
-            p = p.cdr;
-        }
-    },
-    len: function() {
-        if (this.cdr instanceof AST_Seq) {
-            return this.cdr.len() + 1;
-        } else {
-            return 2;
-        }
+        expressions: "[AST_Node*] array of expressions (at least two)"
     },
     _walk: function(visitor) {
         return visitor._visit(this, function(){
-            this.car._walk(visitor);
-            if (this.cdr) this.cdr._walk(visitor);
+            this.expressions.forEach(function(node) {
+                node._walk(visitor);
+            });
         });
     }
 });
diff --git a/lib/compress.js b/lib/compress.js
index 1d9258c..8c25457 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -111,7 +111,7 @@ function Compressor(options, false_by_default) {
         };
     }
     var sequences = this.options["sequences"];
-    this.sequences_limit = sequences == 1 ? 200 : sequences | 0;
+    this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
     this.warnings_produced = {};
 };
 
@@ -440,6 +440,13 @@ merge(Compressor.prototype, {
         return new ctor(props);
     };
 
+    function make_sequence(orig, expressions) {
+        if (expressions.length == 1) return expressions[0];
+        return make_node(AST_Sequence, orig, {
+            expressions: expressions
+        });
+    }
+
     function make_node_from_constant(val, orig) {
         switch (typeof val) {
           case "string":
@@ -482,16 +489,19 @@ merge(Compressor.prototype, {
         if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
             || parent instanceof AST_Call && parent.expression === orig
                 && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
-            return make_node(AST_Seq, orig, {
-                car: make_node(AST_Number, orig, {
-                    value: 0
-                }),
-                cdr: val
-            });
+            return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
         }
         return val;
     }
 
+    function merge_sequence(array, node) {
+        if (node instanceof AST_Sequence) {
+            array.push.apply(array, node.expressions);
+        } else {
+            array.push(node);
+        }
+    }
+
     function as_statement_array(thing) {
         if (thing === null) return [];
         if (thing instanceof AST_BlockStatement) return thing.body;
@@ -1000,18 +1010,17 @@ merge(Compressor.prototype, {
             if (statements.length < 2) return statements;
             var seq = [], ret = [];
             function push_seq() {
-                seq = AST_Seq.from_array(seq);
-                if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
-                    body: seq
-                }));
+                if (!seq.length) return;
+                var body = make_sequence(seq[0], seq);
+                ret.push(make_node(AST_SimpleStatement, body, { body: body }));
                 seq = [];
             };
             statements.forEach(function(stat){
                 if (stat instanceof AST_SimpleStatement) {
-                    if (seqLength(seq) >= compressor.sequences_limit) push_seq();
+                    if (seq.length >= compressor.sequences_limit) push_seq();
                     var body = stat.body;
                     if (seq.length > 0) body = body.drop_side_effect_free(compressor);
-                    if (body) seq.push(body);
+                    if (body) merge_sequence(seq, body);
                 } else {
                     push_seq();
                     ret.push(stat);
@@ -1023,27 +1032,16 @@ merge(Compressor.prototype, {
             return ret;
         };
 
-        function seqLength(a) {
-            for (var len = 0, i = 0; i < a.length; ++i) {
-                var stat = a[i];
-                if (stat instanceof AST_Seq) {
-                    len += stat.len();
-                } else {
-                    len++;
-                }
-            }
-            return len;
-        };
-
         function sequencesize_2(statements, compressor) {
             function cons_seq(right) {
                 ret.pop();
                 var left = prev.body;
-                if (left instanceof AST_Seq) {
-                    left.add(right);
-                } else {
-                    left = AST_Seq.cons(left, right);
+                if (!(left instanceof AST_Sequence)) {
+                    left = make_node(AST_Sequence, left, {
+                        expressions: [ left ]
+                    });
                 }
+                merge_sequence(left.expressions, right);
                 return left.transform(compressor);
             };
             var ret = [], prev = null;
@@ -1202,8 +1200,8 @@ merge(Compressor.prototype, {
             return this.consequent._eq_null(pure_getters)
                 || this.alternative._eq_null(pure_getters);
         })
-        def(AST_Seq, function(pure_getters) {
-            return this.cdr._eq_null(pure_getters);
+        def(AST_Sequence, function(pure_getters) {
+            return this.expressions[this.expressions.length - 1]._eq_null(pure_getters);
         });
         def(AST_SymbolRef, function(pure_getters) {
             if (this.is_undefined) return true;
@@ -1236,8 +1234,8 @@ merge(Compressor.prototype, {
         def(AST_Assign, function(){
             return this.operator == "=" && this.right.is_boolean();
         });
-        def(AST_Seq, function(){
-            return this.cdr.is_boolean();
+        def(AST_Sequence, function(){
+            return this.expressions[this.expressions.length - 1].is_boolean();
         });
         def(AST_True, return_true);
         def(AST_False, return_true);
@@ -1263,8 +1261,8 @@ merge(Compressor.prototype, {
             return binary(this.operator.slice(0, -1))
                 || this.operator == "=" && this.right.is_number(compressor);
         });
-        def(AST_Seq, function(compressor){
-            return this.cdr.is_number(compressor);
+        def(AST_Sequence, function(compressor){
+            return this.expressions[this.expressions.length - 1].is_number(compressor);
         });
         def(AST_Conditional, function(compressor){
             return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
@@ -1287,8 +1285,8 @@ merge(Compressor.prototype, {
         def(AST_Assign, function(compressor){
             return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
         });
-        def(AST_Seq, function(compressor){
-            return this.cdr.is_string(compressor);
+        def(AST_Sequence, function(compressor){
+            return this.expressions[this.expressions.length - 1].is_string(compressor);
         });
         def(AST_Conditional, function(compressor){
             return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
@@ -1616,10 +1614,10 @@ merge(Compressor.prototype, {
                 return this.expression;
             return basic_negation(this);
         });
-        def(AST_Seq, function(compressor){
-            var self = this.clone();
-            self.cdr = self.cdr.negate(compressor);
-            return self;
+        def(AST_Sequence, function(compressor){
+            var expressions = this.expressions.slice();
+            expressions.push(expressions.pop().negate(compressor));
+            return make_sequence(this, expressions);
         });
         def(AST_Conditional, function(compressor, first_in_statement){
             var self = this.clone();
@@ -1763,9 +1761,10 @@ merge(Compressor.prototype, {
                 || this.expression.has_side_effects(compressor)
                 || this.property.has_side_effects(compressor);
         });
-        def(AST_Seq, function(compressor){
-            return this.car.has_side_effects(compressor)
-                || this.cdr.has_side_effects(compressor);
+        def(AST_Sequence, function(compressor){
+            return this.expressions.some(function(expression, index) {
+                return expression.has_side_effects(compressor);
+            });
         });
     })(function(node, func){
         node.DEFMETHOD("has_side_effects", func);
@@ -2015,12 +2014,12 @@ merge(Compressor.prototype, {
                         for (var i = 0; i < def.length;) {
                             var x = def[i];
                             if (x._unused_side_effects) {
-                                side_effects.push(x._unused_side_effects);
+                                merge_sequence(side_effects, x._unused_side_effects);
                                 def.splice(i, 1);
                             } else {
                                 if (side_effects.length > 0) {
-                                    side_effects.push(x.value);
-                                    x.value = AST_Seq.from_array(side_effects);
+                                    merge_sequence(side_effects, x.value);
+                                    x.value = make_sequence(x.value, side_effects);
                                     side_effects = [];
                                 }
                                 ++i;
@@ -2029,7 +2028,7 @@ merge(Compressor.prototype, {
                         if (side_effects.length > 0) {
                             side_effects = make_node(AST_BlockStatement, node, {
                                 body: [ make_node(AST_SimpleStatement, node, {
-                                    body: AST_Seq.from_array(side_effects)
+                                    body: make_sequence(node, side_effects)
                                 }) ]
                             });
                         } else {
@@ -2179,8 +2178,8 @@ merge(Compressor.prototype, {
                                 self.body.splice(i, 1);
                                 continue;
                             }
-                            if (expr instanceof AST_Seq
-                                && (assign = expr.car) instanceof AST_Assign
+                            if (expr instanceof AST_Sequence
+                                && (assign = expr.expressions[0]) instanceof AST_Assign
                                 && assign.operator == "="
                                 && (sym = assign.left) instanceof AST_Symbol
                                 && vars.has(sym.name))
@@ -2190,7 +2189,7 @@ merge(Compressor.prototype, {
                                 def.value = assign.right;
                                 remove(defs, def);
                                 defs.push(def);
-                                self.body[i].body = expr.cdr;
+                                self.body[i].body = make_sequence(expr, expr.expressions.slice(1));
                                 continue;
                             }
                         }
@@ -2224,12 +2223,14 @@ merge(Compressor.prototype, {
         // if all elements were dropped. Note: original array may be
         // returned if nothing changed.
         function trim(nodes, compressor, first_in_statement) {
+            var len = nodes.length;
+            if (!len) return null;
             var ret = [], changed = false;
-            for (var i = 0, len = nodes.length; i < len; i++) {
+            for (var i = 0; i < len; i++) {
                 var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
                 changed |= node !== nodes[i];
                 if (node) {
-                    ret.push(node);
+                    merge_sequence(ret, node);
                     first_in_statement = false;
                 }
             }
@@ -2254,7 +2255,7 @@ merge(Compressor.prototype, {
                 this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
             }
             var args = trim(this.args, compressor, first_in_statement);
-            return args && AST_Seq.from_array(args);
+            return args && make_sequence(this, args);
         });
         def(AST_Function, return_null);
         def(AST_Binary, function(compressor, first_in_statement){
@@ -2270,10 +2271,7 @@ merge(Compressor.prototype, {
               default:
                 var left = this.left.drop_side_effect_free(compressor, first_in_statement);
                 if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
-                return make_node(AST_Seq, this, {
-                    car: left,
-                    cdr: right
-                });
+                return make_sequence(this, [ left, right ]);
             }
         });
         def(AST_Assign, return_this);
@@ -2316,14 +2314,14 @@ merge(Compressor.prototype, {
         });
         def(AST_Object, function(compressor, first_in_statement){
             var values = trim(this.properties, compressor, first_in_statement);
-            return values && AST_Seq.from_array(values);
+            return values && make_sequence(this, values);
         });
         def(AST_ObjectProperty, function(compressor, first_in_statement){
             return this.value.drop_side_effect_free(compressor, first_in_statement);
         });
         def(AST_Array, function(compressor, first_in_statement){
             var values = trim(this.elements, compressor, first_in_statement);
-            return values && AST_Seq.from_array(values);
+            return values && make_sequence(this, values);
         });
         def(AST_Dot, function(compressor, first_in_statement){
             if (this.expression.may_eq_null(compressor)) return this;
@@ -2335,19 +2333,15 @@ merge(Compressor.prototype, {
             if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
             var property = this.property.drop_side_effect_free(compressor);
             if (!property) return expression;
-            return make_node(AST_Seq, this, {
-                car: expression,
-                cdr: property
-            });
+            return make_sequence(this, [ expression, property ]);
         });
-        def(AST_Seq, function(compressor){
-            var cdr = this.cdr.drop_side_effect_free(compressor);
-            if (cdr === this.cdr) return this;
-            if (!cdr) return this.car;
-            return make_node(AST_Seq, this, {
-                car: this.car,
-                cdr: cdr
-            });
+        def(AST_Sequence, function(compressor){
+            var last = this.expressions[this.expressions.length - 1];
+            var expr = last.drop_side_effect_free(compressor);
+            if (expr === last) return this;
+            var expressions = this.expressions.slice(0, -1);
+            if (expr) merge_sequence(expressions, expr);
+            return make_sequence(this, expressions);
         });
     })(function(node, func){
         node.DEFMETHOD("drop_side_effect_free", func);
@@ -2737,7 +2731,7 @@ merge(Compressor.prototype, {
             return a;
         }, []);
         if (assignments.length == 0) return null;
-        return AST_Seq.from_array(assignments);
+        return make_sequence(this, assignments);
     });
 
     OPT(AST_Definitions, function(self, compressor){
@@ -2979,12 +2973,12 @@ merge(Compressor.prototype, {
                 var value = exp.body[0].value;
                 if (!value || value.is_constant()) {
                     var args = self.args.concat(value || make_node(AST_Undefined, self));
-                    return AST_Seq.from_array(args).transform(compressor);
+                    return make_sequence(self, args).transform(compressor);
                 }
             }
             if (compressor.option("side_effects") && all(exp.body, is_empty)) {
                 var args = self.args.concat(make_node(AST_Undefined, self));
-                return AST_Seq.from_array(args).transform(compressor);
+                return make_sequence(self, args).transform(compressor);
             }
         }
         if (compressor.option("drop_console")) {
@@ -3025,40 +3019,85 @@ merge(Compressor.prototype, {
         return self;
     });
 
-    OPT(AST_Seq, function(self, compressor){
-        if (!compressor.option("side_effects"))
+    OPT(AST_Sequence, function(self, compressor){
+        if (!compressor.option("side_effects")) return self;
+        var expressions = [];
+        filter_for_side_effects();
+        var end = expressions.length - 1;
+        trim_right_for_undefined();
+        if (end > 0 && compressor.option("cascade")) trim_left_for_assignment();
+        if (end == 0) {
+            self = maintain_this_binding(compressor.parent(), self, expressions[0]);
+            if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
             return self;
-        self.car = self.car.drop_side_effect_free(compressor, first_in_statement(compressor));
-        if (!self.car) return maintain_this_binding(compressor.parent(), self, self.cdr);
-        if (compressor.option("cascade")) {
-            var left;
-            if (self.car instanceof AST_Assign
-                && !self.car.left.has_side_effects(compressor)) {
-                left = self.car.left;
-            } else if (self.car instanceof AST_Unary
-                && (self.car.operator == "++" || self.car.operator == "--")) {
-                left = self.car.expression;
-            }
-            if (left
-                && !(left instanceof AST_SymbolRef
-                    && left.definition().orig[0] instanceof AST_SymbolLambda)) {
-                var parent, field;
-                var cdr = self.cdr;
+        }
+        self.expressions = expressions;
+        return self;
+
+        function filter_for_side_effects() {
+            var first = first_in_statement(compressor);
+            var last = self.expressions.length - 1;
+            self.expressions.forEach(function(expr, index) {
+                if (index < last) expr = expr.drop_side_effect_free(compressor, first);
+                if (expr) {
+                    merge_sequence(expressions, expr);
+                    first = false;
+                }
+            });
+        }
+
+        function trim_right_for_undefined() {
+            while (end > 0 && is_undefined(expressions[end], compressor)) end--;
+            if (end < expressions.length - 1) {
+                expressions[end] = make_node(AST_UnaryPrefix, self, {
+                    operator   : "void",
+                    expression : expressions[end]
+                });
+                expressions.length = end + 1;
+            }
+        }
+
+        function trim_left_for_assignment() {
+            for (var i = 0, j = 1; j <= end; j++) {
+                var left = expressions[i];
+                var cdr = expressions[j];
+                if (left instanceof AST_Assign
+                    && !left.left.has_side_effects(compressor)) {
+                    left = left.left;
+                } else if (left instanceof AST_Unary
+                    && (left.operator == "++" || left.operator == "--")) {
+                    left = left.expression;
+                } else left = null;
+                if (!left ||
+                    left instanceof AST_SymbolRef
+                        && left.definition().orig[0] instanceof AST_SymbolLambda) {
+                    expressions[++i] = cdr;
+                    continue;
+                }
+                var parent = null, field;
                 while (true) {
                     if (cdr.equivalent_to(left)) {
-                        var car = self.car instanceof AST_UnaryPostfix ? make_node(AST_UnaryPrefix, self.car, {
-                            operator: self.car.operator,
-                            expression: left
-                        }) : self.car;
+                        var car = expressions[i];
+                        if (car instanceof AST_UnaryPostfix) {
+                            car = make_node(AST_UnaryPrefix, car, {
+                                operator: car.operator,
+                                expression: left
+                            });
+                        }
                         if (parent) {
                             parent[field] = car;
-                            return self.cdr;
+                            expressions[i] = expressions[j];
+                        } else {
+                            expressions[i] = car;
                         }
-                        return car;
+                        break;
                     }
                     if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
                         if (cdr.left.is_constant()) {
-                            if (cdr.operator == "||" || cdr.operator == "&&") break;
+                            if (cdr.operator == "||" || cdr.operator == "&&") {
+                                expressions[++i] = expressions[j];
+                                break;
+                            }
                             field = "right";
                         } else {
                             field = "left";
@@ -3066,31 +3105,27 @@ merge(Compressor.prototype, {
                     } else if (cdr instanceof AST_Call
                         || cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
                         field = "expression";
-                    } else break;
+                    } else {
+                        expressions[++i] = expressions[j];
+                        break;
+                    }
                     parent = cdr;
                     cdr = cdr[field];
                 }
             }
+            end = i;
+            expressions.length = end + 1;
         }
-        if (is_undefined(self.cdr, compressor)) {
-            return make_node(AST_UnaryPrefix, self, {
-                operator   : "void",
-                expression : self.car
-            });
-        }
-        return self;
     });
 
     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();
+            if (this.expression instanceof AST_Sequence) {
+                var x = this.expression.expressions.slice();
                 var e = this.clone();
                 e.expression = x.pop();
                 x.push(e);
-                seq = AST_Seq.from_array(x).transform(compressor);
-                return seq;
+                return make_sequence(this, x).optimize(compressor);
             }
         }
         return this;
@@ -3108,15 +3143,12 @@ merge(Compressor.prototype, {
                 || e instanceof AST_NaN
                 || e instanceof AST_Infinity
                 || e instanceof AST_Undefined)) {
-            if (e instanceof AST_Seq) {
-                e = e.to_array();
+            if (e instanceof AST_Sequence) {
+                e = e.expressions.slice();
                 e.push(make_node(AST_True, self));
-                return AST_Seq.from_array(e).optimize(compressor);
+                return make_sequence(self, e).optimize(compressor);
             }
-            return make_node(AST_Seq, self, {
-                car: e,
-                cdr: make_node(AST_True, self)
-            }).optimize(compressor);
+            return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor);
         }
         var seq = self.lift_sequences(compressor);
         if (seq !== self) {
@@ -3146,10 +3178,10 @@ merge(Compressor.prototype, {
                 // typeof always returns a non-empty string, thus it's
                 // always true in booleans
                 compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
-                return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_node(AST_Seq, self, {
-                    car: e,
-                    cdr: make_node(AST_True, self)
-                })).optimize(compressor);
+                return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
+                    e,
+                    make_node(AST_True, self)
+                ])).optimize(compressor);
             }
         }
         if (self.operator == "-" && e instanceof AST_Infinity) {
@@ -3181,29 +3213,32 @@ 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();
+            if (this.left instanceof AST_Sequence) {
+                var x = this.left.expressions.slice();
                 var e = this.clone();
                 e.left = x.pop();
                 x.push(e);
-                return AST_Seq.from_array(x).optimize(compressor);
+                return make_sequence(this, x).optimize(compressor);
             }
-            if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
+            if (this.right instanceof AST_Sequence && !this.left.has_side_effects(compressor)) {
                 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
-                var root = this.right.clone();
-                var cursor, seq = root;
-                while (assign || !seq.car.has_side_effects(compressor)) {
-                    cursor = seq;
-                    if (seq.cdr instanceof AST_Seq) {
-                        seq = seq.cdr = seq.cdr.clone();
-                    } else break;
-                }
-                if (cursor) {
+                var x = this.right.expressions;
+                var last = x.length - 1;
+                for (var i = 0; i < last; i++) {
+                    if (!assign && x[i].has_side_effects(compressor)) break;
+                }
+                if (i == last) {
+                    x = x.slice();
                     var e = this.clone();
-                    e.right = cursor.cdr;
-                    cursor.cdr = e;
-                    return root.optimize(compressor);
+                    e.right = x.pop();
+                    x.push(e);
+                    return make_sequence(this, x).optimize(compressor);
+                } else if (i > 0) {
+                    var e = this.clone();
+                    e.right = make_sequence(this.right, x.slice(i));
+                    x = x.slice(0, i);
+                    x.push(e);
+                    return make_sequence(this, x).optimize(compressor);
                 }
             }
         }
@@ -3272,17 +3307,17 @@ merge(Compressor.prototype, {
             var rr = self.right.evaluate(compressor);
             if (ll && typeof ll == "string") {
                 compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
-                return make_node(AST_Seq, self, {
-                    car: self.right,
-                    cdr: make_node(AST_True, self)
-                }).optimize(compressor);
+                return make_sequence(self, [
+                    self.right,
+                    make_node(AST_True, self)
+                ]).optimize(compressor);
             }
             if (rr && typeof rr == "string") {
                 compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
-                return make_node(AST_Seq, self, {
-                    car: self.left,
-                    cdr: make_node(AST_True, self)
-                }).optimize(compressor);
+                return make_sequence(self, [
+                    self.left,
+                    make_node(AST_True, self)
+                ]).optimize(compressor);
             }
         }
         if (compressor.option("comparisons") && self.is_boolean()) {
@@ -3336,10 +3371,10 @@ merge(Compressor.prototype, {
                     var rr = self.right.evaluate(compressor);
                     if (!rr) {
                         compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
-                        return make_node(AST_Seq, self, {
-                            car: self.left,
-                            cdr: make_node(AST_False, self)
-                        }).optimize(compressor);
+                        return make_sequence(self, [
+                            self.left,
+                            make_node(AST_False, self)
+                        ]).optimize(compressor);
                     } else if (rr !== self.right) {
                         compressor.warn("Dropping side-effect-free && in boolean context [{file}:{line},{col}]", self.start);
                         return self.left.optimize(compressor);
@@ -3362,10 +3397,10 @@ merge(Compressor.prototype, {
                         return self.left.optimize(compressor);
                     } else if (rr !== self.right) {
                         compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
-                        return make_node(AST_Seq, self, {
-                            car: self.left,
-                            cdr: make_node(AST_True, self)
-                        }).optimize(compressor);
+                        return make_sequence(self, [
+                            self.left,
+                            make_node(AST_True, self)
+                        ]).optimize(compressor);
                     }
                 }
                 break;
@@ -3709,10 +3744,12 @@ 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);
+        // This looks like lift_sequences(), should probably be under "sequences"
+        if (self.condition instanceof AST_Sequence) {
+            var expressions = self.condition.expressions.slice();
+            self.condition = expressions.pop();
+            expressions.push(self);
+            return make_sequence(self, expressions);
         }
         var cond = self.condition.evaluate(compressor);
         if (cond !== self.condition) {
@@ -3795,10 +3832,10 @@ merge(Compressor.prototype, {
         }
         // x ? y : y --> x, y
         if (consequent.equivalent_to(alternative)) {
-            return make_node(AST_Seq, self, {
-                car: self.condition,
-                cdr: consequent
-            }).optimize(compressor);
+            return make_sequence(self, [
+                self.condition,
+                consequent
+            ]).optimize(compressor);
         }
 
         if (is_true(self.consequent)) {
@@ -3968,10 +4005,10 @@ merge(Compressor.prototype, {
 
     function literals_in_boolean_context(self, compressor) {
         if (compressor.option("booleans") && compressor.in_boolean_context()) {
-            return best_of(compressor, self, make_node(AST_Seq, self, {
-                car: self,
-                cdr: make_node(AST_True, self)
-            }).optimize(compressor));
+            return best_of(compressor, self, make_sequence(self, [
+                self,
+                make_node(AST_True, self)
+            ]).optimize(compressor));
         }
         return self;
     };
diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js
index 12b55dc..e97d619 100644
--- a/lib/mozilla-ast.js
+++ b/lib/mozilla-ast.js
@@ -149,7 +149,11 @@
             });
         },
         SequenceExpression: function(M) {
-            return AST_Seq.from_array(M.expressions.map(from_moz));
+            return new AST_Sequence({
+                start      : my_start_token(M),
+                end        : my_end_token(M),
+                expressions: M.expressions.map(from_moz)
+            });
         },
         MemberExpression: function(M) {
             return new (M.computed ? AST_Sub : AST_Dot)({
@@ -332,10 +336,10 @@
         };
     });
 
-    def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
+    def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) {
         return {
             type: "SequenceExpression",
-            expressions: M.to_array().map(to_moz)
+            expressions: M.expressions.map(to_moz)
         };
     });
 
diff --git a/lib/output.js b/lib/output.js
index 9ac50c0..fe982a7 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -592,7 +592,7 @@ function OutputStream(options) {
             || p instanceof AST_Call && p.expression === this;
     });
 
-    PARENS(AST_Seq, function(output){
+    PARENS(AST_Sequence, function(output){
         var p = output.parent();
         return p instanceof AST_Call             // (foo, bar)() or foo(1, (2, 3), 4)
             || p instanceof AST_Unary            // !(foo, bar, baz)
@@ -1087,18 +1087,19 @@ function OutputStream(options) {
         AST_Call.prototype._codegen(self, output);
     });
 
-    AST_Seq.DEFMETHOD("_do_print", function(output){
-        this.car.print(output);
-        if (this.cdr) {
-            output.comma();
-            if (output.should_break()) {
-                output.newline();
-                output.indent();
+    AST_Sequence.DEFMETHOD("_do_print", function(output){
+        this.expressions.forEach(function(node, index) {
+            if (index > 0) {
+                output.comma();
+                if (output.should_break()) {
+                    output.newline();
+                    output.indent();
+                }
             }
-            this.cdr.print(output);
-        }
+            node.print(output);
+        });
     });
-    DEFPRINT(AST_Seq, function(self, output){
+    DEFPRINT(AST_Sequence, function(self, output){
         self._do_print(output);
         // var p = output.parent();
         // if (p instanceof AST_Statement) {
diff --git a/lib/parse.js b/lib/parse.js
index c34e13d..c7d7580 100644
--- a/lib/parse.js
+++ b/lib/parse.js
@@ -1527,17 +1527,18 @@ function parse($TEXT, options) {
 
     var expression = function(commas, no_in) {
         var start = S.token;
-        var expr = maybe_assign(no_in);
-        if (commas && is("punc", ",")) {
+        var exprs = [];
+        while (true) {
+            exprs.push(maybe_assign(no_in));
+            if (!commas || !is("punc", ",")) break;
             next();
-            return new AST_Seq({
-                start  : start,
-                car    : expr,
-                cdr    : expression(true, no_in),
-                end    : peek()
-            });
+            commas = true;
         }
-        return expr;
+        return exprs.length == 1 ? exprs[0] : new AST_Sequence({
+            start       : start,
+            expressions : exprs,
+            end         : peek()
+        });
     };
 
     function in_loop(cont) {
diff --git a/lib/propmangle.js b/lib/propmangle.js
index b622299..aaf5936 100644
--- a/lib/propmangle.js
+++ b/lib/propmangle.js
@@ -224,8 +224,8 @@ function mangle_properties(ast, options) {
         try {
             (function walk(node){
                 node.walk(new TreeWalker(function(node){
-                    if (node instanceof AST_Seq) {
-                        walk(node.cdr);
+                    if (node instanceof AST_Sequence) {
+                        walk(node.expressions[node.expressions.length - 1]);
                         return true;
                     }
                     if (node instanceof AST_String) {
@@ -247,8 +247,9 @@ function mangle_properties(ast, options) {
 
     function mangleStrings(node) {
         return node.transform(new TreeTransformer(function(node){
-            if (node instanceof AST_Seq) {
-                node.cdr = mangleStrings(node.cdr);
+            if (node instanceof AST_Sequence) {
+                var last = node.expressions.length - 1;
+                node.expressions[last] = mangleStrings(node.expressions[last]);
             }
             else if (node instanceof AST_String) {
                 node.value = mangle(node.value);
diff --git a/lib/transform.js b/lib/transform.js
index 3018e8f..112e5f2 100644
--- a/lib/transform.js
+++ b/lib/transform.js
@@ -174,9 +174,8 @@ TreeTransformer.prototype = new TreeWalker;
         self.args = do_list(self.args, tw);
     });
 
-    _(AST_Seq, function(self, tw){
-        self.car = self.car.transform(tw);
-        self.cdr = self.cdr.transform(tw);
+    _(AST_Sequence, function(self, tw){
+        self.expressions = do_list(self.expressions, tw);
     });
 
     _(AST_Dot, function(self, tw){
diff --git a/lib/utils.js b/lib/utils.js
index fdb2047..e21fc5e 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -346,7 +346,7 @@ function first_in_statement(stack) {
     for (var i = 0, p; p = stack.parent(i); i++) {
         if (p instanceof AST_Statement && p.body === node)
             return true;
-        if ((p instanceof AST_Seq           && p.car === node        ) ||
+        if ((p instanceof AST_Sequence      && p.expressions[0] === node) ||
             (p instanceof AST_Call          && p.expression === node && !(p instanceof AST_New) ) ||
             (p instanceof AST_Dot           && p.expression === node ) ||
             (p instanceof AST_Sub           && p.expression === node ) ||
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
index 200b487..7a6688b 100644
--- a/test/compress/conditionals.js
+++ b/test/compress/conditionals.js
@@ -979,12 +979,12 @@ delete_conditional_1: {
         console.log(delete (1 ? 0 / 0 : x));
     }
     expect: {
-        console.log((void 0, !0));
-        console.log((void 0, !0));
-        console.log((1 / 0, !0));
-        console.log((1 / 0, !0));
-        console.log((NaN, !0));
-        console.log((NaN, !0));
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
     }
     expect_stdout: true
 }
@@ -1006,12 +1006,12 @@ delete_conditional_2: {
         console.log(delete (0 ? x : 0 / 0));
     }
     expect: {
-        console.log((void 0, !0));
-        console.log((void 0, !0));
-        console.log((Infinity, !0));
-        console.log((1 / 0, !0));
-        console.log((NaN, !0));
-        console.log((NaN, !0));
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
     }
     expect_stdout: true
 }
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index 611acf0..585ee2b 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -922,12 +922,12 @@ delete_binary_1: {
         console.log(delete (true && (0 / 0)));
     }
     expect: {
-        console.log((void 0, !0));
-        console.log((void 0, !0));
-        console.log((1 / 0, !0));
-        console.log((1 / 0, !0));
-        console.log((NaN, !0));
-        console.log((NaN, !0));
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
     }
     expect_stdout: true
 }
@@ -948,12 +948,12 @@ delete_binary_2: {
         console.log(delete (false || (0 / 0)));
     }
     expect: {
-        console.log((void 0, !0));
-        console.log((void 0, !0));
-        console.log((Infinity, !0));
-        console.log((1 / 0, !0));
-        console.log((NaN, !0));
-        console.log((NaN, !0));
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
     }
     expect_stdout: true
 }
diff --git a/test/compress/issue-640.js b/test/compress/issue-640.js
index fbf5f37..c9a68dc 100644
--- a/test/compress/issue-640.js
+++ b/test/compress/issue-640.js
@@ -159,7 +159,7 @@ negate_iife_4: {
         })();
     }
     expect: {
-        (function(){ return t })() ? console.log(true) : console.log(false), function(){
+        !function(){ return t }() ? console.log(false) : console.log(true), function(){
             console.log("something");
         }();
     }
@@ -183,7 +183,7 @@ negate_iife_5: {
         })();
     }
     expect: {
-        (function(){ return t })() ? foo(true) : bar(false), function(){
+        !function(){ return t }() ? bar(false) : foo(true), function(){
             console.log("something");
         }();
     }
@@ -207,7 +207,7 @@ negate_iife_5_off: {
         })();
     }
     expect: {
-        (function(){ return t })() ? foo(true) : bar(false), function(){
+        !function(){ return t }() ? bar(false) : foo(true), function(){
             console.log("something");
         }();
     }
diff --git a/test/compress/sequences.js b/test/compress/sequences.js
index 699341c..3fb2627 100644
--- a/test/compress/sequences.js
+++ b/test/compress/sequences.js
@@ -460,7 +460,7 @@ issue_1758: {
         console.log(function(c) {
             var undefined = 42;
             return function() {
-                return c--, c--, c.toString(), void 0;
+                return c--, c--, void c.toString();
             }();
         }());
     }
@@ -481,12 +481,12 @@ delete_seq_1: {
         console.log(delete (1, 0 / 0));
     }
     expect: {
-        console.log((void 0, !0));
-        console.log((void 0, !0));
-        console.log((1 / 0, !0));
-        console.log((1 / 0, !0));
-        console.log((NaN, !0));
-        console.log((0 / 0, !0));
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
     }
     expect_stdout: true
 }
@@ -505,12 +505,12 @@ delete_seq_2: {
         console.log(delete (1, 2, 0 / 0));
     }
     expect: {
-        console.log((void 0, !0));
-        console.log((void 0, !0));
-        console.log((1 / 0, !0));
-        console.log((1 / 0, !0));
-        console.log((NaN, !0));
-        console.log((0 / 0, !0));
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
     }
     expect_stdout: true
 }
@@ -530,12 +530,12 @@ delete_seq_3: {
         console.log(delete (1, 2, 0 / 0));
     }
     expect: {
-        console.log((void 0, !0));
-        console.log((void 0, !0));
-        console.log((Infinity, !0));
-        console.log((1 / 0, !0));
-        console.log((NaN, !0));
-        console.log((0 / 0, !0));
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
+        console.log(!0);
     }
     expect_stdout: true
 }
@@ -606,7 +606,85 @@ delete_seq_6: {
     }
     expect: {
         var a;
-        console.log((a, !0));
+        console.log(!0);
     }
     expect_stdout: true
 }
+
+side_effects: {
+    options = {
+        sequences: true,
+        side_effects: true,
+    }
+    input: {
+        0, a(), 1, b(), 2, c(), 3;
+    }
+    expect: {
+        a(), b(), c();
+    }
+}
+
+side_effects_cascade_1: {
+    options = {
+        cascade: true,
+        conditionals: true,
+        sequences: true,
+        side_effects: true,
+    }
+    input: {
+        function f(a, b) {
+            a -= 42;
+            if (a < 0) a = 0;
+            b.a = a;
+        }
+    }
+    expect: {
+        function f(a, b) {
+            (a -= 42) < 0 && (a = 0), b.a = a;
+        }
+    }
+}
+
+side_effects_cascade_2: {
+    options = {
+        cascade: true,
+        side_effects: true,
+    }
+    input: {
+        function f(a, b) {
+            b = a,
+            !a + (b += a) || (b += a),
+            b = a,
+            b;
+        }
+    }
+    expect: {
+        function f(a, b) {
+            b = a,
+            !a + (b += a) || (b += a),
+            b = a;
+        }
+    }
+}
+
+side_effects_cascade_3: {
+    options = {
+        cascade: true,
+        conditionals: true,
+        side_effects: true,
+    }
+    input: {
+        function f(a, b) {
+            "foo" ^ (b += a),
+            b ? false : (b = a) ? -1 : (b -= a) - (b ^= a),
+            a-- || !a,
+            a;
+        }
+    }
+    expect: {
+        function f(a, b) {
+            !(b += a) && ((b = a) || (b -= a, b ^= a)),
+            --a;
+        }
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/uglifyjs.git



More information about the Pkg-javascript-commits mailing list