Skip to content

Commit

Permalink
implement keep_fargs for mangle (#5307)
Browse files Browse the repository at this point in the history
closes #4657
  • Loading branch information
alexlamsl committed Jan 20, 2022
1 parent efed55f commit e24b255
Show file tree
Hide file tree
Showing 20 changed files with 507 additions and 267 deletions.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -115,6 +115,7 @@ a double dash to prevent input files being used as option arguments:
Equivalent to setting `ie: true` in `minify()`
for `compress`, `mangle` and `output` options.
By default UglifyJS will not try to be IE-proof.
--keep-fargs Do not mangle/drop function arguments.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--name-cache <file> File to hold mangled name mappings.
Expand Down Expand Up @@ -504,6 +505,9 @@ if (result.error) throw result.error;

- `ie` (default: `false`) — enable workarounds for Internet Explorer bugs.

- `keep_fargs` (default: `false`) — pass `true` to prevent discarding or mangling
of function arguments.

- `keep_fnames` (default: `false`) — pass `true` to prevent discarding or mangling
of function names. Useful for code relying on `Function.prototype.name`.

Expand Down Expand Up @@ -868,6 +872,9 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
- `debug` (default: `false`) — Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.

- `keep_fargs` (default: `false`) — Use `true` to prevent mangling of function
arguments.

- `keep_quoted` (default: `false`) — Only mangle unquoted property names.

- `regex` (default: `null`) — Pass a RegExp literal to only mangle property
Expand Down
5 changes: 5 additions & 0 deletions bin/uglifyjs
Expand Up @@ -72,6 +72,7 @@ function process_option(name, no_value) {
} else {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
toplevels.push([ {
keep_fargs: "keep-fargs",
keep_fnames: "keep-fnames",
nameCache: "name-cache",
}[name] || name, option ]);
Expand Down Expand Up @@ -104,6 +105,7 @@ function process_option(name, no_value) {
" -d, --define <expr>[=value] Global definitions.",
" -e, --enclose [arg[,...][:value[,...]]] Embed everything in a big function, with configurable argument(s) & value(s).",
" --ie Support non-standard Internet Explorer.",
" --keep-fargs Do not mangle/drop function arguments.",
" --keep-fnames Do not mangle/drop function names. Useful for code relying on Function.prototype.name.",
" --name-cache <file> File to hold mangled name mappings.",
" --rename Force symbol expansion.",
Expand Down Expand Up @@ -160,6 +162,9 @@ function process_option(name, no_value) {
case "no-annotations":
options.annotations = false;
break;
case "keep-fargs":
options.keep_fargs = true;
break;
case "keep-fnames":
options.keep_fnames = true;
break;
Expand Down
75 changes: 34 additions & 41 deletions lib/compress.js
Expand Up @@ -443,10 +443,6 @@ Compressor.prototype.compress = function(node) {
return def.name == "arguments" && def.scope.uses_arguments;
}

function is_funarg(def) {
return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
}

function cross_scope(def, sym) {
do {
if (def === sym) return false;
Expand Down Expand Up @@ -2053,7 +2049,7 @@ Compressor.prototype.compress = function(node) {
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
left: node,
right: rvalue,
});
}
Expand Down Expand Up @@ -6229,51 +6225,46 @@ Compressor.prototype.compress = function(node) {
var changed = false;
var merged = Object.create(null);
while (first.length && last.length) {
var head = first.pop();
var def = head.definition;
if (!(def.id in prev)) continue;
if (!references[def.id]) continue;
var head_refs = {
start: references[def.id].start,
};
var tail = last.shift();
if (!tail) continue;
var def = tail.definition;
var tail_refs = references[def.id];
if (!tail_refs) continue;
tail_refs = { end: tail_refs.end };
while (def.id in merged) def = merged[def.id];
head_refs.end = references[def.id].end;
tail_refs.start = references[def.id].start;
var skipped = [];
do {
var tail = last.pop();
if (!tail) continue;
var head = first.shift();
if (tail.index > head.index) continue;
var id = tail.definition.id;
var tail_refs = references[id];
if (!tail_refs) continue;
var id = head.definition.id;
if (!(id in prev)) continue;
var head_refs = references[id];
if (!head_refs) continue;
if (head_refs.start.block !== tail_refs.start.block
|| !mergeable(head_refs, tail_refs)
|| (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
|| compressor.option("webkit") && is_funarg(def) !== is_funarg(tail.definition)
|| !all(tail_refs, function(sym) {
return sym.scope.find_variable(def.name) === def;
})) {
skipped.unshift(tail);
|| compressor.option("webkit") && is_funarg(def) !== is_funarg(head.definition)
|| !safe_to_rename(head_refs, def)
|| !safe_to_rename(references[def.id], head.definition)) {
skipped.push(head);
continue;
}
var orig = [], refs = [];
tail_refs.forEach(function(sym) {
head_refs.forEach(function(sym) {
sym.thedef = def;
sym.name = def.name;
if (sym instanceof AST_SymbolRef) {
refs.push(sym);
def.references.push(sym);
} else {
orig.push(sym);
def.orig.push(sym);
}
});
def.orig = orig.concat(def.orig);
def.references = refs.concat(def.references);
def.fixed = tail.definition.fixed && def.fixed;
if (!head.definition.fixed) def.fixed = false;
merged[id] = def;
changed = true;
break;
} while (last.length);
if (skipped.length) last = last.concat(skipped);
} while (first.length);
if (skipped.length) first = skipped.concat(first);
}
return changed;

Expand Down Expand Up @@ -6357,6 +6348,12 @@ Compressor.prototype.compress = function(node) {
function mergeable(head, tail) {
return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
}

function safe_to_rename(refs, def) {
return all(refs, function(sym) {
return sym.scope.find_variable(def.name) === def;
});
}
});

function fill_holes(orig, elements) {
Expand Down Expand Up @@ -8072,9 +8069,7 @@ Compressor.prototype.compress = function(node) {
return exp.drop_side_effect_free(compressor, first_in_statement);
}
function convert_spread(node) {
return node instanceof AST_Spread ? make_node(AST_Array, node, {
elements: [ node ]
}) : node;
return node instanceof AST_Spread ? make_node(AST_Array, node, { elements: [ node ] }) : node;
}
def(AST_Node, return_this);
def(AST_Accessor, return_null);
Expand Down Expand Up @@ -8396,20 +8391,20 @@ Compressor.prototype.compress = function(node) {
node = alternative ? make_node(AST_Binary, this, {
operator: "||",
left: this.condition,
right: alternative
right: alternative,
}) : this.condition.drop_side_effect_free(compressor);
} else if (!alternative) {
node = make_node(AST_Binary, this, {
operator: "&&",
left: this.condition,
right: consequent
right: consequent,
});
} else {
node = this.clone();
node.consequent = consequent;
node.alternative = alternative;
}
if (!compressor.option("ie")) return node;
if (!exprs) return node;
if (node) exprs.push(node);
return exprs.length == 0 ? null : make_sequence(this, exprs);
});
Expand Down Expand Up @@ -8442,9 +8437,7 @@ Compressor.prototype.compress = function(node) {
return !(node instanceof AST_Spread);
})) return this;
return make_sequence(this, values.map(function(node) {
return node instanceof AST_Spread ? make_node(AST_Object, node, {
properties: [ node ],
}) : node;
return node instanceof AST_Spread ? make_node(AST_Object, node, { properties: [ node ] }) : node;
}));
});
def(AST_ObjectIdentity, return_null);
Expand Down
3 changes: 3 additions & 0 deletions lib/minify.js
Expand Up @@ -78,6 +78,7 @@ function minify(files, options) {
enclose: false,
ie: false,
ie8: false,
keep_fargs: false,
keep_fnames: false,
mangle: {},
nameCache: null,
Expand All @@ -99,6 +100,7 @@ function minify(files, options) {
if (options.annotations !== undefined) set_shorthand("annotations", options, [ "compress", "output" ]);
if (options.ie8) options.ie = options.ie || options.ie8;
if (options.ie) set_shorthand("ie", options, [ "compress", "mangle", "output" ]);
if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output" ]);
Expand All @@ -109,6 +111,7 @@ function minify(files, options) {
cache: options.nameCache && (options.nameCache.vars || {}),
eval: false,
ie: false,
keep_fargs: false,
keep_fnames: false,
properties: false,
reserved: [],
Expand Down
34 changes: 23 additions & 11 deletions lib/scope.js
Expand Up @@ -92,15 +92,19 @@ SymbolDef.prototype = {
if (def && def !== self) return def.redefined() || def;
},
unmangleable: function(options) {
return this.global && !options.toplevel
|| this.exported
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolClass
|| this.orig[0] instanceof AST_SymbolDefClass
|| this.orig[0] instanceof AST_SymbolDefun
|| this.orig[0] instanceof AST_SymbolLambda);
if (this.exported) return true;
if (this.undeclared) return true;
if (!options.eval && this.scope.pinned()) return true;
if (options.keep_fargs && is_funarg(this)) return true;
if (options.keep_fnames) {
var sym = this.orig[0];
if (sym instanceof AST_SymbolClass) return true;
if (sym instanceof AST_SymbolDefClass) return true;
if (sym instanceof AST_SymbolDefun) return true;
if (sym instanceof AST_SymbolLambda) return true;
}
if (!options.toplevel && this.global) return true;
return false;
},
};

Expand All @@ -113,6 +117,10 @@ DEF_BITPROPS(SymbolDef, [
"undeclared",
]);

function is_funarg(def) {
return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
}

var unary_side_effects = makePredicate("delete ++ --");

function is_lhs(node, parent) {
Expand Down Expand Up @@ -473,8 +481,11 @@ AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
push_uniq(s.enclosed, def);
if (!options) {
s._var_names = undefined;
} else if (options.keep_fnames) {
s.functions.each(function(d) {
} else {
if (options.keep_fargs && s instanceof AST_Lambda) s.each_argname(function(arg) {
push_uniq(def.scope.enclosed, arg.definition());
});
if (options.keep_fnames) s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});
}
Expand Down Expand Up @@ -579,6 +590,7 @@ function _default_mangler_options(options) {
options = defaults(options, {
eval : false,
ie : false,
keep_fargs : false,
keep_fnames : false,
reserved : [],
toplevel : false,
Expand Down
14 changes: 7 additions & 7 deletions test/compress/awaits.js
Expand Up @@ -1746,10 +1746,10 @@ issue_4454_2: {
f("PASS");
}
expect: {
function f(b) {
(async function(c = console.log(b)) {})();
var b = 42..toString();
console.log(b);
function f(a) {
(async function(c = console.log(a)) {})();
var a = 42..toString();
console.log(a);
}
f("PASS");
}
Expand Down Expand Up @@ -2449,9 +2449,9 @@ issue_5032_normal: {
console.log(value);
return value;
}
async function f(c) {
var b = log(c), c = b;
log(b);
async function f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS");
Expand Down
26 changes: 26 additions & 0 deletions test/compress/const.js
Expand Up @@ -220,6 +220,32 @@ merge_vars_3: {
expect_stdout: true
}

merge_vars_4: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = 1;
console.log(typeof a);
{
var b = console;
console.log(typeof b);
const a = 0;
}
}
expect: {
var a = 1;
console.log(typeof a);
{
var b = console;
console.log(typeof b);
const a = 0;
}
}
expect_stdout: true
}

use_before_init_1: {
options = {
reduce_vars: true,
Expand Down
10 changes: 5 additions & 5 deletions test/compress/destructured.js
Expand Up @@ -1845,13 +1845,13 @@ issue_4294: {
expect: {
A = "PASS";
(function() {
var b = function({
[b]: {},
var a = function({
[a]: {},
}) {}({
[b]: 0,
[a]: 0,
});
var b = A;
console.log(b);
var a = A;
console.log(a);
})();
}
expect_stdout: "PASS"
Expand Down
6 changes: 3 additions & 3 deletions test/compress/functions.js
Expand Up @@ -7605,11 +7605,11 @@ issue_5230: {
}());
}
expect: {
while (void (f = function(c) {
while (void (f = function(a) {
var b = 42;
console;
var c;
for (var k in c = [ c ])
var a;
for (var k in a = [ a ])
console.log(b++);
})(f));
var f;
Expand Down

0 comments on commit e24b255

Please sign in to comment.