uglifier 0.1.0 → 0.1.1

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.

data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/lib/uglifier.rb CHANGED
@@ -10,6 +10,7 @@ class Uglifier
10
10
  :squeeze => true, # Squeeze code resulting in smaller, but less-readable code
11
11
  :seqs => true, # Reduce consecutive statements in blocks into single statement
12
12
  :dead_code => true, # Remove dead code (e.g. after return)
13
+ :extra => false, # Additional and potentially unsafe optimizations
13
14
  :beautify => false, # Ouput indented code
14
15
  :beautify_options => {
15
16
  :indent_level => 4,
@@ -51,12 +52,13 @@ class Uglifier
51
52
  def squeeze(cxt, ast)
52
53
  cxt["ast_squeeze"].call(ast, {
53
54
  "make_seqs" => @options[:seqs],
54
- "dead_code" => @options[:dead_code]
55
+ "dead_code" => @options[:dead_code],
56
+ "extra" => @options[:extra]
55
57
  })
56
58
  end
57
59
 
58
60
  def initialize_v8(cxt)
59
- cxt["process"] = { :version => "v1" }
61
+ cxt["process"] = { :version => "v0.2.0" }
60
62
  exports = {
61
63
  "sys" => {
62
64
  :debug => lambda { |m| puts m }
@@ -15,4 +15,14 @@ describe "Uglifier" do
15
15
  Uglifier.new.compile(")(")
16
16
  }.should raise_error(Uglifier::Error)
17
17
  end
18
+
19
+ it "logs to output" do
20
+ $stdout.should_receive(:write).at_least(:once)
21
+ lambda {
22
+ Uglifier.new.compile("function uglifyThis() {
23
+ return;
24
+ return 1; // This is an error
25
+ }")
26
+ }.should_not raise_error(Uglifier::Error)
27
+ end
18
28
  end
data/uglifier.gemspec ADDED
@@ -0,0 +1,83 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{uglifier}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ville Lautanala"]
12
+ s.date = %q{2010-11-18}
13
+ s.email = %q{lautis@gmail.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE.txt",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".gitmodules",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/uglifier.rb",
28
+ "spec/spec_helper.rb",
29
+ "spec/uglifier_spec.rb",
30
+ "uglifier.gemspec",
31
+ "vendor/uglifyjs/lib/parse-js.js",
32
+ "vendor/uglifyjs/lib/process.js"
33
+ ]
34
+ s.homepage = %q{http://github.com/lautis/uglifier}
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.7}
37
+ s.summary = %q{Ruby wrapper for UglifyJS JavaScript compressor}
38
+ s.test_files = [
39
+ "spec/spec_helper.rb",
40
+ "spec/uglifier_spec.rb"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<therubyracer>, [">= 0.7.5"])
49
+ s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
50
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
51
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
52
+ s.add_development_dependency(%q<rcov>, [">= 0"])
53
+ s.add_development_dependency(%q<therubyracer>, [">= 0.7.5"])
54
+ s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
55
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
56
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
57
+ s.add_development_dependency(%q<rcov>, [">= 0"])
58
+ else
59
+ s.add_dependency(%q<therubyracer>, [">= 0.7.5"])
60
+ s.add_dependency(%q<rspec>, ["~> 2.0.0"])
61
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
62
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
63
+ s.add_dependency(%q<rcov>, [">= 0"])
64
+ s.add_dependency(%q<therubyracer>, [">= 0.7.5"])
65
+ s.add_dependency(%q<rspec>, ["~> 2.0.0"])
66
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
67
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
68
+ s.add_dependency(%q<rcov>, [">= 0"])
69
+ end
70
+ else
71
+ s.add_dependency(%q<therubyracer>, [">= 0.7.5"])
72
+ s.add_dependency(%q<rspec>, ["~> 2.0.0"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
75
+ s.add_dependency(%q<rcov>, [">= 0"])
76
+ s.add_dependency(%q<therubyracer>, [">= 0.7.5"])
77
+ s.add_dependency(%q<rspec>, ["~> 2.0.0"])
78
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
79
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre5"])
80
+ s.add_dependency(%q<rcov>, [">= 0"])
81
+ end
82
+ end
83
+
@@ -460,9 +460,10 @@ function tokenizer($TEXT, skip_comments) {
460
460
 
461
461
  var handle_slash = skip_comments ? function() {
462
462
  next();
463
+ var regex_allowed = S.regex_allowed;
463
464
  switch (peek()) {
464
- case "/": read_line_comment(); return next_token();
465
- case "*": read_multiline_comment(); return next_token();
465
+ case "/": read_line_comment(); S.regex_allowed = regex_allowed; return next_token();
466
+ case "*": read_multiline_comment(); S.regex_allowed = regex_allowed; return next_token();
466
467
  }
467
468
  return S.regex_allowed ? read_regexp() : read_operator("/");
468
469
  } : function() {
@@ -405,9 +405,7 @@ function ast_add_scope(ast) {
405
405
  if (c != null) return [
406
406
  "try",
407
407
  t.map(walk),
408
- with_new_scope(function(){
409
- return [ define(c[0]), c[1].map(walk) ];
410
- }),
408
+ [ define(c[0]), c[1].map(walk) ],
411
409
  f != null ? f.map(walk) : null
412
410
  ];
413
411
  },
@@ -509,9 +507,7 @@ function ast_mangle(ast, do_toplevel) {
509
507
  "try": function(t, c, f) {
510
508
  return [ "try",
511
509
  t.map(walk),
512
- c ? with_scope(c.scope, function(){
513
- return [ get_mangled(c[0]), c[1].map(walk) ];
514
- }) : null,
510
+ c != null ? [ get_mangled(c[0]), c[1].map(walk) ] : null,
515
511
  f != null ? f.map(walk) : null ];
516
512
  },
517
513
  "toplevel": function(body) {
@@ -527,36 +523,6 @@ function ast_mangle(ast, do_toplevel) {
527
523
  });
528
524
  };
529
525
 
530
- // function ast_has_side_effects(ast) {
531
- // var w = ast_walker();
532
- // var FOUND_SIDE_EFFECTS = {};
533
- // function _found() { throw FOUND_SIDE_EFFECTS };
534
- // try {
535
- // w.with_walkers({
536
- // "new": _found,
537
- // "call": _found,
538
- // "assign": _found,
539
- // "defun": _found,
540
- // "var": _found,
541
- // "const": _found,
542
- // "throw": _found,
543
- // "return": _found,
544
- // "break": _found,
545
- // "continue": _found,
546
- // "label": _found,
547
- // "function": function(name) {
548
- // if (name) _found();
549
- // }
550
- // }, function(){
551
- // w.walk(ast);
552
- // });
553
- // } catch(ex) {
554
- // if (ex === FOUND_SIDE_EFFECTS)
555
- // return true;
556
- // throw ex;
557
- // }
558
- // };
559
-
560
526
  /* -----[
561
527
  - compress foo["bar"] into foo.bar,
562
528
  - remove block brackets {} where possible
@@ -572,11 +538,72 @@ function warn(msg) {
572
538
  sys.debug(msg);
573
539
  };
574
540
 
541
+ function best_of(ast1, ast2) {
542
+ return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
543
+ };
544
+
545
+ function last_stat(b) {
546
+ if (b[0] == "block" && b[1] && b[1].length > 0)
547
+ return b[1][b[1].length - 1];
548
+ return b;
549
+ }
550
+
551
+ function aborts(t) {
552
+ if (t) {
553
+ t = last_stat(t);
554
+ if (t[0] == "return" || t[0] == "break" || t[0] == "continue" || t[0] == "throw")
555
+ return true;
556
+ }
557
+ };
558
+
559
+ function negate(c) {
560
+ var not_c = [ "unary-prefix", "!", c ];
561
+ switch (c[0]) {
562
+ case "unary-prefix":
563
+ return c[1] == "!" ? c[2] : c;
564
+ case "atom":
565
+ switch (c[1]) {
566
+ case "false": return [ "num", 0 ];
567
+ case "true": return [ "num", 1 ];
568
+ }
569
+ break;
570
+ case "binary":
571
+ var op = c[1], left = c[2], right = c[3];
572
+ switch (op) {
573
+ case "<=": return [ "binary", ">", left, right ];
574
+ case "<": return [ "binary", ">=", left, right ];
575
+ case ">=": return [ "binary", "<", left, right ];
576
+ case ">": return [ "binary", "<=", left, right ];
577
+ case "==": return [ "binary", "!=", left, right ];
578
+ case "!=": return [ "binary", "==", left, right ];
579
+ case "===": return [ "binary", "!==", left, right ];
580
+ case "!==": return [ "binary", "===", left, right ];
581
+ case "&&": return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]);
582
+ case "||": return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]);
583
+ }
584
+ break;
585
+ }
586
+ return not_c;
587
+ };
588
+
589
+ function make_conditional(c, t, e) {
590
+ if (c[0] == "unary-prefix" && c[1] == "!") {
591
+ return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
592
+ } else {
593
+ return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ];
594
+ }
595
+ };
596
+
597
+ function empty(b) {
598
+ return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
599
+ };
600
+
575
601
  function ast_squeeze(ast, options) {
576
602
  options = defaults(options, {
577
- make_seqs: true,
578
- dead_code: true,
579
- no_warnings: false
603
+ make_seqs : true,
604
+ dead_code : true,
605
+ no_warnings : false,
606
+ extra : false
580
607
  });
581
608
 
582
609
  var w = ast_walker(), walk = w.walk;
@@ -585,22 +612,98 @@ function ast_squeeze(ast, options) {
585
612
  return node[0] == "string" || node[0] == "num";
586
613
  };
587
614
 
615
+ function find_first_execute(node) {
616
+ if (!node)
617
+ return false;
618
+
619
+ switch (node[0]) {
620
+ case "num":
621
+ case "string":
622
+ case "name":
623
+ return node;
624
+ case "call":
625
+ case "conditional":
626
+ case "for":
627
+ case "if":
628
+ case "new":
629
+ case "return":
630
+ case "stat":
631
+ case "switch":
632
+ case "throw":
633
+ return find_first_execute(node[1]);
634
+ case "binary":
635
+ return find_first_execute(node[2]);
636
+ case "assign":
637
+ if (node[1] === true)
638
+ return find_first_execute(node[3]);
639
+ break;
640
+ case "var":
641
+ if (node[1][0].length > 1)
642
+ return find_first_execute(node[1][0][1]);
643
+ break;
644
+ }
645
+ return null;
646
+ }
647
+
648
+ function find_assign_recursive(p, v) {
649
+ if (p[0] == "assign" && p[1] != true || p[0] == "unary-prefix") {
650
+ if (p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1])
651
+ return true;
652
+ return false;
653
+ }
654
+
655
+ if (p[0] != "assign" || p[1] !== true)
656
+ return false;
657
+
658
+ if ((is_constant(p[3]) && p[3][0] == v[0] && p[3][1] == v[1]) ||
659
+ (p[3][0] == "name" && v[0] == "name" && p[3][1] == v[1]) ||
660
+ (p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1]))
661
+ return true;
662
+
663
+ return find_assign_recursive(p[3], v);
664
+ };
665
+
588
666
  function rmblock(block) {
589
667
  if (block != null && block[0] == "block" && block[1] && block[1].length == 1)
590
668
  block = block[1][0];
591
669
  return block;
592
670
  };
593
671
 
672
+ function clone(obj) {
673
+ if (obj && obj.constructor == Array)
674
+ return obj.map(clone);
675
+ return obj;
676
+ };
677
+
678
+ function make_seq_to_statements(node) {
679
+ if (node[0] != "seq") {
680
+ switch (node[0]) {
681
+ case "var":
682
+ case "const":
683
+ return [ node ];
684
+ default:
685
+ return [ [ "stat", node ] ];
686
+ }
687
+ }
688
+
689
+ var ret = [];
690
+ for (var i = 1; i < node.length; i++)
691
+ ret.push.apply(ret, make_seq_to_statements(node[i]));
692
+
693
+ return ret;
694
+ };
695
+
594
696
  function _lambda(name, args, body) {
595
697
  return [ this[0], name, args, tighten(body.map(walk), "lambda") ];
596
698
  };
597
699
 
598
700
  // we get here for blocks that have been already transformed.
599
- // this function does two things:
701
+ // this function does a few things:
600
702
  // 1. discard useless blocks
601
703
  // 2. join consecutive var declarations
602
704
  // 3. remove obviously dead code
603
705
  // 4. transform consecutive statements using the comma operator
706
+ // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... }
604
707
  function tighten(statements, block_type) {
605
708
  statements = statements.reduce(function(a, stat){
606
709
  if (stat[0] == "block") {
@@ -613,6 +716,64 @@ function ast_squeeze(ast, options) {
613
716
  return a;
614
717
  }, []);
615
718
 
719
+ if (options.extra) {
720
+ // Detightening things. We do this because then we can assume that the
721
+ // statements are structured in a specific way.
722
+ statements = (function(a, prev) {
723
+ statements.forEach(function(cur) {
724
+ switch (cur[0]) {
725
+ case "for":
726
+ if (cur[1] != null) {
727
+ a.push.apply(a, make_seq_to_statements(cur[1]));
728
+ cur[1] = null;
729
+ }
730
+ a.push(cur);
731
+ break;
732
+ case "stat":
733
+ var stats = make_seq_to_statements(cur[1]);
734
+ stats.forEach(function(s) {
735
+ if (s[1][0] == "unary-postfix")
736
+ s[1][0] = "unary-prefix";
737
+ });
738
+ a.push.apply(a, stats);
739
+ break;
740
+ default:
741
+ a.push(cur);
742
+ }
743
+ });
744
+ return a;
745
+ })([]);
746
+
747
+ statements = (function(a, prev) {
748
+ statements.forEach(function(cur) {
749
+ if (!(prev && prev[0] == "stat")) {
750
+ a.push(cur);
751
+ prev = cur;
752
+ return;
753
+ }
754
+
755
+ var p = prev[1];
756
+ var c = find_first_execute(cur);
757
+ if (c && find_assign_recursive(p, c)) {
758
+ var old_cur = clone(cur);
759
+ c.splice(0, c.length);
760
+ c.push.apply(c, p);
761
+ var tmp_cur = best_of(cur, [ "toplevel", [ prev, old_cur ] ]);
762
+ if (tmp_cur == cur) {
763
+ a[a.length -1] = cur;
764
+ } else {
765
+ cur = old_cur;
766
+ a.push(cur);
767
+ }
768
+ } else {
769
+ a.push(cur);
770
+ }
771
+ prev = cur;
772
+ });
773
+ return a;
774
+ })([]);
775
+ }
776
+
616
777
  statements = (function(a, prev){
617
778
  statements.forEach(function(cur){
618
779
  if (prev && ((cur[0] == "var" && prev[0] == "var") ||
@@ -646,28 +807,47 @@ function ast_squeeze(ast, options) {
646
807
 
647
808
  if (options.make_seqs) statements = (function(a, prev) {
648
809
  statements.forEach(function(cur){
649
- if (!prev) {
650
- a.push(cur);
651
- if (cur[0] == "stat") prev = cur;
652
- } else if (cur[0] == "stat" && prev[0] == "stat") {
810
+ if (prev && prev[0] == "stat" && cur[0] == "stat") {
653
811
  prev[1] = [ "seq", prev[1], cur[1] ];
654
812
  } else {
655
813
  a.push(cur);
656
- prev = null;
814
+ prev = cur;
657
815
  }
658
816
  });
659
817
  return a;
660
818
  })([]);
661
819
 
820
+ if (options.extra) {
821
+ statements = (function(a, prev){
822
+ statements.forEach(function(cur){
823
+ var replaced = false;
824
+ if (prev && cur[0] == "for" && cur[1] == null && (prev[0] == "var" || prev[0] == "const" || prev[0] == "stat")) {
825
+ cur[1] = prev;
826
+ a[a.length - 1] = cur;
827
+ } else {
828
+ a.push(cur);
829
+ }
830
+ prev = cur;
831
+ });
832
+ return a;
833
+ })([]);
834
+ }
835
+
662
836
  if (block_type == "lambda") statements = (function(i, a, stat){
663
837
  while (i < statements.length) {
664
838
  stat = statements[i++];
665
- if (stat[0] == "if" && stat[2][0] == "return" && stat[2][1] == null && !stat[3]) {
666
- a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
667
- break;
668
- } else {
669
- a.push(stat);
839
+ if (stat[0] == "if" && !stat[3]) {
840
+ if (stat[2][0] == "return" && stat[2][1] == null) {
841
+ a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
842
+ break;
843
+ }
844
+ var last = last_stat(stat[2]);
845
+ if (last[0] == "return" && last[1] == null) {
846
+ a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ]));
847
+ break;
848
+ }
670
849
  }
850
+ a.push(stat);
671
851
  }
672
852
  return a;
673
853
  })(0, []);
@@ -675,52 +855,30 @@ function ast_squeeze(ast, options) {
675
855
  return statements;
676
856
  };
677
857
 
678
- function best_of(ast1, ast2) {
679
- return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
680
- };
681
-
682
- function aborts(t) {
683
- if (t[0] == "block" && t[1] && t[1].length > 0)
684
- t = t[1][t[1].length - 1]; // interested in last statement
685
- if (t[0] == "return" || t[0] == "break" || t[0] == "continue" || t[0] == "throw")
686
- return true;
687
- };
688
-
689
- function negate(c) {
690
- if (c[0] == "unary-prefix" && c[1] == "!") return c[2];
691
- else return [ "unary-prefix", "!", c ];
692
- };
693
-
694
- function make_conditional(c, t, e) {
695
- if (c[0] == "unary-prefix" && c[1] == "!") {
696
- return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
697
- } else {
698
- return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ];
699
- }
700
- };
701
-
702
- function empty(b) {
703
- return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
704
- };
705
-
706
858
  function make_if(c, t, e) {
707
859
  c = walk(c);
708
860
  t = walk(t);
709
861
  e = walk(e);
710
- var negated = c[0] == "unary-prefix" && c[1] == "!";
862
+
711
863
  if (empty(t)) {
712
- if (negated) c = c[2];
713
- else c = [ "unary-prefix", "!", c ];
864
+ c = negate(c);
714
865
  t = e;
715
866
  e = null;
716
- }
717
- if (empty(e)) {
867
+ } else if (empty(e)) {
718
868
  e = null;
719
869
  } else {
720
- if (negated) {
721
- c = c[2];
722
- var tmp = t; t = e; e = tmp;
723
- }
870
+ // if we have both else and then, maybe it makes sense to switch them?
871
+ (function(){
872
+ var a = gen_code(c);
873
+ var n = negate(c);
874
+ var b = gen_code(n);
875
+ if (b.length < a.length) {
876
+ var tmp = t;
877
+ t = e;
878
+ e = tmp;
879
+ c = n;
880
+ }
881
+ })();
724
882
  }
725
883
  if (empty(e) && empty(t))
726
884
  return [ "stat", c ];
@@ -748,6 +906,15 @@ function ast_squeeze(ast, options) {
748
906
  }
749
907
  ret = walk([ "block", ret ]);
750
908
  }
909
+ else if (t && aborts(e)) {
910
+ ret = [ [ "if", negate(c), e ] ];
911
+ if (t[0] == "block") {
912
+ if (t[1]) ret = ret.concat(t[1]);
913
+ } else {
914
+ ret.push(t);
915
+ }
916
+ ret = walk([ "block", ret ]);
917
+ }
751
918
  return ret;
752
919
  };
753
920
 
@@ -785,19 +952,23 @@ function ast_squeeze(ast, options) {
785
952
  left = walk(left);
786
953
  right = walk(right);
787
954
  var best = [ "binary", op, left, right ];
788
- if (is_constant(left) && is_constant(right)) {
789
- var val = null;
790
- switch (op) {
791
- case "+": val = left[1] + right[1]; break;
792
- case "*": val = left[1] * right[1]; break;
793
- case "/": val = left[1] / right[1]; break;
794
- case "-": val = left[1] - right[1]; break;
795
- case "<<": val = left[1] << right[1]; break;
796
- case ">>": val = left[1] >> right[1]; break;
797
- case ">>>": val = left[1] >>> right[1]; break;
798
- }
799
- if (val != null) {
800
- best = best_of(best, [ typeof val == "string" ? "string" : "num", val ]);
955
+ if (is_constant(right)) {
956
+ if (is_constant(left)) {
957
+ var val = null;
958
+ switch (op) {
959
+ case "+": val = left[1] + right[1]; break;
960
+ case "*": val = left[1] * right[1]; break;
961
+ case "/": val = left[1] / right[1]; break;
962
+ case "-": val = left[1] - right[1]; break;
963
+ case "<<": val = left[1] << right[1]; break;
964
+ case ">>": val = left[1] >> right[1]; break;
965
+ case ">>>": val = left[1] >>> right[1]; break;
966
+ }
967
+ if (val != null) {
968
+ best = best_of(best, [ typeof val == "string" ? "string" : "num", val ]);
969
+ }
970
+ } else if (left[0] == "binary" && left[1] == "+" && left[3][0] == "string") {
971
+ best = best_of(best, [ "binary", "+", left[2], [ "string", left[3][1] + right[1] ] ]);
801
972
  }
802
973
  }
803
974
  return best;
@@ -812,6 +983,16 @@ function ast_squeeze(ast, options) {
812
983
  c != null ? [ c[0], tighten(c[1].map(walk)) ] : null,
813
984
  f != null ? tighten(f.map(walk)) : null
814
985
  ];
986
+ },
987
+ "unary-prefix": function(op, cond) {
988
+ if (op == "!")
989
+ return best_of(this, negate(cond));
990
+ },
991
+ "call": function(expr, args) {
992
+ if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
993
+ // foo.toString() ==> foo+""
994
+ return [ "binary", "+", expr[1], [ "string", "" ]];
995
+ }
815
996
  }
816
997
  }, function() {
817
998
  return walk(ast);
@@ -1005,7 +1186,7 @@ function gen_code(ast, beautify) {
1005
1186
  "assign": function(op, lvalue, rvalue) {
1006
1187
  if (op && op !== true) op += "=";
1007
1188
  else op = "=";
1008
- return add_spaces([ make(lvalue), op, make(rvalue) ]);
1189
+ return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]);
1009
1190
  },
1010
1191
  "dot": function(expr) {
1011
1192
  var out = make(expr), i = 1;
@@ -1193,6 +1374,10 @@ function gen_code(ast, beautify) {
1193
1374
  };
1194
1375
 
1195
1376
  function make_name(name) {
1377
+ switch (name) {
1378
+ case "true": return beautify ? "true" : "!0";
1379
+ case "false": return beautify ? "false" : "!1";
1380
+ }
1196
1381
  return name.toString();
1197
1382
  };
1198
1383
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uglifier
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 0
10
- version: 0.1.0
9
+ - 1
10
+ version: 0.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ville Lautanala
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-03 00:00:00 +02:00
18
+ date: 2010-11-18 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -197,6 +197,7 @@ files:
197
197
  - lib/uglifier.rb
198
198
  - spec/spec_helper.rb
199
199
  - spec/uglifier_spec.rb
200
+ - uglifier.gemspec
200
201
  - vendor/uglifyjs/lib/parse-js.js
201
202
  - vendor/uglifyjs/lib/process.js
202
203
  has_rdoc: true