enhance keep_fargs (#3409)

This commit is contained in:
Alex Lam S.L
2019-05-13 21:58:04 +08:00
committed by GitHub
parent 3bc7cc82bb
commit 2e4fbdeb08
6 changed files with 1145 additions and 26 deletions

View File

@@ -1,6 +1,6 @@
UglifyJS is released under the BSD license: UglifyJS is released under the BSD license:
Copyright 2012-2018 (c) Mihai Bazon <mihai.bazon@gmail.com> Copyright 2012-2019 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions

View File

@@ -664,8 +664,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `join_vars` (default: `true`) -- join consecutive `var` statements - `join_vars` (default: `true`) -- join consecutive `var` statements
- `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused - `keep_fargs` (default: `strict`) -- Discard unused function arguments. Code
function arguments. You need this for code which relies on `Function.length`. which relies on `Function.length` will break if this is done indiscriminately,
i.e. when passing `true`. Pass `false` to always retain function arguments.
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the - `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on compressor from discarding function names. Useful for code relying on

View File

@@ -376,7 +376,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
} }
}, AST_Scope); }, AST_Scope);
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $propdoc: {
name: "[AST_SymbolDeclaration?] the name of this function", name: "[AST_SymbolDeclaration?] the name of this function",
@@ -614,6 +614,18 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
$propdoc: { $propdoc: {
expression: "[AST_Node] the “container” expression", expression: "[AST_Node] the “container” expression",
property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
},
getProperty: function() {
var p = this.property;
if (p instanceof AST_Constant) {
return p.getValue();
}
if (p instanceof AST_UnaryPrefix
&& p.operator == "void"
&& p.expression instanceof AST_Constant) {
return;
}
return p;
} }
}); });

View File

