uglifier 1.0.0 → 1.0.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.

Files changed (5) hide show
  1. data/.travis.yml +0 -1
  2. data/VERSION +1 -1
  3. data/lib/uglify.js +427 -108
  4. data/uglifier.gemspec +7 -6
  5. metadata +31 -46
data/.travis.yml CHANGED
@@ -1,5 +1,4 @@
1
1
  rvm:
2
2
  - 1.8.7
3
3
  - 1.9.2
4
- env: "EXECJS_RUNTIME='RubyRacer'"
5
4
  before_script: "git submodule update --init --recursive"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.0.1
data/lib/uglify.js CHANGED
@@ -239,7 +239,7 @@ var OPERATORS = array_to_hash([
239
239
  "||"
240
240
  ]);
241
241
 
242
- var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\v\u200b"));
242
+ var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b"));
243
243
 
244
244
  var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
245
245
 
@@ -454,11 +454,12 @@ function tokenizer($TEXT) {
454
454
  case "r" : return "\r";
455
455
  case "t" : return "\t";
456
456
  case "b" : return "\b";
457
- case "v" : return "\v";
457
+ case "v" : return "\u000b";
458
458
  case "f" : return "\f";
459
459
  case "0" : return "\0";
460
460
  case "x" : return String.fromCharCode(hex_bytes(2));
461
461
  case "u" : return String.fromCharCode(hex_bytes(4));
462
+ case "\n": return "";
462
463
  default : return ch;
463
464
  }
464
465
  };
