diff --git a/lib/propmangle.js b/lib/propmangle.js index 4187db121c5..52705393fc1 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -83,7 +83,8 @@ function mangle_properties(ast, options) { cache : null, only_cache : false, regex : null, - ignore_quoted : false + ignore_quoted : false, + treat_as_global : [], }); var reserved = options.reserved; @@ -104,7 +105,17 @@ function mangle_properties(ast, options) { var names_to_mangle = []; var unmangleable = []; var ignored = {}; - + + if (!cache.global_defs) cache.global_defs = {}; + + var treat_as_global = options.treat_as_global; + var mangle_globals = !!treat_as_global.length; + + function is_global_alias(name) + { + return treat_as_global.indexOf(name) >= 0; + } + // step 1: find candidates to mangle ast.walk(new TreeWalker(function(node){ if (node instanceof AST_ObjectKeyVal) { @@ -116,6 +127,16 @@ function mangle_properties(ast, options) { } else if (node instanceof AST_Dot) { add(node.property); + + // if the left side of the dot is a global alias (e.g. window.foo), and the left side + // does not refer to a local variable, add it to a list of global definitions. + if (mangle_globals && + node.expression instanceof AST_SymbolRef && + node.expression.global() && + is_global_alias(node.expression.name) && + !is_global_alias(node.property)) { + cache.global_defs[node.property] = true; + } } else if (node instanceof AST_Sub) { addStrings(node.property, ignore_quoted); @@ -123,6 +144,15 @@ function mangle_properties(ast, options) { else if (node instanceof AST_ConciseMethod) { add(node.name.name); } + else if (node instanceof AST_SymbolRef) { + // if this term stands alone (e.g. just 'foo' where 'foo' cannot be shown to + // be a local variable in scope), also treat it as a global definition. + if (mangle_globals && + node.global() && + !is_global_alias(node.name)) { + cache.global_defs[node.name] = true; + } + } })); // step 2: transform the tree, renaming properties @@ -147,6 +177,17 @@ function mangle_properties(ast, options) { node.name.name = mangle(node.name.name); } } + else if (node instanceof AST_SymbolRef) { + // if this is a standalone global reference which does not refer to a local variable in scope, + // and it's on our list of global definitions, then mangle it. + if (mangle_globals && + node.global() && + node.name in cache.global_defs) { + var mangled_name = mangle(node.name); + node.name = mangled_name; + node.definition().mangled_name = mangled_name; + } + } // else if (node instanceof AST_String) { // if (should_mangle(node.value)) { // AST_Node.warn( @@ -178,7 +219,8 @@ function mangle_properties(ast, options) { if (regex && !regex.test(name)) return false; if (reserved.indexOf(name) >= 0) return false; return cache.props.has(name) - || names_to_mangle.indexOf(name) >= 0; + || names_to_mangle.indexOf(name) >= 0 + || (mangle_globals && name in cache.global_defs); } function add(name, ignore) { @@ -205,6 +247,12 @@ function mangle_properties(ast, options) { do { mangled = base54(++cache.cname); } while (!can_mangle(mangled)); + + // HACK: to avoid mangled global references colliding with local variable names, use + // a prefix on all mangled global names to effectively move them to a different namespace. + if (mangle_globals && name in cache.global_defs) + mangled = "g_" + mangled; + cache.props.set(name, mangled); } return mangled; diff --git a/test/compress/properties.js b/test/compress/properties.js index 7ef6a014d86..94b6434fc64 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -143,6 +143,52 @@ mangle_unquoted_properties: { } } +mangle_global_properties: { + mangle_props = { + treat_as_global: ["window"], + reserved: ["console", "log"] + } + input: { + window.foo = {}; + foo.bar = 1; + external.baz = 2; + console.log(external); + } + expect: { + window.g_a = {}; + g_a.b = 1; + g_d.c = 2; + console.log(g_d); + } +} + +mangle_global_properties_not_local: { + options = { + } + mangle_props = { + treat_as_global: ["self"], + reserved: ["console", "log"] + } + input: { + self.foo = {}; + function test() + { + var self = this; + console.log(self.bar); + }; + console.log(typeof bar); + } + expect: { + self.g_a = {}; + function test() + { + var self = this; + console.log(self.b); + }; + console.log(typeof g_b); + } +} + first_256_chars_as_properties: { beautify = { ascii_only: true,