enhance mangle.properties (#4064)
This commit is contained in:
@@ -197,9 +197,7 @@ function minify(files, options) {
|
|||||||
toplevel.mangle_names(options.mangle);
|
toplevel.mangle_names(options.mangle);
|
||||||
}
|
}
|
||||||
if (timings) timings.properties = Date.now();
|
if (timings) timings.properties = Date.now();
|
||||||
if (options.mangle && options.mangle.properties) {
|
if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties);
|
||||||
toplevel = mangle_properties(toplevel, options.mangle.properties);
|
|
||||||
}
|
|
||||||
if (timings) timings.output = Date.now();
|
if (timings) timings.output = Date.now();
|
||||||
var result = {};
|
var result = {};
|
||||||
if (options.output.ast) {
|
if (options.output.ast) {
|
||||||
|
|||||||
@@ -43,7 +43,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function find_builtins(reserved) {
|
var builtins = function() {
|
||||||
|
var names = [];
|
||||||
// NaN will be included due to Number.NaN
|
// NaN will be included due to Number.NaN
|
||||||
[
|
[
|
||||||
"null",
|
"null",
|
||||||
@@ -67,19 +68,21 @@ function find_builtins(reserved) {
|
|||||||
].forEach(function(ctor) {
|
].forEach(function(ctor) {
|
||||||
Object.getOwnPropertyNames(ctor).map(add);
|
Object.getOwnPropertyNames(ctor).map(add);
|
||||||
if (ctor.prototype) {
|
if (ctor.prototype) {
|
||||||
|
Object.getOwnPropertyNames(new ctor()).map(add);
|
||||||
Object.getOwnPropertyNames(ctor.prototype).map(add);
|
Object.getOwnPropertyNames(ctor.prototype).map(add);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return makePredicate(names);
|
||||||
|
|
||||||
function add(name) {
|
function add(name) {
|
||||||
push_uniq(reserved, name);
|
names.push(name);
|
||||||
}
|
}
|
||||||
}
|
}();
|
||||||
|
|
||||||
function reserve_quoted_keys(ast, reserved) {
|
function reserve_quoted_keys(ast, reserved) {
|
||||||
ast.walk(new TreeWalker(function(node) {
|
ast.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_ObjectKeyVal && node.quote) {
|
if (node instanceof AST_ObjectKeyVal) {
|
||||||
add(node.key);
|
if (node.quote) add(node.key);
|
||||||
} else if (node instanceof AST_Sub) {
|
} else if (node instanceof AST_Sub) {
|
||||||
addStrings(node.property, add);
|
addStrings(node.property, add);
|
||||||
}
|
}
|
||||||
@@ -91,17 +94,14 @@ function reserve_quoted_keys(ast, reserved) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addStrings(node, add) {
|
function addStrings(node, add) {
|
||||||
node.walk(new TreeWalker(function(node) {
|
if (node instanceof AST_Conditional) {
|
||||||
if (node instanceof AST_Sequence) {
|
addStrings(node.consequent, add);
|
||||||
addStrings(node.tail_node(), add);
|
addStrings(node.alternative, add);
|
||||||
} else if (node instanceof AST_String) {
|
} else if (node instanceof AST_Sequence) {
|
||||||
add(node.value);
|
addStrings(node.tail_node(), add);
|
||||||
} else if (node instanceof AST_Conditional) {
|
} else if (node instanceof AST_String) {
|
||||||
addStrings(node.consequent, add);
|
add(node.value);
|
||||||
addStrings(node.alternative, add);
|
}
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mangle_properties(ast, options) {
|
function mangle_properties(ast, options) {
|
||||||
@@ -115,16 +115,17 @@ function mangle_properties(ast, options) {
|
|||||||
reserved: null,
|
reserved: null,
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var reserved = options.reserved;
|
var reserved = Object.create(options.builtins ? null : builtins);
|
||||||
if (!Array.isArray(reserved)) reserved = [];
|
if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
|
||||||
if (!options.builtins) find_builtins(reserved);
|
reserved[name] = true;
|
||||||
|
});
|
||||||
|
|
||||||
var cname = -1;
|
var cname = -1;
|
||||||
var cache;
|
var cache;
|
||||||
if (options.cache) {
|
if (options.cache) {
|
||||||
cache = options.cache.props;
|
cache = options.cache.props;
|
||||||
cache.each(function(mangled_name) {
|
cache.each(function(name) {
|
||||||
push_uniq(reserved, mangled_name);
|
reserved[name] = true;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
cache = new Dictionary();
|
cache = new Dictionary();
|
||||||
@@ -139,62 +140,93 @@ function mangle_properties(ast, options) {
|
|||||||
var debug_suffix;
|
var debug_suffix;
|
||||||
if (debug) debug_suffix = options.debug === true ? "" : options.debug;
|
if (debug) debug_suffix = options.debug === true ? "" : options.debug;
|
||||||
|
|
||||||
var names_to_mangle = [];
|
var names_to_mangle = Object.create(null);
|
||||||
var unmangleable = [];
|
var unmangleable = Object.create(reserved);
|
||||||
|
|
||||||
// step 1: find candidates to mangle
|
// step 1: find candidates to mangle
|
||||||
ast.walk(new TreeWalker(function(node) {
|
ast.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_ObjectKeyVal) {
|
if (node instanceof AST_Binary) {
|
||||||
|
if (node.operator == "in") addStrings(node.left, add);
|
||||||
|
} else if (node.TYPE == "Call") {
|
||||||
|
var exp = node.expression;
|
||||||
|
if (exp instanceof AST_Dot) switch (exp.property) {
|
||||||
|
case "defineProperty":
|
||||||
|
case "getOwnPropertyDescriptor":
|
||||||
|
if (node.args.length < 2) break;
|
||||||
|
exp = exp.expression;
|
||||||
|
if (!(exp instanceof AST_SymbolRef)) break;
|
||||||
|
if (exp.name != "Object") break;
|
||||||
|
if (!exp.definition().undeclared) break;
|
||||||
|
addStrings(node.args[1], add);
|
||||||
|
break;
|
||||||
|
case "hasOwnProperty":
|
||||||
|
if (node.args.length < 1) break;
|
||||||
|
addStrings(node.args[0], add);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (node instanceof AST_Dot) {
|
||||||
|
add(node.property);
|
||||||
|
} else if (node instanceof AST_ObjectKeyVal) {
|
||||||
add(node.key);
|
add(node.key);
|
||||||
} else if (node instanceof AST_ObjectProperty) {
|
} else if (node instanceof AST_ObjectProperty) {
|
||||||
// setter or getter, since KeyVal is handled above
|
// setter or getter, since KeyVal is handled above
|
||||||
add(node.key.name);
|
add(node.key.name);
|
||||||
} else if (node instanceof AST_Dot) {
|
|
||||||
add(node.property);
|
|
||||||
} else if (node instanceof AST_Sub) {
|
} else if (node instanceof AST_Sub) {
|
||||||
addStrings(node.property, add);
|
addStrings(node.property, add);
|
||||||
} else if (node instanceof AST_Call
|
|
||||||
&& node.expression.print_to_string() == "Object.defineProperty") {
|
|
||||||
addStrings(node.args[1], add);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// step 2: transform the tree, renaming properties
|
// step 2: renaming properties
|
||||||
return ast.transform(new TreeTransformer(function(node) {
|
ast.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_ObjectKeyVal) {
|
if (node instanceof AST_Binary) {
|
||||||
|
if (node.operator == "in") mangleStrings(node.left);
|
||||||
|
} else if (node.TYPE == "Call") {
|
||||||
|
var exp = node.expression;
|
||||||
|
if (exp instanceof AST_Dot) switch (exp.property) {
|
||||||
|
case "defineProperty":
|
||||||
|
case "getOwnPropertyDescriptor":
|
||||||
|
if (node.args.length < 2) break;
|
||||||
|
exp = exp.expression;
|
||||||
|
if (!(exp instanceof AST_SymbolRef)) break;
|
||||||
|
if (exp.name != "Object") break;
|
||||||
|
if (!exp.definition().undeclared) break;
|
||||||
|
mangleStrings(node.args[1]);
|
||||||
|
break;
|
||||||
|
case "hasOwnProperty":
|
||||||
|
if (node.args.length < 1) break;
|
||||||
|
mangleStrings(node.args[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (node instanceof AST_Dot) {
|
||||||
|
node.property = mangle(node.property);
|
||||||
|
} else if (node instanceof AST_ObjectKeyVal) {
|
||||||
node.key = mangle(node.key);
|
node.key = mangle(node.key);
|
||||||
} else if (node instanceof AST_ObjectProperty) {
|
} else if (node instanceof AST_ObjectProperty) {
|
||||||
// setter or getter
|
// setter or getter
|
||||||
node.key.name = mangle(node.key.name);
|
node.key.name = mangle(node.key.name);
|
||||||
} else if (node instanceof AST_Dot) {
|
} else if (node instanceof AST_Sub) {
|
||||||
node.property = mangle(node.property);
|
if (!options.keep_quoted) mangleStrings(node.property);
|
||||||
} else if (!options.keep_quoted && node instanceof AST_Sub) {
|
|
||||||
node.property = mangleStrings(node.property);
|
|
||||||
} else if (node instanceof AST_Call
|
|
||||||
&& node.expression.print_to_string() == "Object.defineProperty") {
|
|
||||||
node.args[1] = mangleStrings(node.args[1]);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// only function declarations after this line
|
// only function declarations after this line
|
||||||
|
|
||||||
function can_mangle(name) {
|
function can_mangle(name) {
|
||||||
if (unmangleable.indexOf(name) >= 0) return false;
|
if (unmangleable[name]) return false;
|
||||||
if (reserved.indexOf(name) >= 0) return false;
|
|
||||||
if (options.only_cache) return cache.has(name);
|
if (options.only_cache) return cache.has(name);
|
||||||
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
|
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function should_mangle(name) {
|
function should_mangle(name) {
|
||||||
|
if (reserved[name]) return false;
|
||||||
if (regex && !regex.test(name)) return false;
|
if (regex && !regex.test(name)) return false;
|
||||||
if (reserved.indexOf(name) >= 0) return false;
|
return cache.has(name) || names_to_mangle[name];
|
||||||
return cache.has(name) || names_to_mangle.indexOf(name) >= 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function add(name) {
|
function add(name) {
|
||||||
if (can_mangle(name)) push_uniq(names_to_mangle, name);
|
if (can_mangle(name)) names_to_mangle[name] = true;
|
||||||
if (!should_mangle(name)) push_uniq(unmangleable, name);
|
if (!should_mangle(name)) unmangleable[name] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mangle(name) {
|
function mangle(name) {
|
||||||
@@ -218,17 +250,13 @@ function mangle_properties(ast, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mangleStrings(node) {
|
function mangleStrings(node) {
|
||||||
return node.transform(new TreeTransformer(function(node) {
|
if (node instanceof AST_Sequence) {
|
||||||
if (node instanceof AST_Sequence) {
|
mangleStrings(node.expressions.tail_node());
|
||||||
var last = node.expressions.length - 1;
|
} else if (node instanceof AST_String) {
|
||||||
node.expressions[last] = mangleStrings(node.expressions[last]);
|
node.value = mangle(node.value);
|
||||||
} else if (node instanceof AST_String) {
|
} else if (node instanceof AST_Conditional) {
|
||||||
node.value = mangle(node.value);
|
mangleStrings(node.consequent);
|
||||||
} else if (node instanceof AST_Conditional) {
|
mangleStrings(node.alternative);
|
||||||
node.consequent = mangleStrings(node.consequent);
|
}
|
||||||
node.alternative = mangleStrings(node.alternative);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,9 +312,7 @@ function test_case(test) {
|
|||||||
if (test.mangle) {
|
if (test.mangle) {
|
||||||
output.compute_char_frequency(test.mangle);
|
output.compute_char_frequency(test.mangle);
|
||||||
output.mangle_names(test.mangle);
|
output.mangle_names(test.mangle);
|
||||||
if (test.mangle.properties) {
|
if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties);
|
||||||
output = U.mangle_properties(output, test.mangle.properties);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var output_code = make_code(output, output_options);
|
var output_code = make_code(output, output_options);
|
||||||
if (expect != output_code) {
|
if (expect != output_code) {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ evaluate_string_length: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mangle_properties: {
|
mangle_properties_1: {
|
||||||
mangle = {
|
mangle = {
|
||||||
properties: {
|
properties: {
|
||||||
keep_quoted: false,
|
keep_quoted: false,
|
||||||
@@ -152,6 +152,53 @@ mangle_properties: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mangle_properties_2: {
|
||||||
|
mangle = {
|
||||||
|
properties: {
|
||||||
|
reserved: [
|
||||||
|
"value",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var o = {
|
||||||
|
prop1: 1,
|
||||||
|
};
|
||||||
|
Object.defineProperty(o, "prop2", {
|
||||||
|
value: 2,
|
||||||
|
});
|
||||||
|
Object.defineProperties(o, {
|
||||||
|
prop3: {
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log("prop1", o.prop1, "prop1" in o);
|
||||||
|
console.log("prop2", o.prop2, o.hasOwnProperty("prop2"));
|
||||||
|
console.log("prop3", o.prop3, Object.getOwnPropertyDescriptor(o, "prop3").value);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var o = {
|
||||||
|
o: 1,
|
||||||
|
};
|
||||||
|
Object.defineProperty(o, "p", {
|
||||||
|
value: 2,
|
||||||
|
});
|
||||||
|
Object.defineProperties(o, {
|
||||||
|
r: {
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log("prop1", o.o, "o" in o);
|
||||||
|
console.log("prop2", o.p, o.hasOwnProperty("p"));
|
||||||
|
console.log("prop3", o.r, Object.getOwnPropertyDescriptor(o, "r").value);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"prop1 1 true",
|
||||||
|
"prop2 2 true",
|
||||||
|
"prop3 3 3",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
mangle_unquoted_properties: {
|
mangle_unquoted_properties: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user