lib/uglify.js in uglifier-1.0.3 vs lib/uglify.js in uglifier-1.0.4
- old
+ new
@@ -237,11 +237,11 @@
"&=",
"&&",
"||"
]);
-var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b"));
+var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
@@ -305,15 +305,11 @@
function JS_Parse_Error(message, line, col, pos) {
this.message = message;
this.line = line;
this.col = col;
this.pos = pos;
- try {
- ({})();
- } catch(ex) {
- this.stack = ex.stack;
- };
+ this.stack = new Error().stack;
};
JS_Parse_Error.prototype.toString = function() {
return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
};
@@ -343,16 +339,16 @@
comments_before : []
};
function peek() { return S.text.charAt(S.pos); };
- function next(signal_eof) {
+ function next(signal_eof, in_string) {
var ch = S.text.charAt(S.pos++);
if (signal_eof && !ch)
throw EX_EOF;
if (ch == "\n") {
- S.newline_before = true;
+ S.newline_before = S.newline_before || !in_string;
++S.line;
S.col = 0;
} else {
++S.col;
}
@@ -445,12 +441,12 @@
} else {
parse_error("Invalid syntax: " + num);
}
};
- function read_escaped_char() {
- var ch = next(true);
+ function read_escaped_char(in_string) {
+ var ch = next(true, in_string);
switch (ch) {
case "n" : return "\n";
case "r" : return "\r";
case "t" : return "\t";
case "b" : return "\b";
@@ -494,11 +490,11 @@
else if (first >= "4" && octal_len <= 1) return ++octal_len;
}
return false;
});
if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
- else ch = read_escaped_char();
+ else ch = read_escaped_char(true);
}
else if (ch == quote) break;
ret += ch;
}
return token("string", ret);
@@ -556,13 +552,13 @@
}
}
return name;
};
- function read_regexp() {
+ function read_regexp(regexp) {
return with_eof_error("Unterminated regular expression", function(){
- var prev_backslash = false, regexp = "", ch, in_class = false;
+ var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (prev_backslash) {
regexp += "\\" + ch;
prev_backslash = false;
} else if (ch == "[") {
in_class = true;
@@ -607,11 +603,11 @@
case "*":
S.comments_before.push(read_multiline_comment());
S.regex_allowed = regex_allowed;
return next_token();
}
- return S.regex_allowed ? read_regexp() : read_operator("/");
+ return S.regex_allowed ? read_regexp("") : read_operator("/");
};
function handle_dot() {
next();
return is_digit(peek())
@@ -638,12 +634,12 @@
else throw ex;
}
};
function next_token(force_regexp) {
- if (force_regexp)
- return read_regexp();
+ if (force_regexp != null)
+ return read_regexp(force_regexp);
skip_whitespace();
start_token();
var ch = peek();
if (!ch) return token("eof");
if (is_digit(ch)) return read_num();
@@ -828,13 +824,13 @@
};
else return parser;
};
var statement = maybe_embed_tokens(function() {
- if (is("operator", "/")) {
+ if (is("operator", "/") || is("operator", "/=")) {
S.peeked = null;
- S.token = S.input(true); // force regexp
+ S.token = S.input(S.token.value.substr(1)); // force regexp
}
switch (S.token.type) {
case "num":
case "string":
case "regexp":
@@ -900,10 +896,12 @@
case "switch":
return as("switch", parenthesised(), switch_block_());
case "throw":
+ if (S.token.nlb)
+ croak("Illegal newline after 'throw'");
return as("throw", prog1(expression, semicolon));
case "try":
return try_();
@@ -1275,11 +1273,11 @@
return expr;
};
function is_assignable(expr) {
if (!exigent_mode) return true;
- switch (expr[0]) {
+ switch (expr[0]+"") {
case "dot":
case "sub":
case "new":
case "call":
return true;
@@ -1349,11 +1347,11 @@
ret[a[i]] = true;
return ret;
};
function slice(a, start) {
- return Array.prototype.slice.call(a, start == null ? 0 : start);
+ return Array.prototype.slice.call(a, start || 0);
};
function characters(str) {
return str.split("");
};
@@ -1455,11 +1453,11 @@
PRECEDENCE = jsp.PRECEDENCE,
OPERATORS = jsp.OPERATORS;
/* -----[ helper for AST traversal ]----- */
-function ast_walker(ast) {
+function ast_walker() {
function _vardefs(defs) {
return [ this[0], MAP(defs, function(def){
var a = [ def[0] ];
if (def.length > 1)
a[1] = walk(def[1]);
@@ -1612,10 +1610,21 @@
} finally {
stack.pop();
}
};
+ function dive(ast) {
+ if (ast == null)
+ return null;
+ try {
+ stack.push(ast);
+ return walkers[ast[0]].apply(ast, ast.slice(1));
+ } finally {
+ stack.pop();
+ }
+ };
+
function with_walkers(walkers, cont){
var save = {}, i;
for (i in walkers) if (HOP(walkers, i)) {
save[i] = user[i];
user[i] = walkers[i];
@@ -1628,10 +1637,11 @@
return ret;
};
return {
walk: walk,
+ dive: dive,
with_walkers: with_walkers,
parent: function() {
return stack[stack.length - 2]; // last one is current node
},
stack: function() {
@@ -2021,25 +2031,10 @@
(expr[0] == "seq"
&& boolean_expr(expr[expr.length - 1]))
);
};
-function make_conditional(c, t, e) {
- var make_real_conditional = function() {
- if (c[0] == "unary-prefix" && c[1] == "!") {
- return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
- } else {
- return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ];
- }
- };
- // shortcut the conditional if the expression has a constant value
- return when_constant(c, function(ast, val){
- warn_unreachable(val ? e : t);
- return (val ? t : e);
- }, make_real_conditional);
-};
-
function empty(b) {
return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
};
function is_string(node) {
@@ -2063,10 +2058,11 @@
case "name":
case "atom":
switch (expr[1]) {
case "true": return true;
case "false": return false;
+ case "null": return null;
}
break;
case "unary-prefix":
switch (expr[1]) {
case "!": return !evaluate(expr[2]);
@@ -2085,10 +2081,11 @@
case "&" : return evaluate(left) & evaluate(right);
case "^" : return evaluate(left) ^ evaluate(right);
case "+" : return evaluate(left) + evaluate(right);
case "*" : return evaluate(left) * evaluate(right);
case "/" : return evaluate(left) / evaluate(right);
+ case "%" : return evaluate(left) % evaluate(right);
case "-" : return evaluate(left) - evaluate(right);
case "<<" : return evaluate(left) << evaluate(right);
case ">>" : return evaluate(left) >> evaluate(right);
case ">>>" : return evaluate(left) >>> evaluate(right);
case "==" : return evaluate(left) == evaluate(right);
@@ -2363,12 +2360,12 @@
function ast_squeeze(ast, options) {
options = defaults(options, {
make_seqs : true,
dead_code : true,
- keep_comps : true,
- no_warnings : false
+ no_warnings : false,
+ keep_comps : true
});
var w = ast_walker(), walk = w.walk, scope;
function negate(c) {
@@ -2401,10 +2398,28 @@
break;
}
return not_c;
};
+ function make_conditional(c, t, e) {
+ var make_real_conditional = function() {
+ if (c[0] == "unary-prefix" && c[1] == "!") {
+ return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
+ } else {
+ return e ? best_of(
+ [ "conditional", c, t, e ],
+ [ "conditional", negate(c), e, t ]
+ ) : [ "binary", "&&", c, t ];
+ }
+ };
+ // shortcut the conditional if the expression has a constant value
+ return when_constant(c, function(ast, val){
+ warn_unreachable(val ? e : t);
+ return (val ? t : e);
+ }, make_real_conditional);
+ };
+
function with_scope(s, cont) {
var _scope = scope;
scope = s;
var ret = cont();
ret.scope = s;
@@ -2540,15 +2555,17 @@
};
function make_if(c, t, e) {
return when_constant(c, function(ast, val){
if (val) {
+ t = walk(t);
warn_unreachable(e);
- return t;
+ return t || [ "block" ];
} else {
+ e = walk(e);
warn_unreachable(t);
- return e;
+ return e || [ "block" ];
}
}, function() {
return make_real_if(c, t, e);
});
};
@@ -2665,11 +2682,19 @@
},
"binary": function(op, left, right) {
return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){
return best_of(walk(c), this);
}, function no() {
- return this;
+ return function(){
+ if(op != "==" && op != "!=") return;
+ var l = walk(left), r = walk(right);
+ if(l && l[0] == "unary-prefix" && l[1] == "!" && l[2][0] == "num")
+ left = ['num', +!l[2][1]];
+ else if (r && r[0] == "unary-prefix" && r[1] == "!" && r[2][0] == "num")
+ right = ['num', +!r[2][1]];
+ return ["binary", op, left, right];
+ }() || this;
});
},
"conditional": function(c, t, e) {
return make_conditional(walk(c), walk(t), walk(e));
},
@@ -2694,25 +2719,22 @@
switch (name) {
case "true": return [ "unary-prefix", "!", [ "num", 0 ]];
case "false": return [ "unary-prefix", "!", [ "num", 1 ]];
}
},
- "new": function(ctor, args) {
- if (ctor[0] == "name" && ctor[1] == "Array" && !scope.has("Array")) {
- if (args.length != 1) {
- return [ "array", args ];
- } else {
- return [ "call", [ "name", "Array" ], args ];
- }
+ "while": _do_while,
+ "assign": function(op, lvalue, rvalue) {
+ lvalue = walk(lvalue);
+ rvalue = walk(rvalue);
+ var okOps = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
+ if (op === true && lvalue[0] === "name" && rvalue[0] === "binary" &&
+ ~okOps.indexOf(rvalue[1]) && rvalue[2][0] === "name" &&
+ rvalue[2][1] === lvalue[1]) {
+ return [ this[0], rvalue[1], lvalue, rvalue[3] ]
}
- },
- "call": function(expr, args) {
- if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
- return [ "array", args ];
- }
- },
- "while": _do_while
+ return [ this[0], op, lvalue, rvalue ];
+ }
}, function() {
for (var i = 0; i < 2; ++i) {
ast = prepare_ifs(ast);
ast = ast_add_scope(ast);
ast = walk(ast);
@@ -2729,11 +2751,12 @@
"object",
"string",
"dot",
"sub",
"call",
- "regexp"
+ "regexp",
+ "defun"
]);
function make_string(str, ascii_only) {
var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
@@ -2879,12 +2902,17 @@
};
function make_num(num) {
var str = num.toString(10), a = [ str.replace(/^0\./, ".") ], m;
if (Math.floor(num) === num) {
- a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
- "0" + num.toString(8)); // same.
+ if (num >= 0) {
+ a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
+ "0" + num.toString(8)); // same.
+ } else {
+ a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
+ "-0" + (-num).toString(8)); // same.
+ }
if ((m = /^(.*?)(0+)$/.exec(num))) {
a.push(m[1] + "e" + m[2].length);
}
} else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
a.push(m[2] + "e-" + (m[1].length + m[2].length),
@@ -2931,11 +2959,13 @@
},
"throw": function(expr) {
return add_spaces([ "throw", make(expr) ]) + ";";
},
"new": function(ctor, args) {
- args = args.length > 0 ? "(" + add_commas(MAP(args, make)) + ")" : "";
+ args = args.length > 0 ? "(" + add_commas(MAP(args, function(expr){
+ return parenthesize(expr, "seq");
+ })) + ")" : "";
return add_spaces([ "new", parenthesize(ctor, "seq", "binary", "conditional", "assign", function(expr){
var w = ast_walker(), has_call = {};
try {
w.with_walkers({
"call": function() { throw has_call },
@@ -2986,11 +3016,11 @@
out += "." + make_name(arguments[i++]);
return out;
},
"call": function(func, args) {
var f = make(func);
- if (needs_parens(func))
+ if (f.charAt(0) != "(" && needs_parens(func))
f = "(" + f + ")";
return f + "(" + add_commas(MAP(args, function(expr){
return parenthesize(expr, "seq");
})) + ")";
},
@@ -3034,11 +3064,12 @@
var left = make(lvalue), right = make(rvalue);
// XXX: I'm pretty sure other cases will bite here.
// we need to be smarter.
// adding parens all the time is the safest bet.
if (member(lvalue[0], [ "assign", "conditional", "seq" ]) ||
- lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]]) {
+ lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]] ||
+ lvalue[0] == "function" && needs_parens(this)) {
left = "(" + left + ")";
}
if (member(rvalue[0], [ "assign", "conditional", "seq" ]) ||
rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]] &&
!(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) {
@@ -3067,13 +3098,14 @@
if (needs_parens(expr))
hash = "(" + hash + ")";
return hash + "[" + make(subscript) + "]";
},
"object": function(props) {
+ var obj_needs_parens = needs_parens(this);
if (props.length == 0)
- return "{}";
- return "{" + newline + with_indent(function(){
+ return obj_needs_parens ? "({})" : "{}";
+ var out = "{" + newline + with_indent(function(){
return MAP(props, function(p){
if (p.length == 3) {
// getter/setter. The name is in p[0], the arg.list in p[1][2], the
// body in p[1][3] and type ("get" / "set") in p[2].
return indent(make_function(p[0], p[1][2], p[1][3], p[2]));
@@ -3090,18 +3122,19 @@
return indent(add_spaces(beautify && options.space_colon
? [ key, ":", val ]
: [ key + ":", val ]));
}).join("," + newline);
}) + newline + indent("}");
+ return obj_needs_parens ? "(" + out + ")" : out;
},
"regexp": function(rx, mods) {
return "/" + rx + "/" + mods;
},
"array": function(elements) {
if (elements.length == 0) return "[]";
- return add_spaces([ "[", add_commas(MAP(elements, function(el){
- if (!beautify && el[0] == "atom" && el[1] == "undefined") return "";
+ return add_spaces([ "[", add_commas(MAP(elements, function(el, i){
+ if (!beautify && el[0] == "atom" && el[1] == "undefined") return i === elements.length - 1 ? "," : "";
return parenthesize(el, "seq");
})), "]" ]);
},
"stat": function(stmt) {
return make(stmt).replace(/;*\s*$/, ";");
@@ -3126,10 +3159,11 @@
// IF having an ELSE clause where the THEN clause ends in an
// IF *without* an ELSE block (then the outer ELSE would refer
// to the inner IF). This function checks for this case and
// adds the block brackets if needed.
function make_then(th) {
+ if (th == null) return ";";
if (th[0] == "do") {
// https://github.com/mishoo/UglifyJS/issues/#issue/57
// IE croaks with "syntax error" on code like this:
// if (foo) do ... while(cond); else ...
// we need block brackets around do/while
@@ -3155,11 +3189,12 @@
var out = keyword || "function";
if (name) {
out += " " + make_name(name);
}
out += "(" + add_commas(MAP(args, make_name)) + ")";
- return add_spaces([ out, make_block(body) ]);
+ out = add_spaces([ out, make_block(body) ]);
+ return needs_parens(this) ? "(" + out + ")" : out;
};
function must_has_semicolon(node) {
switch (node[0]) {
case "with":
@@ -3355,23 +3390,52 @@
exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;
}, "squeeze-more": function(exports, require, module) {var jsp = require("./parse-js"),
pro = require("./process"),
slice = jsp.slice,
member = jsp.member,
+ curry = jsp.curry,
+ MAP = pro.MAP,
PRECEDENCE = jsp.PRECEDENCE,
OPERATORS = jsp.OPERATORS;
function ast_squeeze_more(ast) {
- var w = pro.ast_walker(), walk = w.walk;
+ var w = pro.ast_walker(), walk = w.walk, scope;
+ function with_scope(s, cont) {
+ var save = scope, ret;
+ scope = s;
+ ret = cont();
+ scope = save;
+ return ret;
+ };
+ function _lambda(name, args, body) {
+ return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
+ };
return w.with_walkers({
+ "toplevel": function(body) {
+ return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
+ },
+ "function": _lambda,
+ "defun": _lambda,
+ "new": function(ctor, args) {
+ if (ctor[0] == "name" && ctor[1] == "Array" && !scope.has("Array")) {
+ if (args.length != 1) {
+ return [ "array", args ];
+ } else {
+ return walk([ "call", [ "name", "Array" ], args ]);
+ }
+ }
+ },
"call": function(expr, args) {
if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
// foo.toString() ==> foo+""
return [ "binary", "+", expr[1], [ "string", "" ]];
}
+ if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
+ return [ "array", args ];
+ }
}
}, function() {
- return walk(ast);
+ return walk(pro.ast_add_scope(ast));
});
};
exports.ast_squeeze_more = ast_squeeze_more;
}});