From c486b65ebcd789e5d75cb5fe62b3fe155120877c Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 27 Jan 2012 23:25:19 +0100 Subject: [PATCH 1/5] Add a new option for additional mangling in the code generator for all names matching a given regexp. This is an easy way to mangle 'privates' by prefixing them with a '_' and then using a matching regexp. This is potentially unsafe but works great on the untrivial code I've tested it on thus far --- bin/uglifyjs | 6 ++++- lib/process.js | 61 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/bin/uglifyjs b/bin/uglifyjs index e7ba6273..cb736075 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -30,7 +30,8 @@ var options = { indent_start: 0, quote_keys: false, space_colon: false, - inline_script: false + inline_script: false, + mangle_exp: null }, make: false, output: true // stdout @@ -181,6 +182,9 @@ out: while (args.length > 0) { case "--ascii": options.codegen_options.ascii_only = true; break; + case "--mangle-exp": + options.codegen_options.mangle_exp = new RegExp(args.shift()); + break; case "--make": options.make = true; break; diff --git a/lib/process.js b/lib/process.js index 89be306c..665ec9cb 100644 --- a/lib/process.js +++ b/lib/process.js @@ -66,7 +66,10 @@ var jsp = require("./parse-js"), /* -----[ helper for AST traversal ]----- */ + +var all_names = null; function ast_walker() { + function _vardefs(defs) { return [ this[0], MAP(defs, function(def){ var a = [ def[0] ]; @@ -81,6 +84,7 @@ function ast_walker() { out.push(MAP(statements, walk)); return out; }; + var walkers = { "string": function(str) { return [ this[0], str ]; @@ -89,6 +93,7 @@ function ast_walker() { return [ this[0], num ]; }, "name": function(name) { + all_names[name] = true; return [ this[0], name ]; }, "toplevel": function(statements) { @@ -131,18 +136,22 @@ function ast_walker() { return [ this[0], op, walk(lvalue), walk(rvalue) ]; }, "dot": function(expr) { - return [ this[0], walk(expr) ].concat(slice(arguments, 1)); + var p = slice(arguments, 1) + all_names[p[0]] = true; + return [ this[0], walk(expr) ].concat(p); }, "call": function(expr, args) { return [ this[0], walk(expr), MAP(args, walk) ]; }, "function": function(name, args, body) { + all_names[name] = true; return [ this[0], name, args.slice(), MAP(body, walk) ]; }, "debugger": function() { return [ this[0] ]; }, "defun": function(name, args, body) { + all_names[name] = true; return [ this[0], name, args.slice(), MAP(body, walk) ]; }, "if": function(conditional, t, e) { @@ -177,6 +186,7 @@ function ast_walker() { }, "object": function(props) { return [ this[0], MAP(props, function(p){ + all_names[p[0]] = true; return p.length == 2 ? [ p[0], walk(p[1]) ] : [ p[0], walk(p[1]), p[2] ]; // get/set-ter @@ -402,6 +412,7 @@ function ast_add_scope(ast) { }; function _lambda(name, args, body) { + var is_defun = this[0] == "defun"; return [ this[0], is_defun ? define(name, "defun") : name, args, with_new_scope(function(){ if (!is_defun) define(name, "lambda"); @@ -493,6 +504,7 @@ function ast_add_scope(ast) { /* -----[ mangle names ]----- */ function ast_mangle(ast, options) { + all_names = {}; var w = ast_walker(), walk = w.walk, scope; options = options || {}; @@ -993,6 +1005,7 @@ function ast_lift_variables(ast) { }; function ast_squeeze(ast, options) { + options = defaults(options, { make_seqs : true, dead_code : true, @@ -1411,6 +1424,7 @@ function to_ascii(str) { var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for-in", "with" ]); function gen_code(ast, options) { + options = defaults(options, { indent_start : 0, indent_level : 4, @@ -1418,7 +1432,8 @@ function gen_code(ast, options) { space_colon : false, beautify : false, ascii_only : false, - inline_script: false + inline_script: false, + mangle_exp : null }); var beautify = !!options.beautify; var indentation = 0, @@ -1432,10 +1447,33 @@ function gen_code(ast, options) { return ret; }; - function make_name(name) { + var mid = 0, mangled_names = {}; + function make_name(name, matching, is_key) { + + if (matching && options.mangle_exp && options.mangle_exp.test(name)) { + + if (mangled_names[name]) { + return mangled_names[name]; + + } else { + var mangled = base54(mid++); + while(Object.prototype.hasOwnProperty.call(all_names, mangled)) { + mangled = base54(mid++); + } + + return mangled_names[name] = mangled; + } + + if (is_key) { + return name; + } + + } + name = name.toString(); if (options.ascii_only) name = to_ascii(name); + return name; }; @@ -1546,7 +1584,9 @@ function gen_code(ast, options) { return w.with_walkers({ "string": encode_string, "num": make_num, - "name": make_name, + "name": function(name) { + return make_name(name, true); + }, "debugger": function(){ return "debugger" }, "toplevel": function(statements) { return make_block_statements(statements) @@ -1634,7 +1674,7 @@ function gen_code(ast, options) { } else if (needs_parens(expr)) out = "(" + out + ")"; while (i < arguments.length) - out += "." + make_name(arguments[i++]); + out += "." + make_name(arguments[i++], true); return out; }, "call": function(func, args) { @@ -1726,12 +1766,15 @@ function gen_code(ast, options) { return obj_needs_parens ? "({})" : "{}"; var out = "{" + newline + with_indent(function(){ return MAP(props, function(p){ + + var name = make_name(p[0], true, true); + 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], true)); + return indent(make_function(name, p[1][2], p[1][3], p[2], true)); } - var key = p[0], val = parenthesize(p[1], "seq"); + var key = name, val = parenthesize(p[1], "seq"); if (options.quote_keys) { key = encode_string(key); } else if ((typeof key == "number" || !beautify && +key + "" == key) @@ -1770,7 +1813,7 @@ function gen_code(ast, options) { return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]); }, "atom": function(name) { - return make_name(name); + return make_name(name, true); } }, function(){ return make(ast) }); @@ -1809,7 +1852,7 @@ function gen_code(ast, options) { function make_function(name, args, body, keyword, no_parens) { var out = keyword || "function"; if (name) { - out += " " + make_name(name); + out += " " + make_name(name, true); } out += "(" + add_commas(MAP(args, make_name)) + ")"; out = add_spaces([ out, make_block(body) ]); From 06a43f136891f12782823351ed7c1ebf8c2579e8 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 27 Jan 2012 23:34:31 +0100 Subject: [PATCH 2/5] Only use mangle-exp on objects and dot stuff --- lib/process.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/process.js b/lib/process.js index 665ec9cb..95110314 100644 --- a/lib/process.js +++ b/lib/process.js @@ -1585,7 +1585,7 @@ function gen_code(ast, options) { "string": encode_string, "num": make_num, "name": function(name) { - return make_name(name, true); + return make_name(name); }, "debugger": function(){ return "debugger" }, "toplevel": function(statements) { @@ -1852,7 +1852,7 @@ function gen_code(ast, options) { function make_function(name, args, body, keyword, no_parens) { var out = keyword || "function"; if (name) { - out += " " + make_name(name, true); + out += " " + make_name(name); } out += "(" + add_commas(MAP(args, make_name)) + ")"; out = add_spaces([ out, make_block(body) ]); From f9236a7cfc1ff4edb74a8d5abbe03ac5b87da038 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 27 Jan 2012 23:44:30 +0100 Subject: [PATCH 3/5] Lets just mangle privates prefixed with a _ --- bin/uglifyjs | 6 +++--- lib/process.js | 41 +++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/bin/uglifyjs b/bin/uglifyjs index cb736075..d1f78a95 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -31,7 +31,7 @@ var options = { quote_keys: false, space_colon: false, inline_script: false, - mangle_exp: null + mangle_private: false }, make: false, output: true // stdout @@ -182,8 +182,8 @@ out: while (args.length > 0) { case "--ascii": options.codegen_options.ascii_only = true; break; - case "--mangle-exp": - options.codegen_options.mangle_exp = new RegExp(args.shift()); + case "--mangle-private": + options.codegen_options.mangle_private = true, break; case "--make": options.make = true; diff --git a/lib/process.js b/lib/process.js index 95110314..15e8e184 100644 --- a/lib/process.js +++ b/lib/process.js @@ -1426,14 +1426,14 @@ var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for function gen_code(ast, options) { options = defaults(options, { - indent_start : 0, - indent_level : 4, - quote_keys : false, - space_colon : false, - beautify : false, - ascii_only : false, - inline_script: false, - mangle_exp : null + indent_start : 0, + indent_level : 4, + quote_keys : false, + space_colon : false, + beautify : false, + ascii_only : false, + inline_script : false, + mangle_private: false }); var beautify = !!options.beautify; var indentation = 0, @@ -1447,21 +1447,24 @@ function gen_code(ast, options) { return ret; }; - var mid = 0, mangled_names = {}; + var pid = 0, + priv = /^_([^_]|$)/, + privates = {}; + function make_name(name, matching, is_key) { - if (matching && options.mangle_exp && options.mangle_exp.test(name)) { + if (matching && options.mangle_private && priv.test(name)) { - if (mangled_names[name]) { - return mangled_names[name]; + if (privates[name]) { + return privates[name]; } else { - var mangled = base54(mid++); - while(Object.prototype.hasOwnProperty.call(all_names, mangled)) { - mangled = base54(mid++); + var m = base54(pid++); + while(Object.prototype.hasOwnProperty.call(all_names, m)) { + m = base54(pid++); } - return mangled_names[name] = mangled; + return privates[name] = m; } if (is_key) { @@ -1584,9 +1587,7 @@ function gen_code(ast, options) { return w.with_walkers({ "string": encode_string, "num": make_num, - "name": function(name) { - return make_name(name); - }, + "name": make_name, "debugger": function(){ return "debugger" }, "toplevel": function(statements) { return make_block_statements(statements) @@ -1813,7 +1814,7 @@ function gen_code(ast, options) { return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]); }, "atom": function(name) { - return make_name(name, true); + return make_name(name); } }, function(){ return make(ast) }); From 4f7d7eca1ed5e486ded3ca95a0e407d7eb281ab6 Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Fri, 27 Jan 2012 23:52:48 +0100 Subject: [PATCH 4/5] Only collect keys and dot accessors for private conflict detection --- lib/process.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/process.js b/lib/process.js index 15e8e184..cf83e82e 100644 --- a/lib/process.js +++ b/lib/process.js @@ -67,7 +67,7 @@ var jsp = require("./parse-js"), /* -----[ helper for AST traversal ]----- */ -var all_names = null; +var private_names = null; function ast_walker() { function _vardefs(defs) { @@ -93,7 +93,6 @@ function ast_walker() { return [ this[0], num ]; }, "name": function(name) { - all_names[name] = true; return [ this[0], name ]; }, "toplevel": function(statements) { @@ -137,21 +136,19 @@ function ast_walker() { }, "dot": function(expr) { var p = slice(arguments, 1) - all_names[p[0]] = true; + private_names[p[0]] = true; return [ this[0], walk(expr) ].concat(p); }, "call": function(expr, args) { return [ this[0], walk(expr), MAP(args, walk) ]; }, "function": function(name, args, body) { - all_names[name] = true; return [ this[0], name, args.slice(), MAP(body, walk) ]; }, "debugger": function() { return [ this[0] ]; }, "defun": function(name, args, body) { - all_names[name] = true; return [ this[0], name, args.slice(), MAP(body, walk) ]; }, "if": function(conditional, t, e) { @@ -186,7 +183,7 @@ function ast_walker() { }, "object": function(props) { return [ this[0], MAP(props, function(p){ - all_names[p[0]] = true; + private_names[p[0]] = true; return p.length == 2 ? [ p[0], walk(p[1]) ] : [ p[0], walk(p[1]), p[2] ]; // get/set-ter @@ -504,7 +501,7 @@ function ast_add_scope(ast) { /* -----[ mangle names ]----- */ function ast_mangle(ast, options) { - all_names = {}; + private_names = {}; var w = ast_walker(), walk = w.walk, scope; options = options || {}; @@ -1460,7 +1457,7 @@ function gen_code(ast, options) { } else { var m = base54(pid++); - while(Object.prototype.hasOwnProperty.call(all_names, m)) { + while(Object.prototype.hasOwnProperty.call(private_names, m)) { m = base54(pid++); } From dbf2df3a45fbafaec7326eda060ee72dd7c8695e Mon Sep 17 00:00:00 2001 From: Ivo Wetzel Date: Sun, 29 Jan 2012 18:39:03 +0100 Subject: [PATCH 5/5] Fix tralining comma --- bin/uglifyjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/uglifyjs b/bin/uglifyjs index d1f78a95..bf52b4b3 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -183,7 +183,7 @@ out: while (args.length > 0) { options.codegen_options.ascii_only = true; break; case "--mangle-private": - options.codegen_options.mangle_private = true, + options.codegen_options.mangle_private = true; break; case "--make": options.make = true;