@@ -69,7 +69,7 @@ function Compressor(options, false_by_default) {
if_return : !false_by_default, if_return : !false_by_default,
inline : !false_by_default, inline : !false_by_default,
join_vars : !false_by_default, join_vars : !false_by_default,
keep_fargs : true, keep_fargs : false_by_default || "strict",
keep_fnames : false, keep_fnames : false,
keep_infinity : false, keep_infinity : false,
loops : !false_by_default, loops : !false_by_default,
@@ -104,6 +104,17 @@ function Compressor(options, false_by_default) {
} }
} }
if (this.options["inline"] === true) this.options["inline"] = 3; if (this.options["inline"] === true) this.options["inline"] = 3;
var keep_fargs = this.options["keep_fargs"];
this.drop_fargs = keep_fargs == "strict" ? function(lambda) {
if (lambda.length_read) return false;
var name = lambda.name;
if (!name) return true;
if (name.fixed_value() !== lambda) return false;
var def = name.definition();
if (def.direct_access) return false;
var escaped = def.escaped;
return escaped && escaped.depth != 1;
} : keep_fargs ? return_false : return_true;
var pure_funcs = this.options["pure_funcs"]; var pure_funcs = this.options["pure_funcs"];
if (typeof pure_funcs == "function") { if (typeof pure_funcs == "function") {
this.pure_funcs = pure_funcs; this.pure_funcs = pure_funcs;
@@ -118,6 +129,8 @@ function Compressor(options, false_by_default) {
} else { } else {
this.pure_funcs = return_true; this.pure_funcs = return_true;
} }
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
var top_retain = this.options["top_retain"]; var top_retain = this.options["top_retain"];
if (top_retain instanceof RegExp) { if (top_retain instanceof RegExp) {
this.top_retain = function(def) { this.top_retain = function(def) {
@@ -141,8 +154,6 @@ function Compressor(options, false_by_default) {
funcs: toplevel, funcs: toplevel,
vars: toplevel vars: toplevel
}; };
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
} }
Compressor.prototype = new TreeTransformer; Compressor.prototype = new TreeTransformer;
@@ -272,14 +283,19 @@ merge(Compressor.prototype, {
self.transform(tt); self.transform(tt);
}); });
function read_property(obj, key) { function read_property(obj, node) {
key = get_value(key); var key = node.getProperty();
if (key instanceof AST_Node) return; if (key instanceof AST_Node) return;
var value; var value;
if (obj instanceof AST_Array) { if (obj instanceof AST_Array) {
var elements = obj.elements; var elements = obj.elements;
if (key == "length") return make_node_from_constant(elements.length, obj); if (key == "length") return make_node_from_constant(elements.length, obj);
if (typeof key == "number" && key in elements) value = elements[key]; if (typeof key == "number" && key in elements) value = elements[key];
} else if (obj instanceof AST_Lambda) {
if (key == "length") {
obj.length_read = true;
return make_node_from_constant(obj.argnames.length, obj);
}
} else if (obj instanceof AST_Object) { } else if (obj instanceof AST_Object) {
key = "" + key; key = "" + key;
var props = obj.properties; var props = obj.properties;
@@ -326,7 +342,7 @@ merge(Compressor.prototype, {
return is_modified(compressor, tw, obj, obj, level + 2); return is_modified(compressor, tw, obj, obj, level + 2);
} }
if (parent instanceof AST_PropAccess && parent.expression === node) { if (parent instanceof AST_PropAccess && parent.expression === node) {
var prop = read_property(value, parent.property); var prop = read_property(value, parent);
return !immutable && is_modified(compressor, tw, parent, prop, level + 1); return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
} }
} }
@@ -490,13 +506,15 @@ merge(Compressor.prototype, {
var obj = tw.parent(level + 1); var obj = tw.parent(level + 1);
mark_escaped(tw, d, scope, obj, obj, level + 2, depth); mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
} else if (parent instanceof AST_PropAccess && node === parent.expression) { } else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property); value = read_property(value, parent);
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1); mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
if (value) return; if (value) return;
} }
if (level > 0) return; if (level > 0) return;
if (parent instanceof AST_Call && node === parent.expression) return;
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return; if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
if (parent instanceof AST_SimpleStatement) return; if (parent instanceof AST_SimpleStatement) return;
if (parent instanceof AST_Unary) return;
d.direct_access = true; d.direct_access = true;
} }
@@ -2217,18 +2235,6 @@ merge(Compressor.prototype, {
})); }));
} }
function get_value(key) {
if (key instanceof AST_Constant) {
return key.getValue();
}
if (key instanceof AST_UnaryPrefix
&& key.operator == "void"
&& key.expression instanceof AST_Constant) {
return;
}
return key;
}
function is_undefined(node, compressor) { function is_undefined(node, compressor) {
return node.is_undefined return node.is_undefined
|| node instanceof AST_Undefined || node instanceof AST_Undefined
@@ -3621,7 +3627,7 @@ merge(Compressor.prototype, {
node.name = null; node.name = null;
} }
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = !compressor.option("keep_fargs"); var trim = compressor.drop_fargs(node);
for (var a = node.argnames, i = a.length; --i >= 0;) { for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i]; var sym = a[i];
if (!(sym.definition().id in in_use_ids)) { if (!(sym.definition().id in in_use_ids)) {
@@ -4068,7 +4074,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) { if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id]; var defs = defs_by_id[node.expression.definition().id];
if (defs) { if (defs) {
var def = defs.get(get_value(node.property)); var def = defs.get(node.getProperty());
var sym = make_node(AST_SymbolRef, node, { var sym = make_node(AST_SymbolRef, node, {
name: def.name, name: def.name,
scope: node.expression.scope, scope: node.expression.scope,
@@ -6652,7 +6658,7 @@ merge(Compressor.prototype, {
|| def.orig.length > 1) { || def.orig.length > 1) {
argname = null; argname = null;
} }
} else if (!argname && !compressor.option("keep_fargs") && index < fn.argnames.length + 5) { } else if (!argname && compressor.drop_fargs(fn) && index < fn.argnames.length + 5) {
while (index >= fn.argnames.length) { while (index >= fn.argnames.length) {
argname = make_node(AST_SymbolFunarg, fn, { argname = make_node(AST_SymbolFunarg, fn, {
name: fn.make_var_name("argument_" + fn.argnames.length), name: fn.make_var_name("argument_" + fn.argnames.length),
@@ -6665,6 +6671,7 @@ merge(Compressor.prototype, {
if (argname && find_if(function(node) { if (argname && find_if(function(node) {
return node.name === argname.name; return node.name === argname.name;
}, fn.argnames) === argname) { }, fn.argnames) === argname) {
expr.definition().reassigned = false;
var sym = make_node(AST_SymbolRef, self, argname); var sym = make_node(AST_SymbolRef, self, argname);
sym.reference({}); sym.reference({});
delete argname.__unused; delete argname.__unused;

View File

@@ -405,6 +405,52 @@ issue_3273_global_strict_reduce_vars: {
] ]
} }
issue_3273_keep_fargs_false: {
options = {
arguments: true,
keep_fargs: false,
reduce_vars: true,
}
input: {
(function() {
"use strict";
arguments[0]++;
console.log(arguments[0]);
})(0);
}
expect: {
(function(argument_0) {
"use strict";
argument_0++;
console.log(argument_0);
})(0);
}
expect_stdout: "1"
}
issue_3273_keep_fargs_strict: {
options = {
arguments: true,
keep_fargs: "strict",
reduce_vars: true,
}
input: {
(function() {
"use strict";
arguments[0]++;
console.log(arguments[0]);
})(0);
}
expect: {
(function(argument_0) {
"use strict";
argument_0++;
console.log(argument_0);
})(0);
}
expect_stdout: "1"
}
issue_3282_1: { issue_3282_1: {
options = { options = {
arguments: true, arguments: true,

1053
test/compress/keep_fargs.js Normal file

File diff suppressed because it is too large Load Diff