@@ -479,7 +480,24 @@ function tokenizer($TEXT) {
479
480
  var quote = next(), ret = "";
480
481
  for (;;) {
481
482
  var ch = next(true);
482
- if (ch == "\\") ch = read_escaped_char();
483
+ if (ch == "\\") {
484
+ // read OctalEscapeSequence (XXX: deprecated if "strict mode")
485
+ // https://github.com/mishoo/UglifyJS/issues/178
486
+ var octal_len = 0, first = null;
487
+ ch = read_while(function(ch){
488
+ if (ch >= "0" && ch <= "7") {
489
+ if (!first) {
490
+ first = ch;
491
+ return ++octal_len;
492
+ }
493
+ else if (first <= "3" && octal_len <= 2) return ++octal_len;
494
+ else if (first >= "4" && octal_len <= 1) return ++octal_len;
495
+ }
496
+ return false;
497
+ });
498
+ if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
499
+ else ch = read_escaped_char();
500
+ }
483
501
  else if (ch == quote) break;
484
502
  ret += ch;
485
503
  }
@@ -1107,11 +1125,6 @@ function parse($TEXT, exigent_mode, embed_tokens) {
1107
1125
  next();
1108
1126
  return new_();
1109
1127
  }
1110
- if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
1111
- return make_unary("unary-prefix",
1112
- prog1(S.token.value, next),
1113
- expr_atom(allow_calls));
1114
- }
1115
1128
  if (is("punc")) {
1116
1129
  switch (S.token.value) {
1117
1130
  case "(":
@@ -1212,13 +1225,23 @@ function parse($TEXT, exigent_mode, embed_tokens) {
1212
1225
  next();
1213
1226
  return subscripts(as("call", expr, expr_list(")")), true);
1214
1227
  }
1215
- if (allow_calls && is("operator") && HOP(UNARY_POSTFIX, S.token.value)) {
1216
- return prog1(curry(make_unary, "unary-postfix", S.token.value, expr),
1217
- next);
1218
- }
1219
1228
  return expr;
1220
1229
  };
1221
1230
 
1231
+ function maybe_unary(allow_calls) {
1232
+ if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
1233
+ return make_unary("unary-prefix",
1234
+ prog1(S.token.value, next),
1235
+ maybe_unary(allow_calls));
1236
+ }
1237
+ var val = expr_atom(allow_calls);
1238
+ while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) {
1239
+ val = make_unary("unary-postfix", S.token.value, val);
1240
+ next();
1241
+ }
1242
+ return val;
1243
+ };
1244
+
1222
1245
  function make_unary(tag, op, expr) {
1223
1246
  if ((op == "++" || op == "--") && !is_assignable(expr))
1224
1247
  croak("Invalid use of " + op + " operator");
@@ -1231,14 +1254,14 @@ function parse($TEXT, exigent_mode, embed_tokens) {
1231
1254
  var prec = op != null ? PRECEDENCE[op] : null;
1232
1255
  if (prec != null && prec > min_prec) {
1233
1256
  next();
1234
- var right = expr_op(expr_atom(true), prec, no_in);
1257
+ var right = expr_op(maybe_unary(true), prec, no_in);
1235
1258
  return expr_op(as("binary", op, left, right), min_prec, no_in);
1236
1259
  }
1237
1260
  return left;
1238
1261
  };
1239
1262
 
1240
1263
  function expr_ops(no_in) {
1241
- return expr_op(expr_atom(true), 0, no_in);
1264
+ return expr_op(maybe_unary(true), 0, no_in);
1242
1265
  };
1243
1266
 
1244
1267
  function maybe_conditional(no_in) {
@@ -1707,20 +1730,27 @@ Scope.prototype = {
1707
1730
  return m;
1708
1731
  }
1709
1732
  },
1733
+ set_mangle: function(name, m) {
1734
+ this.rev_mangled[m] = name;
1735
+ return this.mangled[name] = m;
1736
+ },
1710
1737
  get_mangled: function(name, newMangle) {
1711
1738
  if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use
1712
1739
  var s = this.has(name);
1713
1740
  if (!s) return name; // not in visible scope, no mangle
1714
1741
  if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope
1715
1742
  if (!newMangle) return name; // not found and no mangling requested
1716
-
1717
- var m = s.next_mangled();
1718
- s.rev_mangled[m] = name;
1719
- return s.mangled[name] = m;
1743
+ return s.set_mangle(name, s.next_mangled());
1744
+ },
1745
+ references: function(name) {
1746
+ return name && !this.parent || this.uses_with || this.uses_eval || this.refs[name];
1720
1747
  },
1721
- define: function(name) {
1722
- if (name != null)
1723
- return this.names[name] = name;
1748
+ define: function(name, type) {
1749
+ if (name != null) {
1750
+ if (type == "var" || !HOP(this.names, name))
1751
+ this.names[name] = type || "var";
1752
+ return name;
1753
+ }
1724
1754
  }
1725
1755
  };
1726
1756
 
@@ -1738,8 +1768,8 @@ function ast_add_scope(ast) {
1738
1768
  return ret;
1739
1769
  };
1740
1770
 
1741
- function define(name) {
1742
- return current_scope.define(name);
1771
+ function define(name, type) {
1772
+ return current_scope.define(name, type);
1743
1773
  };
1744
1774
 
1745
1775
  function reference(name) {
@@ -1748,33 +1778,41 @@ function ast_add_scope(ast) {
1748
1778
 
1749
1779
  function _lambda(name, args, body) {
1750
1780
  var is_defun = this[0] == "defun";
1751
- return [ this[0], is_defun ? define(name) : name, args, with_new_scope(function(){
1752
- if (!is_defun) define(name);
1753
- MAP(args, define);
1781
+ return [ this[0], is_defun ? define(name, "defun") : name, args, with_new_scope(function(){
1782
+ if (!is_defun) define(name, "lambda");
1783
+ MAP(args, function(name){ define(name, "arg") });
1754
1784
  return MAP(body, walk);
1755
1785
  })];
1756
1786
  };
1757
1787
 
1788
+ function _vardefs(type) {
1789
+ return function(defs) {
1790
+ MAP(defs, function(d){
1791
+ define(d[0], type);
1792
+ if (d[1]) reference(d[0]);
1793
+ });
1794
+ };
1795
+ };
1796
+
1758
1797
  return with_new_scope(function(){
1759
1798
  // process AST
1760
1799
  var ret = w.with_walkers({
1761
1800
  "function": _lambda,
1762
1801
  "defun": _lambda,
1802
+ "label": function(name, stat) { define(name, "label") },
1803
+ "break": function(label) { if (label) reference(label) },
1804
+ "continue": function(label) { if (label) reference(label) },
1763
1805
  "with": function(expr, block) {
1764
1806
  for (var s = current_scope; s; s = s.parent)
1765
1807
  s.uses_with = true;
1766
1808
  },
1767
- "var": function(defs) {
1768
- MAP(defs, function(d){ define(d[0]) });
1769
- },
1770
- "const": function(defs) {
1771
- MAP(defs, function(d){ define(d[0]) });
1772
- },
1809
+ "var": _vardefs("var"),
1810
+ "const": _vardefs("const"),
1773
1811
  "try": function(t, c, f) {
1774
1812
  if (c != null) return [
1775
1813
  this[0],
1776
1814
  MAP(t, walk),
1777
- [ define(c[0]), MAP(c[1], walk) ],
1815
+ [ define(c[0], "catch"), MAP(c[1], walk) ],
1778
1816
  f != null ? MAP(f, walk) : null
1779
1817
  ];
1780
1818
  },
@@ -1849,19 +1887,30 @@ function ast_mangle(ast, options) {
1849
1887
  };
1850
1888
 
1851
1889
  function _lambda(name, args, body) {
1852
- var is_defun = this[0] == "defun";
1853
- if (is_defun && name) name = get_mangled(name);
1890
+ var is_defun = this[0] == "defun", extra;
1891
+ if (name) {
1892
+ if (is_defun) name = get_mangled(name);
1893
+ else {
1894
+ extra = {};
1895
+ if (!(scope.uses_eval || scope.uses_with))
1896
+ name = extra[name] = scope.next_mangled();
1897
+ else
1898
+ extra[name] = name;
1899
+ }
1900
+ }
1854
1901
  body = with_scope(body.scope, function(){
1855
- if (!is_defun && name) name = get_mangled(name);
1856
1902
  args = MAP(args, function(name){ return get_mangled(name) });
1857
1903
  return MAP(body, walk);
1858
- });
1904
+ }, extra);
1859
1905
  return [ this[0], name, args, body ];
1860
1906
  };
1861
1907
 
1862
- function with_scope(s, cont) {
1908
+ function with_scope(s, cont, extra) {
1863
1909
  var _scope = scope;
1864
1910
  scope = s;
1911
+ if (extra) for (var i in extra) if (HOP(extra, i)) {
1912
+ s.set_mangle(i, extra[i]);
1913
+ }
1865
1914
  for (var i in s.names) if (HOP(s.names, i)) {
1866
1915
  get_mangled(i, true);
1867
1916
  }
@@ -1891,6 +1940,9 @@ function ast_mangle(ast, options) {
1891
1940
  }
1892
1941
  return ast;
1893
1942
  },
1943
+ "label": function(label, stat) { return [ this[0], get_mangled(label), walk(stat) ] },
1944
+ "break": function(label) { if (label) return [ this[0], get_mangled(label) ] },
1945
+ "continue": function(label) { if (label) return [ this[0], get_mangled(label) ] },
1894
1946
  "var": _vardefs,
1895
1947
  "const": _vardefs,
1896
1948
  "name": function(name) {
@@ -1937,10 +1989,12 @@ function last_stat(b) {
1937
1989
  }
1938
1990
 
1939
1991
  function aborts(t) {
1940
- if (t) {
1941
- t = last_stat(t);
1942
- if (t[0] == "return" || t[0] == "break" || t[0] == "continue" || t[0] == "throw")
1943
- return true;
1992
+ if (t) switch (last_stat(t)[0]) {
1993
+ case "return":
1994
+ case "break":
1995
+ case "continue":
1996
+ case "throw":
1997
+ return true;
1944
1998
  }
1945
1999
  };
1946
2000
 
@@ -2095,6 +2149,218 @@ function warn_unreachable(ast) {
2095
2149
  warn("Dropping unreachable code: " + gen_code(ast, true));
2096
2150
  };
2097
2151
 
2152
+ function prepare_ifs(ast) {
2153
+ var w = ast_walker(), walk = w.walk;
2154
+ // In this first pass, we rewrite ifs which abort with no else with an
2155
+ // if-else. For example:
2156
+ //
2157
+ // if (x) {
2158
+ // blah();
2159
+ // return y;
2160
+ // }
2161
+ // foobar();
2162
+ //
2163
+ // is rewritten into:
2164
+ //
2165
+ // if (x) {
2166
+ // blah();
2167
+ // return y;
2168
+ // } else {
2169
+ // foobar();
2170
+ // }
2171
+ function redo_if(statements) {
2172
+ statements = MAP(statements, walk);
2173
+
2174
+ for (var i = 0; i < statements.length; ++i) {
2175
+ var fi = statements[i];
2176
+ if (fi[0] != "if") continue;
2177
+
2178
+ if (fi[3] && walk(fi[3])) continue;
2179
+
2180
+ var t = walk(fi[2]);
2181
+ if (!aborts(t)) continue;
2182
+
2183
+ var conditional = walk(fi[1]);
2184
+
2185
+ var e_body = statements.slice(i + 1);
2186
+ var e = e_body.length == 1 ? e_body[0] : [ "block", e_body ];
2187
+
2188
+ var ret = statements.slice(0, i).concat([ [
2189
+ fi[0], // "if"
2190
+ conditional, // conditional
2191
+ t, // then
2192
+ e // else
2193
+ ] ]);
2194
+
2195
+ return redo_if(ret);
2196
+ }
2197
+
2198
+ return statements;
2199
+ };
2200
+
2201
+ function redo_if_lambda(name, args, body) {
2202
+ body = redo_if(body);
2203
+ return [ this[0], name, args, body ];
2204
+ };
2205
+
2206
+ function redo_if_block(statements) {
2207
+ return [ this[0], statements != null ? redo_if(statements) : null ];
2208
+ };
2209
+
2210
+ return w.with_walkers({
2211
+ "defun": redo_if_lambda,
2212
+ "function": redo_if_lambda,
2213
+ "block": redo_if_block,
2214
+ "splice": redo_if_block,
2215
+ "toplevel": function(statements) {
2216
+ return [ this[0], redo_if(statements) ];
2217
+ },
2218
+ "try": function(t, c, f) {
2219
+ return [
2220
+ this[0],
2221
+ redo_if(t),
2222
+ c != null ? [ c[0], redo_if(c[1]) ] : null,
2223
+ f != null ? redo_if(f) : null
2224
+ ];
2225
+ }
2226
+ }, function() {
2227
+ return walk(ast);
2228
+ });
2229
+ };
2230
+
2231
+ function for_side_effects(ast, handler) {
2232
+ var w = ast_walker(), walk = w.walk;
2233
+ var $stop = {}, $restart = {};
2234
+ function stop() { throw $stop };
2235
+ function restart() { throw $restart };
2236
+ function found(){ return handler.call(this, this, w, stop, restart) };
2237
+ function unary(op) {
2238
+ if (op == "++" || op == "--")
2239
+ return found.apply(this, arguments);
2240
+ };
2241
+ return w.with_walkers({
2242
+ "try": found,
2243
+ "throw": found,
2244
+ "return": found,
2245
+ "new": found,
2246
+ "switch": found,
2247
+ "break": found,
2248
+ "continue": found,
2249
+ "assign": found,
2250
+ "call": found,
2251
+ "if": found,
2252
+ "for": found,
2253
+ "for-in": found,
2254
+ "while": found,
2255
+ "do": found,
2256
+ "return": found,
2257
+ "unary-prefix": unary,
2258
+ "unary-postfix": unary,
2259
+ "defun": found
2260
+ }, function(){
2261
+ while (true) try {
2262
+ walk(ast);
2263
+ break;
2264
+ } catch(ex) {
2265
+ if (ex === $stop) break;
2266
+ if (ex === $restart) continue;
2267
+ throw ex;
2268
+ }
2269
+ });
2270
+ };
2271
+
2272
+ function ast_lift_variables(ast) {
2273
+ var w = ast_walker(), walk = w.walk, scope;
2274
+ function do_body(body, env) {
2275
+ var _scope = scope;
2276
+ scope = env;
2277
+ body = MAP(body, walk);
2278
+ var hash = {}, names = MAP(env.names, function(type, name){
2279
+ if (type != "var") return MAP.skip;
2280
+ if (!env.references(name)) return MAP.skip;
2281
+ hash[name] = true;
2282
+ return [ name ];
2283
+ });
2284
+ if (names.length > 0) {
2285
+ // looking for assignments to any of these variables.
2286
+ // we can save considerable space by moving the definitions
2287
+ // in the var declaration.
2288
+ for_side_effects([ "block", body ], function(ast, walker, stop, restart) {
2289
+ if (ast[0] == "assign"
2290
+ && ast[1] === true
2291
+ && ast[2][0] == "name"
2292
+ && HOP(hash, ast[2][1])) {
2293
+ // insert the definition into the var declaration
2294
+ for (var i = names.length; --i >= 0;) {
2295
+ if (names[i][0] == ast[2][1]) {
2296
+ if (names[i][1]) // this name already defined, we must stop
2297
+ stop();
2298
+ names[i][1] = ast[3]; // definition
2299
+ names.push(names.splice(i, 1)[0]);
2300
+ break;
2301
+ }
2302
+ }
2303
+ // remove this assignment from the AST.
2304
+ var p = walker.parent();
2305
+ if (p[0] == "seq") {
2306
+ var a = p[2];
2307
+ a.unshift(0, p.length);
2308
+ p.splice.apply(p, a);
2309
+ }
2310
+ else if (p[0] == "stat") {
2311
+ p.splice(0, p.length, "block"); // empty statement
2312
+ }
2313
+ else {
2314
+ stop();
2315
+ }
2316
+ restart();
2317
+ }
2318
+ stop();
2319
+ });
2320
+ body.unshift([ "var", names ]);
2321
+ }
2322
+ scope = _scope;
2323
+ return body;
2324
+ };
2325
+ function _vardefs(defs) {
2326
+ var ret = null;
2327
+ for (var i = defs.length; --i >= 0;) {
2328
+ var d = defs[i];
2329
+ if (!d[1]) continue;
2330
+ d = [ "assign", true, [ "name", d[0] ], d[1] ];
2331
+ if (ret == null) ret = d;
2332
+ else ret = [ "seq", d, ret ];
2333
+ }
2334
+ if (ret == null) {
2335
+ if (w.parent()[0] == "for-in")
2336
+ return [ "name", defs[0][0] ];
2337
+ return MAP.skip;
2338
+ }
2339
+ return [ "stat", ret ];
2340
+ };
2341
+ function _toplevel(body) {
2342
+ return [ this[0], do_body(body, this.scope) ];
2343
+ };
2344
+ return w.with_walkers({
2345
+ "function": function(name, args, body){
2346
+ for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
2347
+ args.pop();
2348
+ if (!body.scope.references(name)) name = null;
2349
+ return [ this[0], name, args, do_body(body, body.scope) ];
2350
+ },
2351
+ "defun": function(name, args, body){
2352
+ if (!scope.references(name)) return MAP.skip;
2353
+ for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
2354
+ args.pop();
2355
+ return [ this[0], name, args, do_body(body, body.scope) ];
2356
+ },
2357
+ "var": _vardefs,
2358
+ "toplevel": _toplevel
2359
+ }, function(){
2360
+ return walk(ast_add_scope(ast));
2361
+ });
2362
+ };
2363
+
2098
2364
  function ast_squeeze(ast, options) {
2099
2365
  options = defaults(options, {
2100
2366
  make_seqs : true,
@@ -2159,15 +2425,14 @@ function ast_squeeze(ast, options) {
2159
2425
  function _lambda(name, args, body) {
2160
2426
  var is_defun = this[0] == "defun";
2161
2427
  body = with_scope(body.scope, function(){
2162
- var ret = tighten(MAP(body, walk), "lambda");
2163
- if (!is_defun && name && !HOP(scope.refs, name))
2428
+ var ret = tighten(body, "lambda");
2429
+ if (!is_defun && name && !scope.references(name))
2164
2430
  name = null;
2165
2431
  return ret;
2166
2432
  });
2167
2433
  return [ this[0], name, args, body ];
2168
2434
  };
2169
2435
 
2170
- // we get here for blocks that have been already transformed.
2171
2436
  // this function does a few things:
2172
2437
  // 1. discard useless blocks
2173
2438
  // 2. join consecutive var declarations
@@ -2175,6 +2440,8 @@ function ast_squeeze(ast, options) {
2175
2440
  // 4. transform consecutive statements using the comma operator
2176
2441
  // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... }
2177
2442
  function tighten(statements, block_type) {
2443
+ statements = MAP(statements, walk);
2444
+
2178
2445
  statements = statements.reduce(function(a, stat){
2179
2446
  if (stat[0] == "block") {
2180
2447
  if (stat[1]) {
@@ -2202,7 +2469,17 @@ function ast_squeeze(ast, options) {
2202
2469
  if (options.dead_code) statements = (function(a, has_quit){
2203
2470
  statements.forEach(function(st){
2204
2471
  if (has_quit) {
2205
- if (member(st[0], [ "function", "defun" , "var", "const" ])) {
2472
+ if (st[0] == "function" || st[0] == "defun") {
2473
+ a.push(st);
2474
+ }
2475
+ else if (st[0] == "var" || st[0] == "const") {
2476
+ if (!options.no_warnings)
2477
+ warn("Variables declared in unreachable code");
2478
+ st[1] = MAP(st[1], function(def){
2479
+ if (def[1] && !options.no_warnings)
2480
+ warn_unreachable([ "assign", true, [ "name", def[0] ], def[1] ]);
2481
+ return [ def[0] ];
2482
+ });
2206
2483
  a.push(st);
2207
2484
  }
2208
2485
  else if (!options.no_warnings)
@@ -2226,27 +2503,38 @@ function ast_squeeze(ast, options) {
2226
2503
  prev = cur;
2227
2504
  }
2228
2505
  });
2506
+ if (a.length >= 2
2507
+ && a[a.length-2][0] == "stat"
2508
+ && (a[a.length-1][0] == "return" || a[a.length-1][0] == "throw")
2509
+ && a[a.length-1][1])
2510
+ {
2511
+ a.splice(a.length - 2, 2,
2512
+ [ a[a.length-1][0],
2513
+ [ "seq", a[a.length-2][1], a[a.length-1][1] ]]);
2514
+ }
2229
2515
  return a;
2230
2516
  })([]);
2231
2517
 
2232
- if (block_type == "lambda") statements = (function(i, a, stat){
2233
- while (i < statements.length) {
2234
- stat = statements[i++];
2235
- if (stat[0] == "if" && !stat[3]) {
2236
- if (stat[2][0] == "return" && stat[2][1] == null) {
2237
- a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
2238
- break;
2239
- }
2240
- var last = last_stat(stat[2]);
2241
- if (last[0] == "return" && last[1] == null) {
2242
- a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ]));
2243
- break;
2244
- }
2245
- }
2246
- a.push(stat);
2247
- }
2248
- return a;
2249
- })(0, []);
2518
+ // this increases jQuery by 1K. Probably not such a good idea after all..
2519
+ // part of this is done in prepare_ifs anyway.
2520
+ // if (block_type == "lambda") statements = (function(i, a, stat){
2521
+ // while (i < statements.length) {
2522
+ // stat = statements[i++];
2523
+ // if (stat[0] == "if" && !stat[3]) {
2524
+ // if (stat[2][0] == "return" && stat[2][1] == null) {
2525
+ // a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
2526
+ // break;
2527
+ // }
2528
+ // var last = last_stat(stat[2]);
2529
+ // if (last[0] == "return" && last[1] == null) {
2530
+ // a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ]));
2531
+ // break;
2532
+ // }
2533
+ // }
2534
+ // a.push(stat);
2535
+ // }
2536
+ // return a;
2537
+ // })(0, []);
2250
2538
 
2251
2539
  return statements;
2252
2540
  };
@@ -2355,13 +2643,13 @@ function ast_squeeze(ast, options) {
2355
2643
  "if": make_if,
2356
2644
  "toplevel": function(body) {
2357
2645
  return [ "toplevel", with_scope(this.scope, function(){
2358
- return tighten(MAP(body, walk));
2646
+ return tighten(body);
2359
2647
  }) ];
2360
2648
  },
2361
2649
  "switch": function(expr, body) {
2362
2650
  var last = body.length - 1;
2363
2651
  return [ "switch", walk(expr), MAP(body, function(branch, i){
2364
- var block = tighten(MAP(branch[1], walk));
2652
+ var block = tighten(branch[1]);
2365
2653
  if (i == last && block.length > 0) {
2366
2654
  var node = block[block.length - 1];
2367
2655
  if (node[0] == "break" && !node[1])
@@ -2373,7 +2661,7 @@ function ast_squeeze(ast, options) {
2373
2661
  "function": _lambda,
2374
2662
  "defun": _lambda,
2375
2663
  "block": function(body) {
2376
- if (body) return rmblock([ "block", tighten(MAP(body, walk)) ]);
2664
+ if (body) return rmblock([ "block", tighten(body) ]);
2377
2665
  },
2378
2666
  "binary": function(op, left, right) {
2379
2667
  return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){
@@ -2388,9 +2676,9 @@ function ast_squeeze(ast, options) {
2388
2676
  "try": function(t, c, f) {
2389
2677
  return [
2390
2678
  "try",
2391
- tighten(MAP(t, walk)),
2392
- c != null ? [ c[0], tighten(MAP(c[1], walk)) ] : null,
2393
- f != null ? tighten(MAP(f, walk)) : null
2679
+ tighten(t),
2680
+ c != null ? [ c[0], tighten(c[1]) ] : null,
2681
+ f != null ? tighten(f) : null
2394
2682
  ];
2395
2683
  },
2396
2684
  "unary-prefix": function(op, expr) {
@@ -2424,7 +2712,12 @@ function ast_squeeze(ast, options) {
2424
2712
  },
2425
2713
  "while": _do_while
2426
2714
  }, function() {
2427
- return walk(ast_add_scope(ast));
2715
+ for (var i = 0; i < 2; ++i) {
2716
+ ast = prepare_ifs(ast);
2717
+ ast = ast_add_scope(ast);
2718
+ ast = walk(ast);
2719
+ }
2720
+ return ast;
2428
2721
  });
2429
2722
  };
2430
2723
 
@@ -2481,7 +2774,8 @@ function gen_code(ast, options) {
2481
2774
  quote_keys : false,
2482
2775
  space_colon : false,
2483
2776
  beautify : false,
2484
- ascii_only : false
2777
+ ascii_only : false,
2778
+ inline_script: false
2485
2779
  });
2486
2780
  var beautify = !!options.beautify;
2487
2781
  var indentation = 0,
@@ -2489,7 +2783,10 @@ function gen_code(ast, options) {
2489
2783
  space = beautify ? " " : "";
2490
2784
 
2491
2785
  function encode_string(str) {
2492
- return make_string(str, options.ascii_only);
2786
+ var ret = make_string(str, options.ascii_only);
2787
+ if (options.inline_script)
2788
+ ret = ret.replace(/<\x2fscript([>/\t\n\f\r ])/gi, "<\\/script$1");
2789
+ return ret;
2493
2790
  };
2494
2791
 
2495
2792
  function make_name(name) {
@@ -2566,7 +2863,7 @@ function gen_code(ast, options) {
2566
2863
  // we're the first in this "seq" and the
2567
2864
  // parent is "stat", and so on. Messy stuff,
2568
2865
  // but it worths the trouble.
2569
- var a = slice($stack), self = a.pop(), p = a.pop();
2866
+ var a = slice(w.stack()), self = a.pop(), p = a.pop();
2570
2867
  while (p) {
2571
2868
  if (p[0] == "stat") return true;
2572
2869
  if (((p[0] == "seq" || p[0] == "call" || p[0] == "dot" || p[0] == "sub" || p[0] == "conditional") && p[1] === self) ||
@@ -2596,7 +2893,9 @@ function gen_code(ast, options) {
2596
2893
  return best_of(a);
2597
2894
  };
2598
2895
 
2599
- var generators = {
2896
+ var w = ast_walker();
2897
+ var make = w.walk;
2898
+ return w.with_walkers({
2600
2899
  "string": encode_string,
2601
2900
  "num": make_num,
2602
2901
  "name": make_name,
@@ -2605,7 +2904,7 @@ function gen_code(ast, options) {
2605
2904
  .join(newline + newline);
2606
2905
  },
2607
2906
  "splice": function(statements) {
2608
- var parent = $stack[$stack.length - 2][0];
2907
+ var parent = w.parent();
2609
2908
  if (HOP(SPLICE_NEEDS_BRACKETS, parent)) {
2610
2909
  // we need block brackets in this case
2611
2910
  return make_block.apply(this, arguments);
@@ -2745,6 +3044,10 @@ function gen_code(ast, options) {
2745
3044
  !(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) {
2746
3045
  right = "(" + right + ")";
2747
3046
  }
3047
+ else if (!beautify && options.inline_script && (operator == "<" || operator == "<<")
3048
+ && rvalue[0] == "regexp" && /^script/i.test(rvalue[1])) {
3049
+ right = " " + right;
3050
+ }
2748
3051
  return add_spaces([ left, operator, right ]);
2749
3052
  },
2750
3053
  "unary-prefix": function(operator, expr) {
@@ -2775,7 +3078,7 @@ function gen_code(ast, options) {
2775
3078
  // body in p[1][3] and type ("get" / "set") in p[2].
2776
3079
  return indent(make_function(p[0], p[1][2], p[1][3], p[2]));
2777
3080
  }
2778
- var key = p[0], val = make(p[1]);
3081
+ var key = p[0], val = parenthesize(p[1], "seq");
2779
3082
  if (options.quote_keys) {
2780
3083
  key = encode_string(key);
2781
3084
  } else if ((typeof key == "number" || !beautify && +key + "" == key)
@@ -2815,7 +3118,7 @@ function gen_code(ast, options) {
2815
3118
  "atom": function(name) {
2816
3119
  return make_name(name);
2817
3120
  }
2818
- };
3121
+ }, function(){ return make(ast) });
2819
3122
 
2820
3123
  // The squeezer replaces "block"-s that contain only a single
2821
3124
  // statement with the statement itself; technically, the AST
@@ -2830,7 +3133,7 @@ function gen_code(ast, options) {
2830
3133
  // IE croaks with "syntax error" on code like this:
2831
3134
  // if (foo) do ... while(cond); else ...
2832
3135
  // we need block brackets around do/while
2833
- return make([ "block", [ th ]]);
3136
+ return make_block([ th ]);
2834
3137
  }
2835
3138
  var b = th;
2836
3139
  while (true) {
@@ -2857,20 +3160,31 @@ function gen_code(ast, options) {
2857
3160
  return add_spaces([ out, make_block(body) ]);
2858
3161
  };
2859
3162
 
3163
+ function must_has_semicolon(node) {
3164
+ switch (node[0]) {
3165
+ case "with":
3166
+ case "while":
3167
+ return empty(node[2]); // `with' or `while' with empty body?
3168
+ case "for":
3169
+ case "for-in":
3170
+ return empty(node[4]); // `for' with empty body?
3171
+ case "if":
3172
+ if (empty(node[2]) && !node[3]) return true; // `if' with empty `then' and no `else'
3173
+ if (node[3]) {
3174
+ if (empty(node[3])) return true; // `else' present but empty
3175
+ return must_has_semicolon(node[3]); // dive into the `else' branch
3176
+ }
3177
+ return must_has_semicolon(node[2]); // dive into the `then' branch
3178
+ }
3179
+ };
3180
+
2860
3181
  function make_block_statements(statements, noindent) {
2861
3182
  for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) {
2862
3183
  var stat = statements[i];
2863
3184
  var code = make(stat);
2864
3185
  if (code != ";") {
2865
- if (!beautify && i == last) {
2866
- if ((stat[0] == "while" && empty(stat[2])) ||
2867
- (member(stat[0], [ "for", "for-in"] ) && empty(stat[4])) ||
2868
- (stat[0] == "if" && empty(stat[2]) && !stat[3]) ||
2869
- (stat[0] == "if" && stat[3] && empty(stat[3]))) {
2870
- code = code.replace(/;*\s*$/, ";");
2871
- } else {
2872
- code = code.replace(/;+\s*$/, "");
2873
- }
3186
+ if (!beautify && i == last && !must_has_semicolon(stat)) {
3187
+ code = code.replace(/;+\s*$/, "");
2874
3188
  }
2875
3189
  a.push(code);
2876
3190
  }
@@ -2910,20 +3224,6 @@ function gen_code(ast, options) {
2910
3224
  return name;
2911
3225
  };
2912
3226
 
2913
- var $stack = [];
2914
-
2915
- function make(node) {
2916
- var type = node[0];
2917
- var gen = generators[type];
2918
- if (!gen)
2919
- throw new Error("Can't find generator for \"" + type + "\"");
2920
- $stack.push(node);
2921
- var ret = gen.apply(type, node.slice(1));
2922
- $stack.pop();
2923
- return ret;
2924
- };
2925
-
2926
- return make(ast);
2927
3227
  };
2928
3228
 
2929
3229
  function split_lines(code, max_line_length) {
@@ -3008,16 +3308,34 @@ var MAP;
3008
3308
 
3009
3309
  (function(){
3010
3310
  MAP = function(a, f, o) {
3011
- var ret = [];
3012
- for (var i = 0; i < a.length; ++i) {
3311
+ var ret = [], top = [], i;
3312
+ function doit() {
3013
3313
  var val = f.call(o, a[i], i);
3014
- if (val instanceof AtTop) ret.unshift(val.v);
3015
- else ret.push(val);
3016
- }
3017
- return ret;
3314
+ if (val instanceof AtTop) {
3315
+ val = val.v;
3316
+ if (val instanceof Splice) {
3317
+ top.push.apply(top, val.v);
3318
+ } else {
3319
+ top.push(val);
3320
+ }
3321
+ }
3322
+ else if (val != skip) {
3323
+ if (val instanceof Splice) {
3324
+ ret.push.apply(ret, val.v);
3325
+ } else {
3326
+ ret.push(val);
3327
+ }
3328
+ }
3329
+ };
3330
+ if (a instanceof Array) for (i = 0; i < a.length; ++i) doit();
3331
+ else for (i in a) if (HOP(a, i)) doit();
3332
+ return top.concat(ret);
3018
3333
  };
3019
3334
  MAP.at_top = function(val) { return new AtTop(val) };
3335
+ MAP.splice = function(val) { return new Splice(val) };
3336
+ var skip = MAP.skip = {};
3020
3337
  function AtTop(val) { this.v = val };
3338
+ function Splice(val) { this.v = val };
3021
3339
  })();
3022
3340
 
3023
3341
  /* -----[ Exports ]----- */
@@ -3025,6 +3343,7 @@ var MAP;
3025
3343
  exports.ast_walker = ast_walker;
3026
3344
  exports.ast_mangle = ast_mangle;
3027
3345
  exports.ast_squeeze = ast_squeeze;
3346
+ exports.ast_lift_variables = ast_lift_variables;
3028
3347
  exports.gen_code = gen_code;
3029
3348
  exports.ast_add_scope = ast_add_scope;
3030
3349
  exports.set_logger = function(logger) { warn = logger };
data/uglifier.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{uglifier}
8
- s.version = "1.0.0"
8
+ s.version = "1.0.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = [%q{Ville Lautanala}]
12
- s.date = %q{2011-06-21}
11
+ s.authors = ["Ville Lautanala"]
12
+ s.date = %q{2011-08-15}
13
13
  s.email = %q{lautis@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE.txt",
@@ -34,14 +34,15 @@ Gem::Specification.new do |s|
34
34
  "uglifier.gemspec"
35
35
  ]
36
36
  s.homepage = %q{http://github.com/lautis/uglifier}
37
- s.require_paths = [%q{lib}]
38
- s.rubygems_version = %q{1.8.4}
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.6}
39
39
  s.summary = %q{Ruby wrapper for UglifyJS JavaScript compressor}
40
40
 
41
41
  if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
43
  s.specification_version = 3
43
44
 
44
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
45
46
  s.add_runtime_dependency(%q<execjs>, [">= 0.3.0"])
46
47
  s.add_runtime_dependency(%q<multi_json>, [">= 1.0.2"])
47
48
  s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uglifier
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease:
4
+ prerelease: false
6
5
  segments:
7
6
  - 1
8
7
  - 0
9
- - 0
10
- version: 1.0.0
8
+ - 1
9
+ version: 1.0.1
11
10
  platform: ruby
12
11
  authors:
13
12
  - Ville Lautanala
@@ -15,102 +14,91 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2011-06-21 00:00:00 Z
17
+ date: 2011-08-15 00:00:00 +03:00
18
+ default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- type: :runtime
22
- requirement: &id001 !ruby/object:Gem::Requirement
23
- none: false
21
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
22
  requirements:
25
23
  - - ">="
26
24
  - !ruby/object:Gem::Version
27
- hash: 19
28
25
  segments:
29
26
  - 0
30
27
  - 3
31
28
  - 0
32
29
  version: 0.3.0
33
- prerelease: false
34
- version_requirements: *id001
30
+ requirement: *id001
35
31
  name: execjs
36
- - !ruby/object:Gem::Dependency
32
+ prerelease: false
37
33
  type: :runtime
38
- requirement: &id002 !ruby/object:Gem::Requirement
39
- none: false
34
+ - !ruby/object:Gem::Dependency
35
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
36
  requirements:
41
37
  - - ">="
42
38
  - !ruby/object:Gem::Version
43
- hash: 19
44
39
  segments:
45
40
  - 1
46
41
  - 0
47
42
  - 2
48
43
  version: 1.0.2
49
- prerelease: false
50
- version_requirements: *id002
44
+ requirement: *id002
51
45
  name: multi_json
46
+ prerelease: false
47
+ type: :runtime
52
48
  - !ruby/object:Gem::Dependency
53
- type: :development
54
- requirement: &id003 !ruby/object:Gem::Requirement
55
- none: false
49
+ version_requirements: &id003 !ruby/object:Gem::Requirement
56
50
  requirements:
57
51
  - - ~>
58
52
  - !ruby/object:Gem::Version
59
- hash: 23
60
53
  segments:
61
54
  - 2
62
55
  - 6
63
56
  - 0
64
57
  version: 2.6.0
65
- prerelease: false
66
- version_requirements: *id003
58
+ requirement: *id003
67
59
  name: rspec
68
- - !ruby/object:Gem::Dependency
60
+ prerelease: false
69
61
  type: :development
70
- requirement: &id004 !ruby/object:Gem::Requirement
71
- none: false
62
+ - !ruby/object:Gem::Dependency
63
+ version_requirements: &id004 !ruby/object:Gem::Requirement
72
64
  requirements:
73
65
  - - ~>
74
66
  - !ruby/object:Gem::Version
75
- hash: 23
76
67
  segments:
77
68
  - 1
78
69
  - 0
79
70
  - 0
80
71
  version: 1.0.0
81
- prerelease: false
82
- version_requirements: *id004
72
+ requirement: *id004
83
73
  name: bundler
84
- - !ruby/object:Gem::Dependency
74
+ prerelease: false
85
75
  type: :development
86
- requirement: &id005 !ruby/object:Gem::Requirement
87
- none: false
76
+ - !ruby/object:Gem::Dependency
77
+ version_requirements: &id005 !ruby/object:Gem::Requirement
88
78
  requirements:
89
79
  - - ~>
90
80
  - !ruby/object:Gem::Version
91
- hash: 15
92
81
  segments:
93
82
  - 1
94
83
  - 6
95
84
  - 0
96
85
  version: 1.6.0
97
- prerelease: false
98
- version_requirements: *id005
86
+ requirement: *id005
99
87
  name: jeweler
100
- - !ruby/object:Gem::Dependency
88
+ prerelease: false
101
89
  type: :development
102
- requirement: &id006 !ruby/object:Gem::Requirement
103
- none: false
90
+ - !ruby/object:Gem::Dependency
91
+ version_requirements: &id006 !ruby/object:Gem::Requirement
104
92
  requirements:
105
93
  - - ">="
106
94
  - !ruby/object:Gem::Version
107
- hash: 3
108
95
  segments:
109
96
  - 0
110
97
  version: "0"
111
- prerelease: false
112
- version_requirements: *id006
98
+ requirement: *id006
113
99
  name: rcov
100
+ prerelease: false
101
+ type: :development
114
102
  description:
115
103
  email: lautis@gmail.com
116
104
  executables: []
@@ -137,6 +125,7 @@ files:
137
125
  - spec/spec_helper.rb
138
126
  - spec/uglifier_spec.rb
139
127
  - uglifier.gemspec
128
+ has_rdoc: true
140
129
  homepage: http://github.com/lautis/uglifier
141
130
  licenses: []
142
131
 
@@ -146,27 +135,23 @@ rdoc_options: []
146
135
  require_paths:
147
136
  - lib
148
137
  required_ruby_version: !ruby/object:Gem::Requirement
149
- none: false
150
138
  requirements:
151
139
  - - ">="
152
140
  - !ruby/object:Gem::Version
153
- hash: 3
154
141
  segments:
155
142
  - 0
156
143
  version: "0"
157
144
  required_rubygems_version: !ruby/object:Gem::Requirement
158
- none: false
159
145
  requirements:
160
146
  - - ">="
161
147
  - !ruby/object:Gem::Version
162
- hash: 3
163
148
  segments:
164
149
  - 0
165
150
  version: "0"
166
151
  requirements: []
167
152
 
168
153
  rubyforge_project:
169
- rubygems_version: 1.8.4
154
+ rubygems_version: 1.3.6
170
155
  signing_key:
171
156
  specification_version: 3
172
157
  summary: Ruby wrapper for UglifyJS JavaScript compressor