Add treat_as_global option to mangle_properties

This enables mangling of global references using a given list of global
alias names, which can be e.g. "window", "self", etc. depending on
context.
This commit is contained in:
Ashley (Scirra) 2016-10-10 12:55:53 +01:00
parent 88f6ff38d1
commit 58459d0e60
2 changed files with 104 additions and 3 deletions

View File

@ -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;
@ -105,6 +106,20 @@ function mangle_properties(ast, options) {
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;
}
// TODO: don't know if this is necessary to get scope information?
//if (mangle_globals)
// ast.figure_out_scope();
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
@ -116,6 +131,17 @@ 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 +149,16 @@ 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 +183,18 @@ 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 +226,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 +254,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;

View File

@ -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,