vendor/uglifyjs/lib/process.js in uglifier-0.2.0 vs vendor/uglifyjs/lib/process.js in uglifier-0.3.0
- old
+ new
@@ -25,48 +25,53 @@
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
- Distributed under a ZLIB license:
+ Distributed under the BSD license:
Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any
- damages arising from the use of this software.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
- Permission is granted to anyone to use this software for any
- purpose, including commercial applications, and to alter it and
- redistribute it freely, subject to the following restrictions:
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
- 1. The origin of this software must not be misrepresented; you must
- not claim that you wrote the original software. If you use this
- software in a product, an acknowledgment in the product
- documentation would be appreciated but is not required.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
- 2. Altered source versions must be plainly marked as such, and must
- not be misrepresented as being the original software.
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
- 3. This notice may not be removed or altered from any source
- distribution.
-
***********************************************************************/
var jsp = require("./parse-js"),
slice = jsp.slice,
member = jsp.member,
PRECEDENCE = jsp.PRECEDENCE,
OPERATORS = jsp.OPERATORS;
-var sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
-
/* -----[ helper for AST traversal ]----- */
function ast_walker(ast) {
function _vardefs(defs) {
- return defs.map(function(def){
+ return MAP(defs, function(def){
var a = [ def[0] ];
if (def.length > 1)
a[1] = walk(def[1]);
return a;
});
@@ -80,16 +85,16 @@
},
"name": function(name) {
return [ "name", name ];
},
"toplevel": function(statements) {
- return [ "toplevel", statements.map(walk) ];
+ return [ "toplevel", MAP(statements, walk) ];
},
"block": function(statements) {
var out = [ "block" ];
if (statements != null)
- out.push(statements.map(walk));
+ out.push(MAP(statements, walk));
return out;
},
"var": function(defs) {
return [ "var", _vardefs(defs) ];
},
@@ -97,25 +102,25 @@
return [ "const", _vardefs(defs) ];
},
"try": function(t, c, f) {
return [
"try",
- t.map(walk),
- c != null ? [ c[0], c[1].map(walk) ] : null,
- f != null ? f.map(walk) : null
+ MAP(t, walk),
+ c != null ? [ c[0], MAP(c[1], walk) ] : null,
+ f != null ? MAP(f, walk) : null
];
},
"throw": function(expr) {
return [ "throw", walk(expr) ];
},
"new": function(ctor, args) {
- return [ "new", walk(ctor), args.map(walk) ];
+ return [ "new", walk(ctor), MAP(args, walk) ];
},
"switch": function(expr, body) {
- return [ "switch", walk(expr), body.map(function(branch){
+ return [ "switch", walk(expr), MAP(body, function(branch){
return [ branch[0] ? walk(branch[0]) : null,
- branch[1].map(walk) ];
+ MAP(branch[1], walk) ];
}) ];
},
"break": function(label) {
return [ "break", label ];
},
@@ -130,17 +135,17 @@
},
"dot": function(expr) {
return [ "dot", walk(expr) ].concat(slice(arguments, 1));
},
"call": function(expr, args) {
- return [ "call", walk(expr), args.map(walk) ];
+ return [ "call", walk(expr), MAP(args, walk) ];
},
"function": function(name, args, body) {
- return [ "function", name, args.slice(), body.map(walk) ];
+ return [ "function", name, args.slice(), MAP(body, walk) ];
},
"defun": function(name, args, body) {
- return [ "defun", name, args.slice(), body.map(walk) ];
+ return [ "defun", name, args.slice(), MAP(body, walk) ];
},
"if": function(conditional, t, e) {
return [ "if", walk(conditional), walk(t), walk(e) ];
},
"for": function(init, cond, step, block) {
@@ -169,25 +174,27 @@
},
"sub": function(expr, subscript) {
return [ "sub", walk(expr), walk(subscript) ];
},
"object": function(props) {
- return [ "object", props.map(function(p){
- return [ p[0], walk(p[1]) ];
+ return [ "object", MAP(props, function(p){
+ return p.length == 2
+ ? [ p[0], walk(p[1]) ]
+ : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
}) ];
},
"regexp": function(rx, mods) {
return [ "regexp", rx, mods ];
},
"array": function(elements) {
- return [ "array", elements.map(walk) ];
+ return [ "array", MAP(elements, walk) ];
},
"stat": function(stat) {
return [ "stat", walk(stat) ];
},
"seq": function() {
- return [ "seq" ].concat(slice(arguments).map(walk));
+ return [ "seq" ].concat(MAP(slice(arguments), walk));
},
"label": function(name, block) {
return [ "label", name, walk(block) ];
},
"with": function(expr, block) {
@@ -223,17 +230,16 @@
var save = {}, i;
for (i in walkers) if (HOP(walkers, i)) {
save[i] = user[i];
user[i] = walkers[i];
}
- try { return cont(); }
- finally {
- for (i in save) if (HOP(save, i)) {
- if (!save[i]) delete user[i];
- else user[i] = save[i];
- }
+ var ret = cont();
+ for (i in save) if (HOP(save, i)) {
+ if (!save[i]) delete user[i];
+ else user[i] = save[i];
}
+ return ret;
};
return {
walk: walk,
with_walkers: with_walkers,
@@ -375,12 +381,12 @@
current_scope.refs[name] = true;
};
function _lambda(name, args, body) {
return [ this[0], define(name), args, with_new_scope(function(){
- args.map(define);
- return body.map(walk);
+ MAP(args, define);
+ return MAP(body, walk);
})];
};
return with_new_scope(function(){
// process AST
@@ -390,21 +396,21 @@
"with": function(expr, block) {
for (var s = current_scope; s; s = s.parent)
s.uses_with = true;
},
"var": function(defs) {
- defs.map(function(d){ define(d[0]) });
+ MAP(defs, function(d){ define(d[0]) });
},
"const": function(defs) {
- defs.map(function(d){ define(d[0]) });
+ MAP(defs, function(d){ define(d[0]) });
},
"try": function(t, c, f) {
if (c != null) return [
"try",
- t.map(walk),
- [ define(c[0]), c[1].map(walk) ],
- f != null ? f.map(walk) : null
+ MAP(t, walk),
+ [ define(c[0]), MAP(c[1], walk) ],
+ f != null ? MAP(f, walk) : null
];
},
"name": function(name) {
if (name == "eval")
having_eval.push(current_scope);
@@ -422,11 +428,11 @@
// that names can be used prior to their definition.
// scopes where eval was detected and their parents
// are marked with uses_eval, unless they define the
// "eval" name.
- having_eval.map(function(scope){
+ MAP(having_eval, function(scope){
if (!scope.has("eval")) while (scope) {
scope.uses_eval = true;
scope = scope.parent;
}
});
@@ -464,35 +470,48 @@
};
function _lambda(name, args, body) {
if (name) name = get_mangled(name);
body = with_scope(body.scope, function(){
- args = args.map(function(name){ return get_mangled(name) });
- return body.map(walk);
+ args = MAP(args, function(name){ return get_mangled(name) });
+ return MAP(body, walk);
});
return [ this[0], name, args, body ];
};
function with_scope(s, cont) {
var _scope = scope;
scope = s;
for (var i in s.names) if (HOP(s.names, i)) {
get_mangled(i, true);
}
- try { var ret = cont(); ret.scope = s; return ret; }
- finally { scope = _scope; };
+ var ret = cont();
+ ret.scope = s;
+ scope = _scope;
+ return ret;
};
function _vardefs(defs) {
- return defs.map(function(d){
+ return MAP(defs, function(d){
return [ get_mangled(d[0]), walk(d[1]) ];
});
};
return w.with_walkers({
"function": _lambda,
- "defun": _lambda,
+ "defun": function() {
+ // move function declarations to the top when
+ // they are not in some block.
+ var ast = _lambda.apply(this, arguments);
+ switch (w.parent()[0]) {
+ case "toplevel":
+ case "function":
+ case "defun":
+ return MAP.at_top(ast);
+ }
+ return ast;
+ },
"var": function(defs) {
return [ "var", _vardefs(defs) ];
},
"const": function(defs) {
return [ "const", _vardefs(defs) ];
@@ -500,17 +519,17 @@
"name": function(name) {
return [ "name", get_mangled(name) ];
},
"try": function(t, c, f) {
return [ "try",
- t.map(walk),
- c != null ? [ get_mangled(c[0]), c[1].map(walk) ] : null,
- f != null ? f.map(walk) : null ];
+ MAP(t, walk),
+ c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
+ f != null ? MAP(f, walk) : null ];
},
"toplevel": function(body) {
return with_scope(this.scope, function(){
- return [ "toplevel", body.map(walk) ];
+ return [ "toplevel", MAP(body, walk) ];
});
},
"for-in": function(has_var, name, obj, stat) {
return [ "for-in", has_var, get_mangled(name), walk(obj), walk(stat) ];
}
@@ -528,13 +547,11 @@
- if (cond) foo(); ==> cond&&foo();
- if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); // also for throw
- if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
]----- */
-function warn(msg) {
- sys.debug(msg);
-};
+var warn = function(){};
function best_of(ast1, ast2) {
return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
};
@@ -554,17 +571,11 @@
function negate(c) {
var not_c = [ "unary-prefix", "!", c ];
switch (c[0]) {
case "unary-prefix":
- return c[1] == "!" ? c[2] : c;
- case "atom":
- switch (c[1]) {
- case "false": return [ "num", 0 ];
- case "true": return [ "num", 1 ];
- }
- break;
+ return c[1] == "!" ? c[2] : not_c;
case "binary":
var op = c[1], left = c[2], right = c[3];
switch (op) {
case "<=": return [ "binary", ">", left, right ];
case "<": return [ "binary", ">=", left, right ];
@@ -600,12 +611,21 @@
dead_code : true,
no_warnings : false,
extra : false
});
- var w = ast_walker(), walk = w.walk;
+ var w = ast_walker(), walk = w.walk, scope;
+ function with_scope(s, cont) {
+ var _scope = scope;
+ scope = s;
+ var ret = cont();
+ ret.scope = s;
+ scope = _scope;
+ return ret;
+ };
+
function is_constant(node) {
return node[0] == "string" || node[0] == "num";
};
function find_first_execute(node) {
@@ -665,11 +685,11 @@
return block;
};
function clone(obj) {
if (obj && obj.constructor == Array)
- return obj.map(clone);
+ return MAP(obj, clone);
return obj;
};
function make_seq_to_statements(node) {
if (node[0] != "seq") {
@@ -688,11 +708,13 @@
return ret;
};
function _lambda(name, args, body) {
- return [ this[0], name, args, tighten(body.map(walk), "lambda") ];
+ return [ this[0], name, args, with_scope(body.scope, function(){
+ return tighten(MAP(body, walk), "lambda");
+ }) ];
};
// we get here for blocks that have been already transformed.
// this function does a few things:
// 1. discard useless blocks
@@ -923,16 +945,18 @@
}
}
},
"if": make_if,
"toplevel": function(body) {
- return [ "toplevel", tighten(body.map(walk)) ];
+ return [ "toplevel", with_scope(this.scope, function(){
+ return tighten(MAP(body, walk));
+ }) ];
},
"switch": function(expr, body) {
var last = body.length - 1;
- return [ "switch", walk(expr), body.map(function(branch, i){
- var block = tighten(branch[1].map(walk));
+ return [ "switch", walk(expr), MAP(body, function(branch, i){
+ var block = tighten(MAP(branch[1], walk));
if (i == last && block.length > 0) {
var node = block[block.length - 1];
if (node[0] == "break" && !node[1])
block.pop();
}
@@ -940,11 +964,11 @@
}) ];
},
"function": _lambda,
"defun": _lambda,
"block": function(body) {
- if (body) return rmblock([ "block", tighten(body.map(walk)) ]);
+ if (body) return rmblock([ "block", tighten(MAP(body, walk)) ]);
},
"binary": function(op, left, right) {
left = walk(left);
right = walk(right);
var best = [ "binary", op, left, right ];
@@ -973,21 +997,49 @@
return make_conditional(walk(c), walk(t), walk(e));
},
"try": function(t, c, f) {
return [
"try",
- tighten(t.map(walk)),
- c != null ? [ c[0], tighten(c[1].map(walk)) ] : null,
- f != null ? tighten(f.map(walk)) : null
+ tighten(MAP(t, walk)),
+ c != null ? [ c[0], tighten(MAP(c[1], walk)) ] : null,
+ f != null ? tighten(MAP(f, walk)) : null
];
},
"unary-prefix": function(op, cond) {
- if (op == "!")
+ if (op == "!") {
+ cond = walk(cond);
+ if (cond[0] == "unary-prefix" && cond[1] == "!") {
+ var p = w.parent();
+ if (p[0] == "unary-prefix" && p[1] == "!")
+ return cond[2];
+ return [ "unary-prefix", "!", cond ];
+ }
return best_of(this, negate(cond));
+ }
+ },
+ "name": function(name) {
+ 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 ];
+ }
+ }
+ },
+ "call": function(expr, args) {
+ if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
+ return [ "array", args ];
+ }
}
}, function() {
- return walk(ast);
+ return walk(ast_add_scope(ast));
});
};
/* -----[ re-generate code from the AST ]----- */
@@ -999,10 +1051,32 @@
"sub",
"call",
"regexp"
]);
+function make_string(str) {
+ var dq = 0, sq = 0;
+ str = str.replace(/[\\\b\f\n\r\t\x22\x27]/g, function(s){
+ switch (s) {
+ case "\\": return "\\\\";
+ case "\b": return "\\b";
+ case "\f": return "\\f";
+ case "\n": return "\\n";
+ case "\r": return "\\r";
+ case "\t": return "\\t";
+ case '"': ++dq; return '"';
+ case "'": ++sq; return "'";
+ }
+ return s;
+ });
+ if (dq > sq) {
+ return "'" + str.replace(/\x27/g, "\\'") + "'";
+ } else {
+ return '"' + str.replace(/\x22/g, '\\"') + '"';
+ }
+};
+
function gen_code(ast, beautify) {
if (beautify) beautify = defaults(beautify, {
indent_start : 0,
indent_level : 4,
quote_keys : false,
@@ -1118,14 +1192,14 @@
return make_block_statements(statements)
.join(newline + newline);
},
"block": make_block,
"var": function(defs) {
- return "var " + add_commas(defs.map(make_1vardef)) + ";";
+ return "var " + add_commas(MAP(defs, make_1vardef)) + ";";
},
"const": function(defs) {
- return "const " + add_commas(defs.map(make_1vardef)) + ";";
+ return "const " + add_commas(MAP(defs, make_1vardef)) + ";";
},
"try": function(tr, ca, fi) {
var out = [ "try", make_block(tr) ];
if (ca) out.push("catch", "(" + ca[0] + ")", make_block(ca[1]));
if (fi) out.push("finally", make_block(fi));
@@ -1133,11 +1207,11 @@
},
"throw": function(expr) {
return add_spaces([ "throw", make(expr) ]) + ";";
},
"new": function(ctor, args) {
- args = args.length > 0 ? "(" + add_commas(args.map(make)) + ")" : "";
+ args = args.length > 0 ? "(" + add_commas(MAP(args, make)) + ")" : "";
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 },
@@ -1187,11 +1261,13 @@
},
"call": function(func, args) {
var f = make(func);
if (needs_parens(func))
f = "(" + f + ")";
- return f + "(" + add_commas(args.map(make)) + ")";
+ return f + "(" + add_commas(MAP(args, function(expr){
+ return parenthesize(expr, "seq");
+ })) + ")";
},
"function": make_function,
"defun": make_function,
"if": function(co, th, el) {
var out = [ "if", "(" + make(co) + ")", el ? make_then(th) : make(th) ];
@@ -1263,11 +1339,16 @@
},
"object": function(props) {
if (props.length == 0)
return "{}";
return "{" + newline + with_indent(function(){
- return props.map(function(p){
+ 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]));
+ }
var key = p[0], val = make(p[1]);
if (beautify && beautify.quote_keys) {
key = make_string(key);
} else if (typeof key == "number" || !beautify && +key + "" == key) {
key = make_num(+key);
@@ -1283,17 +1364,19 @@
"regexp": function(rx, mods) {
return "/" + rx + "/" + mods;
},
"array": function(elements) {
if (elements.length == 0) return "[]";
- return add_spaces([ "[", add_commas(elements.map(make)), "]" ]);
+ return add_spaces([ "[", add_commas(MAP(elements, function(el){
+ return parenthesize(el, "seq");
+ })), "]" ]);
},
"stat": function(stmt) {
return make(stmt).replace(/;*\s*$/, ";");
},
"seq": function() {
- return add_commas(slice(arguments).map(make));
+ return add_commas(MAP(slice(arguments), make));
},
"label": function(name, block) {
return add_spaces([ make_name(name), ":", make(block) ]);
},
"with": function(expr, block) {
@@ -1339,43 +1422,20 @@
else break;
}
return make(th);
};
- function make_function(name, args, body) {
- var out = "function";
+ function make_function(name, args, body, keyword) {
+ var out = keyword || "function";
if (name) {
out += " " + make_name(name);
}
- out += "(" + add_commas(args.map(make_name)) + ")";
+ out += "(" + add_commas(MAP(args, make_name)) + ")";
return add_spaces([ out, make_block(body) ]);
};
- function make_string(str) {
- // return '"' +
- // str.replace(/\x5c/g, "\\\\")
- // .replace(/\r?\n/g, "\\n")
- // .replace(/\t/g, "\\t")
- // .replace(/\r/g, "\\r")
- // .replace(/\f/g, "\\f")
- // .replace(/[\b]/g, "\\b")
- // .replace(/\x22/g, "\\\"")
- // .replace(/[\x00-\x1f]|[\x80-\xff]/g, function(c){
- // var hex = c.charCodeAt(0).toString(16);
- // if (hex.length < 2)
- // hex = "0" + hex;
- // return "\\x" + hex;
- // })
- // + '"';
- return JSON.stringify(str); // STILL cheating.
- };
-
function make_name(name) {
- switch (name) {
- case "true": return beautify ? "true" : "!0";
- case "false": return beautify ? "false" : "!1";
- }
return name.toString();
};
function make_block_statements(statements) {
for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) {
@@ -1385,17 +1445,17 @@
if (!beautify && i == last)
code = code.replace(/;+\s*$/, "");
a.push(code);
}
}
- return a.map(indent);
+ return MAP(a, indent);
};
function make_switch_block(body) {
var n = body.length;
if (n == 0) return "{}";
- return "{" + newline + body.map(function(branch, i){
+ return "{" + newline + MAP(body, function(branch, i){
var has_body = branch[1].length > 0, code = with_indent(function(){
return indent(branch[0]
? add_spaces([ "case", make(branch[0]) + ":" ])
: "default:");
}, 0.5) + (has_body ? newline + with_indent(function(){
@@ -1469,13 +1529,32 @@
function HOP(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
};
+// some utilities
+
+var MAP;
+
+(function(){
+ MAP = function(a, f, o) {
+ var ret = [];
+ for (var i = 0; i < a.length; ++i) {
+ var val = f.call(o, a[i], i);
+ if (val instanceof AtTop) ret.unshift(val.v);
+ else ret.push(val);
+ }
+ return ret;
+ };
+ MAP.at_top = function(val) { return new AtTop(val) };
+ function AtTop(val) { this.v = val };
+})();
+
/* -----[ Exports ]----- */
exports.ast_walker = ast_walker;
exports.ast_mangle = ast_mangle;
exports.ast_squeeze = ast_squeeze;
exports.gen_code = gen_code;
exports.ast_add_scope = ast_add_scope;
exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;
+exports.set_logger = function(logger) { warn = logger };