uglifier 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of uglifier might be problematic. Click here for more details.

@@ -69,139 +69,135 @@ var jsp = require("./parse-js"),
69
69
 
70
70
  function ast_walker(ast) {
71
71
  function _vardefs(defs) {
72
- return MAP(defs, function(def){
72
+ return [ this[0], MAP(defs, function(def){
73
73
  var a = [ def[0] ];
74
74
  if (def.length > 1)
75
75
  a[1] = walk(def[1]);
76
76
  return a;
77
- });
77
+ }) ];
78
78
  };
79
79
  var walkers = {
80
80
  "string": function(str) {
81
- return [ "string", str ];
81
+ return [ this[0], str ];
82
82
  },
83
83
  "num": function(num) {
84
- return [ "num", num ];
84
+ return [ this[0], num ];
85
85
  },
86
86
  "name": function(name) {
87
- return [ "name", name ];
87
+ return [ this[0], name ];
88
88
  },
89
89
  "toplevel": function(statements) {
90
- return [ "toplevel", MAP(statements, walk) ];
90
+ return [ this[0], MAP(statements, walk) ];
91
91
  },
92
92
  "block": function(statements) {
93
- var out = [ "block" ];
93
+ var out = [ this[0] ];
94
94
  if (statements != null)
95
95
  out.push(MAP(statements, walk));
96
96
  return out;
97
97
  },
98
- "var": function(defs) {
99
- return [ "var", _vardefs(defs) ];
100
- },
101
- "const": function(defs) {
102
- return [ "const", _vardefs(defs) ];
103
- },
98
+ "var": _vardefs,
99
+ "const": _vardefs,
104
100
  "try": function(t, c, f) {
105
101
  return [
106
- "try",
102
+ this[0],
107
103
  MAP(t, walk),
108
104
  c != null ? [ c[0], MAP(c[1], walk) ] : null,
109
105
  f != null ? MAP(f, walk) : null
110
106
  ];
111
107
  },
112
108
  "throw": function(expr) {
113
- return [ "throw", walk(expr) ];
109
+ return [ this[0], walk(expr) ];
114
110
  },
115
111
  "new": function(ctor, args) {
116
- return [ "new", walk(ctor), MAP(args, walk) ];
112
+ return [ this[0], walk(ctor), MAP(args, walk) ];
117
113
  },
118
114
  "switch": function(expr, body) {
119
- return [ "switch", walk(expr), MAP(body, function(branch){
115
+ return [ this[0], walk(expr), MAP(body, function(branch){
120
116
  return [ branch[0] ? walk(branch[0]) : null,
121
117
  MAP(branch[1], walk) ];
122
118
  }) ];
123
119
  },
124
120
  "break": function(label) {
125
- return [ "break", label ];
121
+ return [ this[0], label ];
126
122
  },
127
123
  "continue": function(label) {
128
- return [ "continue", label ];
124
+ return [ this[0], label ];
129
125
  },
130
126
  "conditional": function(cond, t, e) {
131
- return [ "conditional", walk(cond), walk(t), walk(e) ];
127
+ return [ this[0], walk(cond), walk(t), walk(e) ];
132
128
  },
133
129
  "assign": function(op, lvalue, rvalue) {
134
- return [ "assign", op, walk(lvalue), walk(rvalue) ];
130
+ return [ this[0], op, walk(lvalue), walk(rvalue) ];
135
131
  },
136
132
  "dot": function(expr) {
137
- return [ "dot", walk(expr) ].concat(slice(arguments, 1));
133
+ return [ this[0], walk(expr) ].concat(slice(arguments, 1));
138
134
  },
139
135
  "call": function(expr, args) {
140
- return [ "call", walk(expr), MAP(args, walk) ];
136
+ return [ this[0], walk(expr), MAP(args, walk) ];
141
137
  },
142
138
  "function": function(name, args, body) {
143
- return [ "function", name, args.slice(), MAP(body, walk) ];
139
+ return [ this[0], name, args.slice(), MAP(body, walk) ];
144
140
  },
145
141
  "defun": function(name, args, body) {
146
- return [ "defun", name, args.slice(), MAP(body, walk) ];
142
+ return [ this[0], name, args.slice(), MAP(body, walk) ];
147
143
  },
148
144
  "if": function(conditional, t, e) {
149
- return [ "if", walk(conditional), walk(t), walk(e) ];
145
+ return [ this[0], walk(conditional), walk(t), walk(e) ];
150
146
  },
151
147
  "for": function(init, cond, step, block) {
152
- return [ "for", walk(init), walk(cond), walk(step), walk(block) ];
148
+ return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
153
149
  },
154
- "for-in": function(has_var, key, hash, block) {
155
- return [ "for-in", has_var, key, walk(hash), walk(block) ];
150
+ "for-in": function(vvar, key, hash, block) {
151
+ return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
156
152
  },
157
153
  "while": function(cond, block) {
158
- return [ "while", walk(cond), walk(block) ];
154
+ return [ this[0], walk(cond), walk(block) ];
159
155
  },
160
156
  "do": function(cond, block) {
161
- return [ "do", walk(cond), walk(block) ];
157
+ return [ this[0], walk(cond), walk(block) ];
162
158
  },
163
159
  "return": function(expr) {
164
- return [ "return", walk(expr) ];
160
+ return [ this[0], walk(expr) ];
165
161
  },
166
162
  "binary": function(op, left, right) {
167
- return [ "binary", op, walk(left), walk(right) ];
163
+ return [ this[0], op, walk(left), walk(right) ];
168
164
  },
169
165
  "unary-prefix": function(op, expr) {
170
- return [ "unary-prefix", op, walk(expr) ];
166
+ return [ this[0], op, walk(expr) ];
171
167
  },
172
168
  "unary-postfix": function(op, expr) {
173
- return [ "unary-postfix", op, walk(expr) ];
169
+ return [ this[0], op, walk(expr) ];
174
170
  },
175
171
  "sub": function(expr, subscript) {
176
- return [ "sub", walk(expr), walk(subscript) ];
172
+ return [ this[0], walk(expr), walk(subscript) ];
177
173
  },
178
174
  "object": function(props) {
179
- return [ "object", MAP(props, function(p){
175
+ return [ this[0], MAP(props, function(p){
180
176
  return p.length == 2
181
177
  ? [ p[0], walk(p[1]) ]
182
178
  : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
183
179
  }) ];
184
180
  },
185
181
  "regexp": function(rx, mods) {
186
- return [ "regexp", rx, mods ];
182
+ return [ this[0], rx, mods ];
187
183
  },
188
184
  "array": function(elements) {
189
- return [ "array", MAP(elements, walk) ];
185
+ return [ this[0], MAP(elements, walk) ];
190
186
  },
191
187
  "stat": function(stat) {
192
- return [ "stat", walk(stat) ];
188
+ return [ this[0], walk(stat) ];
193
189
  },
194
190
  "seq": function() {
195
- return [ "seq" ].concat(MAP(slice(arguments), walk));
191
+ return [ this[0] ].concat(MAP(slice(arguments), walk));
196
192
  },
197
193
  "label": function(name, block) {
198
- return [ "label", name, walk(block) ];
194
+ return [ this[0], name, walk(block) ];
199
195
  },
200
196
  "with": function(expr, block) {
201
- return [ "with", walk(expr), walk(block) ];
197
+ return [ this[0], walk(expr), walk(block) ];
202
198
  },
203
199
  "atom": function(name) {
204
- return [ "atom", name ];
200
+ return [ this[0], name ];
205
201
  }
206
202
  };
207
203
 
@@ -405,7 +401,7 @@ function ast_add_scope(ast) {
405
401
  },
406
402
  "try": function(t, c, f) {
407
403
  if (c != null) return [
408
- "try",
404
+ this[0],
409
405
  MAP(t, walk),
410
406
  [ define(c[0]), MAP(c[1], walk) ],
411
407
  f != null ? MAP(f, walk) : null
@@ -415,10 +411,6 @@ function ast_add_scope(ast) {
415
411
  if (name == "eval")
416
412
  having_eval.push(current_scope);
417
413
  reference(name);
418
- },
419
- "for-in": function(has_var, name) {
420
- if (has_var) define(name);
421
- else reference(name);
422
414
  }
423
415
  }, function(){
424
416
  return walk(ast);
@@ -461,11 +453,14 @@ function ast_add_scope(ast) {
461
453
 
462
454
  /* -----[ mangle names ]----- */
463
455
 
464
- function ast_mangle(ast, do_toplevel) {
456
+ function ast_mangle(ast, options) {
465
457
  var w = ast_walker(), walk = w.walk, scope;
458
+ options = options || {};
466
459
 
467
460
  function get_mangled(name, newMangle) {
468
- if (!do_toplevel && !scope.parent) return name; // don't mangle toplevel
461
+ if (!options.toplevel && !scope.parent) return name; // don't mangle toplevel
462
+ if (options.except && member(name, options.except))
463
+ return name;
469
464
  return scope.get_mangled(name, newMangle);
470
465
  };
471
466
 
@@ -491,9 +486,9 @@ function ast_mangle(ast, do_toplevel) {
491
486
  };
492
487
 
493
488
  function _vardefs(defs) {
494
- return MAP(defs, function(d){
489
+ return [ this[0], MAP(defs, function(d){
495
490
  return [ get_mangled(d[0]), walk(d[1]) ];
496
- });
491
+ }) ];
497
492
  };
498
493
 
499
494
  return w.with_walkers({
@@ -510,28 +505,22 @@ function ast_mangle(ast, do_toplevel) {
510
505
  }
511
506
  return ast;
512
507
  },
513
- "var": function(defs) {
514
- return [ "var", _vardefs(defs) ];
515
- },
516
- "const": function(defs) {
517
- return [ "const", _vardefs(defs) ];
518
- },
508
+ "var": _vardefs,
509
+ "const": _vardefs,
519
510
  "name": function(name) {
520
- return [ "name", get_mangled(name) ];
511
+ return [ this[0], get_mangled(name) ];
521
512
  },
522
513
  "try": function(t, c, f) {
523
- return [ "try",
514
+ return [ this[0],
524
515
  MAP(t, walk),
525
516
  c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
526
517
  f != null ? MAP(f, walk) : null ];
527
518
  },
528
519
  "toplevel": function(body) {
529
- return with_scope(this.scope, function(){
530
- return [ "toplevel", MAP(body, walk) ];
520
+ var self = this;
521
+ return with_scope(self.scope, function(){
522
+ return [ self[0], MAP(body, walk) ];
531
523
  });
532
- },
533
- "for-in": function(has_var, name, obj, stat) {
534
- return [ "for-in", has_var, get_mangled(name), walk(obj), walk(stat) ];
535
524
  }
536
525
  }, function() {
537
526
  return walk(ast_add_scope(ast));
@@ -569,28 +558,29 @@ function aborts(t) {
569
558
  }
570
559
  };
571
560
 
572
- function negate(c) {
573
- var not_c = [ "unary-prefix", "!", c ];
574
- switch (c[0]) {
575
- case "unary-prefix":
576
- return c[1] == "!" ? c[2] : not_c;
577
- case "binary":
578
- var op = c[1], left = c[2], right = c[3];
579
- switch (op) {
580
- case "<=": return [ "binary", ">", left, right ];
581
- case "<": return [ "binary", ">=", left, right ];
582
- case ">=": return [ "binary", "<", left, right ];
583
- case ">": return [ "binary", "<=", left, right ];
584
- case "==": return [ "binary", "!=", left, right ];
585
- case "!=": return [ "binary", "==", left, right ];
586
- case "===": return [ "binary", "!==", left, right ];
587
- case "!==": return [ "binary", "===", left, right ];
588
- case "&&": return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]);
589
- case "||": return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]);
590
- }
591
- break;
592
- }
593
- return not_c;
561
+ function boolean_expr(expr) {
562
+ return ( (expr[0] == "unary-prefix"
563
+ && member(expr[1], [ "!", "delete" ])) ||
564
+
565
+ (expr[0] == "binary"
566
+ && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) ||
567
+
568
+ (expr[0] == "binary"
569
+ && member(expr[1], [ "&&", "||" ])
570
+ && boolean_expr(expr[2])
571
+ && boolean_expr(expr[3])) ||
572
+
573
+ (expr[0] == "conditional"
574
+ && boolean_expr(expr[2])
575
+ && boolean_expr(expr[3])) ||
576
+
577
+ (expr[0] == "assign"
578
+ && expr[1] === true
579
+ && boolean_expr(expr[3])) ||
580
+
581
+ (expr[0] == "seq"
582
+ && boolean_expr(expr[expr.length - 1]))
583
+ );
594
584
  };
595
585
 
596
586
  function make_conditional(c, t, e) {
@@ -605,16 +595,143 @@ function empty(b) {
605
595
  return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
606
596
  };
607
597
 
598
+ function is_string(node) {
599
+ return (node[0] == "string" ||
600
+ node[0] == "unary-prefix" && node[1] == "typeof" ||
601
+ node[0] == "binary" && node[1] == "+" &&
602
+ (is_string(node[2]) || is_string(node[3])));
603
+ };
604
+
605
+ var when_constant = (function(){
606
+
607
+ var $NOT_CONSTANT = {};
608
+
609
+ // this can only evaluate constant expressions. If it finds anything
610
+ // not constant, it throws $NOT_CONSTANT.
611
+ function evaluate(expr) {
612
+ switch (expr[0]) {
613
+ case "string":
614
+ case "num":
615
+ return expr[1];
616
+ case "name":
617
+ case "atom":
618
+ switch (expr[1]) {
619
+ case "true": return true;
620
+ case "false": return false;
621
+ }
622
+ break;
623
+ case "unary-prefix":
624
+ switch (expr[1]) {
625
+ case "!": return !evaluate(expr[2]);
626
+ case "typeof": return typeof evaluate(expr[2]);
627
+ case "~": return ~evaluate(expr[2]);
628
+ case "-": return -evaluate(expr[2]);
629
+ case "+": return +evaluate(expr[2]);
630
+ }
631
+ break;
632
+ case "binary":
633
+ var left = expr[2], right = expr[3];
634
+ switch (expr[1]) {
635
+ case "&&" : return evaluate(left) && evaluate(right);
636
+ case "||" : return evaluate(left) || evaluate(right);
637
+ case "|" : return evaluate(left) | evaluate(right);
638
+ case "&" : return evaluate(left) & evaluate(right);
639
+ case "^" : return evaluate(left) ^ evaluate(right);
640
+ case "+" : return evaluate(left) + evaluate(right);
641
+ case "*" : return evaluate(left) * evaluate(right);
642
+ case "/" : return evaluate(left) / evaluate(right);
643
+ case "-" : return evaluate(left) - evaluate(right);
644
+ case "<<" : return evaluate(left) << evaluate(right);
645
+ case ">>" : return evaluate(left) >> evaluate(right);
646
+ case ">>>" : return evaluate(left) >>> evaluate(right);
647
+ case "==" : return evaluate(left) == evaluate(right);
648
+ case "===" : return evaluate(left) === evaluate(right);
649
+ case "!=" : return evaluate(left) != evaluate(right);
650
+ case "!==" : return evaluate(left) !== evaluate(right);
651
+ case "<" : return evaluate(left) < evaluate(right);
652
+ case "<=" : return evaluate(left) <= evaluate(right);
653
+ case ">" : return evaluate(left) > evaluate(right);
654
+ case ">=" : return evaluate(left) >= evaluate(right);
655
+ case "in" : return evaluate(left) in evaluate(right);
656
+ case "instanceof" : return evaluate(left) instanceof evaluate(right);
657
+ }
658
+ }
659
+ throw $NOT_CONSTANT;
660
+ };
661
+
662
+ return function(expr, yes, no) {
663
+ try {
664
+ var val = evaluate(expr), ast;
665
+ switch (typeof val) {
666
+ case "string": ast = [ "string", val ]; break;
667
+ case "number": ast = [ "num", val ]; break;
668
+ case "boolean": ast = [ "name", String(val) ]; break;
669
+ default: throw new Error("Can't handle constant of type: " + (typeof val));
670
+ }
671
+ return yes.call(expr, ast, val);
672
+ } catch(ex) {
673
+ if (ex === $NOT_CONSTANT) {
674
+ if (expr[0] == "binary"
675
+ && (expr[1] == "===" || expr[1] == "!==")
676
+ && ((is_string(expr[2]) && is_string(expr[3]))
677
+ || (boolean_expr(expr[2]) && boolean_expr(expr[3])))) {
678
+ expr[1] = expr[1].substr(0, 2);
679
+ }
680
+ return no ? no.call(expr, expr) : null;
681
+ }
682
+ else throw ex;
683
+ }
684
+ };
685
+
686
+ })();
687
+
688
+ function warn_unreachable(ast) {
689
+ if (!empty(ast))
690
+ warn("Dropping unreachable code: " + gen_code(ast, true));
691
+ };
692
+
608
693
  function ast_squeeze(ast, options) {
609
694
  options = defaults(options, {
610
695
  make_seqs : true,
611
696
  dead_code : true,
612
- no_warnings : false,
613
- extra : false
697
+ keep_comps : true,
698
+ no_warnings : false
614
699
  });
615
700
 
616
701
  var w = ast_walker(), walk = w.walk, scope;
617
702
 
703
+ function negate(c) {
704
+ var not_c = [ "unary-prefix", "!", c ];
705
+ switch (c[0]) {
706
+ case "unary-prefix":
707
+ return c[1] == "!" && boolean_expr(c[2]) ? c[2] : not_c;
708
+ case "seq":
709
+ c = slice(c);
710
+ c[c.length - 1] = negate(c[c.length - 1]);
711
+ return c;
712
+ case "conditional":
713
+ return best_of(not_c, [ "conditional", c[1], negate(c[2]), negate(c[3]) ]);
714
+ case "binary":
715
+ var op = c[1], left = c[2], right = c[3];
716
+ if (!options.keep_comps) switch (op) {
717
+ case "<=" : return [ "binary", ">", left, right ];
718
+ case "<" : return [ "binary", ">=", left, right ];
719
+ case ">=" : return [ "binary", "<", left, right ];
720
+ case ">" : return [ "binary", "<=", left, right ];
721
+ }
722
+ switch (op) {
723
+ case "==" : return [ "binary", "!=", left, right ];
724
+ case "!=" : return [ "binary", "==", left, right ];
725
+ case "===" : return [ "binary", "!==", left, right ];
726
+ case "!==" : return [ "binary", "===", left, right ];
727
+ case "&&" : return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]);
728
+ case "||" : return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]);
729
+ }
730
+ break;
731
+ }
732
+ return not_c;
733
+ };
734
+
618
735
  function with_scope(s, cont) {
619
736
  var _scope = scope;
620
737
  scope = s;
@@ -624,89 +741,14 @@ function ast_squeeze(ast, options) {
624
741
  return ret;
625
742
  };
626
743
 
627
- function is_constant(node) {
628
- return node[0] == "string" || node[0] == "num";
629
- };
630
-
631
- function find_first_execute(node) {
632
- if (!node)
633
- return false;
634
-
635
- switch (node[0]) {
636
- case "num":
637
- case "string":
638
- case "name":
639
- return node;
640
- case "call":
641
- case "conditional":
642
- case "for":
643
- case "if":
644
- case "new":
645
- case "return":
646
- case "stat":
647
- case "switch":
648
- case "throw":
649
- return find_first_execute(node[1]);
650
- case "binary":
651
- return find_first_execute(node[2]);
652
- case "assign":
653
- if (node[1] === true)
654
- return find_first_execute(node[3]);
655
- break;
656
- case "var":
657
- if (node[1][0].length > 1)
658
- return find_first_execute(node[1][0][1]);
659
- break;
660
- }
661
- return null;
662
- }
663
-
664
- function find_assign_recursive(p, v) {
665
- if (p[0] == "assign" && p[1] != true || p[0] == "unary-prefix") {
666
- if (p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1])
667
- return true;
668
- return false;
669
- }
670
-
671
- if (p[0] != "assign" || p[1] !== true)
672
- return false;
673
-
674
- if ((is_constant(p[3]) && p[3][0] == v[0] && p[3][1] == v[1]) ||
675
- (p[3][0] == "name" && v[0] == "name" && p[3][1] == v[1]) ||
676
- (p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1]))
677
- return true;
678
-
679
- return find_assign_recursive(p[3], v);
680
- };
681
-
682
744
  function rmblock(block) {
683
- if (block != null && block[0] == "block" && block[1] && block[1].length == 1)
684
- block = block[1][0];
685
- return block;
686
- };
687
-
688
- function clone(obj) {
689
- if (obj && obj.constructor == Array)
690
- return MAP(obj, clone);
691
- return obj;
692
- };
693
-
694
- function make_seq_to_statements(node) {
695
- if (node[0] != "seq") {
696
- switch (node[0]) {
697
- case "var":
698
- case "const":
699
- return [ node ];
700
- default:
701
- return [ [ "stat", node ] ];
702
- }
745
+ if (block != null && block[0] == "block" && block[1]) {
746
+ if (block[1].length == 1)
747
+ block = block[1][0];
748
+ else if (block[1].length == 0)
749
+ block = [ "block" ];
703
750
  }
704
-
705
- var ret = [];
706
- for (var i = 1; i < node.length; i++)
707
- ret.push.apply(ret, make_seq_to_statements(node[i]));
708
-
709
- return ret;
751
+ return block;
710
752
  };
711
753
 
712
754
  function _lambda(name, args, body) {
@@ -734,64 +776,6 @@ function ast_squeeze(ast, options) {
734
776
  return a;
735
777
  }, []);
736
778
 
737
- if (options.extra) {
738
- // Detightening things. We do this because then we can assume that the
739
- // statements are structured in a specific way.
740
- statements = (function(a, prev) {
741
- statements.forEach(function(cur) {
742
- switch (cur[0]) {
743
- case "for":
744
- if (cur[1] != null) {
745
- a.push.apply(a, make_seq_to_statements(cur[1]));
746
- cur[1] = null;
747
- }
748
- a.push(cur);
749
- break;
750
- case "stat":
751
- var stats = make_seq_to_statements(cur[1]);
752
- stats.forEach(function(s) {
753
- if (s[1][0] == "unary-postfix")
754
- s[1][0] = "unary-prefix";
755
- });
756
- a.push.apply(a, stats);
757
- break;
758
- default:
759
- a.push(cur);
760
- }
761
- });
762
- return a;
763
- })([]);
764
-
765
- statements = (function(a, prev) {
766
- statements.forEach(function(cur) {
767
- if (!(prev && prev[0] == "stat")) {
768
- a.push(cur);
769
- prev = cur;
770
- return;
771
- }
772
-
773
- var p = prev[1];
774
- var c = find_first_execute(cur);
775
- if (c && find_assign_recursive(p, c)) {
776
- var old_cur = clone(cur);
777
- c.splice(0, c.length);
778
- c.push.apply(c, p);
779
- var tmp_cur = best_of(cur, [ "toplevel", [ prev, old_cur ] ]);
780
- if (tmp_cur == cur) {
781
- a[a.length -1] = cur;
782
- } else {
783
- cur = old_cur;
784
- a.push(cur);
785
- }
786
- } else {
787
- a.push(cur);
788
- }
789
- prev = cur;
790
- });
791
- return a;
792
- })([]);
793
- }
794
-
795
779
  statements = (function(a, prev){
796
780
  statements.forEach(function(cur){
797
781
  if (prev && ((cur[0] == "var" && prev[0] == "var") ||
@@ -812,7 +796,7 @@ function ast_squeeze(ast, options) {
812
796
  a.push(st);
813
797
  }
814
798
  else if (!options.no_warnings)
815
- warn("Removing unreachable code: " + gen_code(st, true));
799
+ warn_unreachable(st);
816
800
  }
817
801
  else {
818
802
  a.push(st);
@@ -835,22 +819,6 @@ function ast_squeeze(ast, options) {
835
819
  return a;
836
820
  })([]);
837
821
 
838
- if (options.extra) {
839
- statements = (function(a, prev){
840
- statements.forEach(function(cur){
841
- var replaced = false;
842
- if (prev && cur[0] == "for" && cur[1] == null && (prev[0] == "var" || prev[0] == "const" || prev[0] == "stat")) {
843
- cur[1] = prev;
844
- a[a.length - 1] = cur;
845
- } else {
846
- a.push(cur);
847
- }
848
- prev = cur;
849
- });
850
- return a;
851
- })([]);
852
- }
853
-
854
822
  if (block_type == "lambda") statements = (function(i, a, stat){
855
823
  while (i < statements.length) {
856
824
  stat = statements[i++];
@@ -874,6 +842,20 @@ function ast_squeeze(ast, options) {
874
842
  };
875
843
 
876
844
  function make_if(c, t, e) {
845
+ return when_constant(c, function(ast, val){
846
+ if (val) {
847
+ warn_unreachable(e);
848
+ return t;
849
+ } else {
850
+ warn_unreachable(t);
851
+ return e;
852
+ }
853
+ }, function() {
854
+ return make_real_if(c, t, e);
855
+ });
856
+ };
857
+
858
+ function make_real_if(c, t, e) {
877
859
  c = walk(c);
878
860
  t = walk(t);
879
861
  e = walk(e);
@@ -901,7 +883,10 @@ function ast_squeeze(ast, options) {
901
883
  if (empty(e) && empty(t))
902
884
  return [ "stat", c ];
903
885
  var ret = [ "if", c, t, e ];
904
- if (t[0] == "stat") {
886
+ if (t[0] == "if" && empty(t[3]) && empty(e)) {
887
+ ret = best_of(ret, walk([ "if", [ "binary", "&&", c, t[1] ], t[2] ]));
888
+ }
889
+ else if (t[0] == "stat") {
905
890
  if (e) {
906
891
  if (e[0] == "stat") {
907
892
  ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]);
@@ -911,7 +896,7 @@ function ast_squeeze(ast, options) {
911
896
  ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]);
912
897
  }
913
898
  }
914
- else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw")) {
899
+ else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw") && t[1] && e[1]) {
915
900
  ret = best_of(ret, [ t[0], make_conditional(c, t[1], e[1] ) ]);
916
901
  }
917
902
  else if (e && aborts(t)) {
@@ -936,6 +921,17 @@ function ast_squeeze(ast, options) {
936
921
  return ret;
937
922
  };
938
923
 
924
+ function _do_while(cond, body) {
925
+ return when_constant(cond, function(cond, val){
926
+ if (!val) {
927
+ warn_unreachable(body);
928
+ return [ "block" ];
929
+ } else {
930
+ return [ "for", null, null, null, walk(body) ];
931
+ }
932
+ });
933
+ };
934
+
939
935
  return w.with_walkers({
940
936
  "sub": function(expr, subscript) {
941
937
  if (subscript[0] == "string") {
@@ -963,35 +959,23 @@ function ast_squeeze(ast, options) {
963
959
  return [ branch[0] ? walk(branch[0]) : null, block ];
964
960
  }) ];
965
961
  },
966
- "function": _lambda,
962
+ "function": function() {
963
+ var ret = _lambda.apply(this, arguments);
964
+ if (ret[1] && !HOP(scope.refs, ret[1])) {
965
+ ret[1] = null;
966
+ }
967
+ return ret;
968
+ },
967
969
  "defun": _lambda,
968
970
  "block": function(body) {
969
971
  if (body) return rmblock([ "block", tighten(MAP(body, walk)) ]);
970
972
  },
971
973
  "binary": function(op, left, right) {
972
- left = walk(left);
973
- right = walk(right);
974
- var best = [ "binary", op, left, right ];
975
- if (is_constant(right)) {
976
- if (is_constant(left)) {
977
- var val = null;
978
- switch (op) {
979
- case "+": val = left[1] + right[1]; break;
980
- case "*": val = left[1] * right[1]; break;
981
- case "/": val = left[1] / right[1]; break;
982
- case "-": val = left[1] - right[1]; break;
983
- case "<<": val = left[1] << right[1]; break;
984
- case ">>": val = left[1] >> right[1]; break;
985
- case ">>>": val = left[1] >>> right[1]; break;
986
- }
987
- if (val != null) {
988
- best = best_of(best, [ typeof val == "string" ? "string" : "num", val ]);
989
- }
990
- } else if (left[0] == "binary" && left[1] == "+" && left[3][0] == "string") {
991
- best = best_of(best, [ "binary", "+", left[2], [ "string", left[3][1] + right[1] ] ]);
992
- }
993
- }
994
- return best;
974
+ return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){
975
+ return best_of(walk(c), this);
976
+ }, function no() {
977
+ return this;
978
+ });
995
979
  },
996
980
  "conditional": function(c, t, e) {
997
981
  return make_conditional(walk(c), walk(t), walk(e));
@@ -1004,17 +988,14 @@ function ast_squeeze(ast, options) {
1004
988
  f != null ? tighten(MAP(f, walk)) : null
1005
989
  ];
1006
990
  },
1007
- "unary-prefix": function(op, cond) {
1008
- if (op == "!") {
1009
- cond = walk(cond);
1010
- if (cond[0] == "unary-prefix" && cond[1] == "!") {
1011
- var p = w.parent();
1012
- if (p[0] == "unary-prefix" && p[1] == "!")
1013
- return cond[2];
1014
- return [ "unary-prefix", "!", cond ];
1015
- }
1016
- return best_of(this, negate(cond));
1017
- }
991
+ "unary-prefix": function(op, expr) {
992
+ expr = walk(expr);
993
+ var ret = [ "unary-prefix", op, expr ];
994
+ if (op == "!")
995
+ ret = best_of(ret, negate(expr));
996
+ return when_constant(ret, function(ast, val){
997
+ return walk(ast); // it's either true or false, so minifies to !0 or !1
998
+ }, function() { return ret });
1018
999
  },
1019
1000
  "name": function(name) {
1020
1001
  switch (name) {
@@ -1035,7 +1016,9 @@ function ast_squeeze(ast, options) {
1035
1016
  if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
1036
1017
  return [ "array", args ];
1037
1018
  }
1038
- }
1019
+ },
1020
+ "while": _do_while,
1021
+ "do": _do_while
1039
1022
  }, function() {
1040
1023
  return walk(ast_add_scope(ast));
1041
1024
  });
@@ -1253,7 +1236,9 @@ function gen_code(ast, beautify) {
1253
1236
  },
1254
1237
  "dot": function(expr) {
1255
1238
  var out = make(expr), i = 1;
1256
- if (needs_parens(expr))
1239
+ if (expr[0] == "num")
1240
+ out += ".";
1241
+ else if (needs_parens(expr))
1257
1242
  out = "(" + out + ")";
1258
1243
  while (i < arguments.length)
1259
1244
  out += "." + make_name(arguments[i++]);
@@ -1286,12 +1271,11 @@ function gen_code(ast, beautify) {
1286
1271
  out.push("(" + args + ")", make(block));
1287
1272
  return add_spaces(out);
1288
1273
  },
1289
- "for-in": function(has_var, key, hash, block) {
1290
- var out = add_spaces([ "for", "(" ]);
1291
- if (has_var)
1292
- out += "var ";
1293
- out += add_spaces([ make_name(key) + " in " + make(hash) + ")", make(block) ]);
1294
- return out;
1274
+ "for-in": function(vvar, key, hash, block) {
1275
+ return add_spaces([ "for", "(" +
1276
+ (vvar ? make(vvar).replace(/;+$/, "") : make(key)),
1277
+ "in",
1278
+ make(hash) + ")", make(block) ]);
1295
1279
  },
1296
1280
  "while": function(condition, block) {
1297
1281
  return add_spaces([ "while", "(" + make(condition) + ")", make(block) ]);
@@ -1314,7 +1298,8 @@ function gen_code(ast, beautify) {
1314
1298
  left = "(" + left + ")";
1315
1299
  }
1316
1300
  if (member(rvalue[0], [ "assign", "conditional", "seq" ]) ||
1317
- rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]]) {
1301
+ rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]] &&
1302
+ !(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) {
1318
1303
  right = "(" + right + ")";
1319
1304
  }
1320
1305
  return add_spaces([ left, operator, right ]);
@@ -1350,7 +1335,8 @@ function gen_code(ast, beautify) {
1350
1335
  var key = p[0], val = make(p[1]);
1351
1336
  if (beautify && beautify.quote_keys) {
1352
1337
  key = make_string(key);
1353
- } else if (typeof key == "number" || !beautify && +key + "" == key) {
1338
+ } else if ((typeof key == "number" || !beautify && +key + "" == key)
1339
+ && parseFloat(key) >= 0) {
1354
1340
  key = make_num(+key);
1355
1341
  } else if (!is_identifier(key)) {
1356
1342
  key = make_string(key);
@@ -1367,6 +1353,7 @@ function gen_code(ast, beautify) {
1367
1353
  "array": function(elements) {
1368
1354
  if (elements.length == 0) return "[]";
1369
1355
  return add_spaces([ "[", add_commas(MAP(elements, function(el){
1356
+ if (!beautify && el[0] == "atom" && el[1] == "undefined") return "";
1370
1357
  return parenthesize(el, "seq");
1371
1358
  })), "]" ]);
1372
1359
  },
@@ -1384,12 +1371,6 @@ function gen_code(ast, beautify) {
1384
1371
  },
1385
1372
  "atom": function(name) {
1386
1373
  return make_name(name);
1387
- },
1388
- "comment1": function(text) {
1389
- return "//" + text + "\n";
1390
- },
1391
- "comment2": function(text) {
1392
- return "/*" + text + "*/";
1393
1374
  }
1394
1375
  };
1395
1376
 
@@ -1442,8 +1423,16 @@ function gen_code(ast, beautify) {
1442
1423
  var stat = statements[i];
1443
1424
  var code = make(stat);
1444
1425
  if (code != ";") {
1445
- if (!beautify && i == last)
1446
- code = code.replace(/;+\s*$/, "");
1426
+ if (!beautify && i == last) {
1427
+ if ((stat[0] == "while" && empty(stat[2])) ||
1428
+ (member(stat[0], [ "for", "for-in"] ) && empty(stat[4])) ||
1429
+ (stat[0] == "if" && empty(stat[2]) && !stat[3]) ||
1430
+ (stat[0] == "if" && stat[3] && empty(stat[3]))) {
1431
+ code = code.replace(/;*\s*$/, ";");
1432
+ } else {
1433
+ code = code.replace(/;+\s*$/, "");
1434
+ }
1435
+ }
1447
1436
  a.push(code);
1448
1437
  }
1449
1438
  }
@@ -1498,6 +1487,49 @@ function gen_code(ast, beautify) {
1498
1487
  return make(ast);
1499
1488
  };
1500
1489
 
1490
+ function split_lines(code, max_line_length) {
1491
+ var splits = [ 0 ];
1492
+ jsp.parse(function(){
1493
+ var next_token = jsp.tokenizer(code);
1494
+ var last_split = 0;
1495
+ var prev_token;
1496
+ function current_length(tok) {
1497
+ return tok.pos - last_split;
1498
+ };
1499
+ function split_here(tok) {
1500
+ last_split = tok.pos;
1501
+ splits.push(last_split);
1502
+ };
1503
+ function custom(){
1504
+ var tok = next_token.apply(this, arguments);
1505
+ out: {
1506
+ if (prev_token) {
1507
+ if (prev_token.type == "keyword") break out;
1508
+ }
1509
+ if (current_length(tok) > max_line_length) {
1510
+ switch (tok.type) {
1511
+ case "keyword":
1512
+ case "atom":
1513
+ case "name":
1514
+ case "punc":
1515
+ split_here(tok);
1516
+ break out;
1517
+ }
1518
+ }
1519
+ }
1520
+ prev_token = tok;
1521
+ return tok;
1522
+ };
1523
+ custom.context = function() {
1524
+ return next_token.context.apply(this, arguments);
1525
+ };
1526
+ return custom;
1527
+ }());
1528
+ return splits.map(function(pos, i){
1529
+ return code.substring(pos, splits[i + 1] || code.length);
1530
+ }).join("\n");
1531
+ };
1532
+
1501
1533
  /* -----[ Utilities ]----- */
1502
1534
 
1503
1535
  function repeat_string(str, i) {
@@ -1558,3 +1590,5 @@ exports.gen_code = gen_code;
1558
1590
  exports.ast_add_scope = ast_add_scope;
1559
1591
  exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;
1560
1592
  exports.set_logger = function(logger) { warn = logger };
1593
+ exports.make_string = make_string;
1594
+ exports.split_lines = split_lines;