enhance keep_fargs (#3409)
This commit is contained in:
2
LICENSE
2
LICENSE
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
14
lib/ast.js
14
lib/ast.js
@@ -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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
1053
test/compress/keep_fargs.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user