vendor/uglifyjs/lib/process.js in uglifier-0.4.0 vs vendor/uglifyjs/lib/process.js in uglifier-0.5.0

- old
+ new

@@ -8,18 +8,17 @@ This file implements some AST processors. They work on data built by parse-js. Exported functions: - - ast_mangle(ast, include_toplevel) -- mangles the - variable/function names in the AST. Returns an AST. Pass true - as second argument to mangle toplevel names too. + - ast_mangle(ast, options) -- mangles the variable/function names + in the AST. Returns an AST. - ast_squeeze(ast) -- employs various optimizations to make the final generated code even smaller. Returns an AST. - - gen_code(ast, beautify) -- generates JS code from the AST. Pass + - gen_code(ast, options) -- generates JS code from the AST. Pass true (or an object, see the code for some options) as second argument to get "pretty" (indented) code. -------------------------------- (C) --------------------------------- @@ -254,12 +253,12 @@ this.names = {}; // names defined in this scope this.mangled = {}; // mangled names (orig.name => mangled) this.rev_mangled = {}; // reverse lookup (mangled => orig.name) this.cname = -1; // current mangled name this.refs = {}; // names referenced from this scope - this.uses_with = false; // will become TRUE if eval() is detected in this or any subscopes - this.uses_eval = false; // will become TRUE if with() is detected in this or any subscopes + this.uses_with = false; // will become TRUE if with() is detected in this or any subscopes + this.uses_eval = false; // will become TRUE if eval() is detected in this or any subscopes this.parent = parent; // parent scope this.children = []; // sub-scopes if (parent) { this.level = parent.level + 1; parent.children.push(this); @@ -934,13 +933,14 @@ return w.with_walkers({ "sub": function(expr, subscript) { if (subscript[0] == "string") { var name = subscript[1]; - if (is_identifier(name)) { + if (is_identifier(name)) return [ "dot", walk(expr), name ]; - } + else if (/^[1-9][0-9]*$/.test(name) || name === "0") + return [ "sub", walk(expr), [ "num", parseInt(name, 10) ] ]; } }, "if": make_if, "toplevel": function(body) { return [ "toplevel", with_scope(this.scope, function(){ @@ -1027,55 +1027,78 @@ /* -----[ re-generate code from the AST ]----- */ var DOT_CALL_NO_PARENS = jsp.array_to_hash([ "name", "array", + "object", "string", "dot", "sub", "call", "regexp" ]); -function make_string(str) { +function make_string(str, ascii_only) { var dq = 0, sq = 0; - str = str.replace(/[\\\b\f\n\r\t\x22\x27]/g, function(s){ + str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029]/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 "\u2028": return "\\u2028"; + case "\u2029": return "\\u2029"; case '"': ++dq; return '"'; case "'": ++sq; return "'"; } return s; }); - if (dq > sq) { - return "'" + str.replace(/\x27/g, "\\'") + "'"; - } else { - return '"' + str.replace(/\x22/g, '\\"') + '"'; - } + if (ascii_only) str = to_ascii(str); + if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; + else return '"' + str.replace(/\x22/g, '\\"') + '"'; }; -function gen_code(ast, beautify) { - if (beautify) beautify = defaults(beautify, { +function to_ascii(str) { + return str.replace(/[\u0080-\uffff]/g, function(ch) { + var code = ch.charCodeAt(0).toString(16); + while (code.length < 4) code = "0" + code; + return "\\u" + code; + }); +}; + +function gen_code(ast, options) { + options = defaults(options, { indent_start : 0, indent_level : 4, quote_keys : false, - space_colon : false + space_colon : false, + beautify : false, + ascii_only : false }); + var beautify = !!options.beautify; var indentation = 0, newline = beautify ? "\n" : "", space = beautify ? " " : ""; + function encode_string(str) { + return make_string(str, options.ascii_only); + }; + + function make_name(name) { + name = name.toString(); + if (options.ascii_only) + name = to_ascii(name); + return name; + }; + function indent(line) { if (line == null) line = ""; if (beautify) - line = repeat_string(" ", beautify.indent_start + indentation * beautify.indent_level) + line; + line = repeat_string(" ", options.indent_start + indentation * options.indent_level) + line; return line; }; function with_indent(cont, incr) { if (incr == null) incr = 1; @@ -1125,11 +1148,11 @@ } return best_of([ a[0], best_of(a.slice(1)) ]); }; function needs_parens(expr) { - if (expr[0] == "function") { + if (expr[0] == "function" || expr[0] == "object") { // dot/call on a literal function requires the // function literal itself to be parenthesized // only if it's the first "thing" in a // statement. This means that the parent is // "stat", but it could also be a "seq" and @@ -1137,13 +1160,12 @@ // parent is "stat", and so on. Messy stuff, // but it worths the trouble. var a = slice($stack), self = a.pop(), p = a.pop(); while (p) { if (p[0] == "stat") return true; - if ((p[0] == "seq" && p[1] === self) || - (p[0] == "call" && p[1] === self) || - (p[0] == "binary" && p[2] === self)) { + if (((p[0] == "seq" || p[0] == "call" || p[0] == "dot" || p[0] == "sub" || p[0] == "conditional") && p[1] === self) || + ((p[0] == "binary" || p[0] == "assign" || p[0] == "unary-postfix") && p[2] === self)) { self = p; p = a.pop(); } else { return false; } @@ -1166,11 +1188,11 @@ } return best_of(a); }; var generators = { - "string": make_string, + "string": encode_string, "num": make_num, "name": make_name, "toplevel": function(statements) { return make_block_statements(statements) .join(newline + newline); @@ -1234,13 +1256,14 @@ else op = "="; return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]); }, "dot": function(expr) { var out = make(expr), i = 1; - if (expr[0] == "num") - out += "."; - else if (needs_parens(expr)) + if (expr[0] == "num") { + if (!/\./.test(expr[1])) + out += "."; + } else if (needs_parens(expr)) out = "(" + out + ")"; while (i < arguments.length) out += "." + make_name(arguments[i++]); return out; }, @@ -1331,19 +1354,19 @@ // 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); + if (options.quote_keys) { + key = encode_string(key); } else if ((typeof key == "number" || !beautify && +key + "" == key) && parseFloat(key) >= 0) { key = make_num(+key); } else if (!is_identifier(key)) { - key = make_string(key); + key = encode_string(key); } - return indent(add_spaces(beautify && beautify.space_colon + return indent(add_spaces(beautify && options.space_colon ? [ key, ":", val ] : [ key + ":", val ])); }).join("," + newline); }) + newline + indent("}"); }, @@ -1412,14 +1435,10 @@ } out += "(" + add_commas(MAP(args, make_name)) + ")"; return add_spaces([ out, make_block(body) ]); }; - function make_name(name) { - return name.toString(); - }; - function make_block_statements(statements) { for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) { var stat = statements[i]; var code = make(stat); if (code != ";") { @@ -1465,11 +1484,11 @@ }; function make_1vardef(def) { var name = def[0], val = def[1]; if (val != null) - name = add_spaces([ name, "=", make(val) ]); + name = add_spaces([ make_name(name), "=", make(val) ]); return name; }; var $stack = []; @@ -1586,9 +1605,12 @@ 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 }; exports.make_string = make_string; exports.split_lines = split_lines; +exports.MAP = MAP; + +// keep this last! +exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;