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 };