enhance mangle.properties (#4064)

This commit is contained in:
Alex Lam S.L
2020-08-23 01:45:39 +01:00
committed by GitHub
parent 35fe1092d3
commit da85d102e3
4 changed files with 135 additions and 64 deletions

View File

@@ -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) {

View File

@@ -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;
}));
} }
} }

View File

@@ -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) {

View File

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