enhance reduce_vars & side_effects (#4712)
This commit is contained in:
@@ -405,6 +405,10 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function is_lambda(node) {
|
||||||
|
return node instanceof AST_Class || node instanceof AST_Lambda;
|
||||||
|
}
|
||||||
|
|
||||||
function is_arguments(def) {
|
function is_arguments(def) {
|
||||||
return def.name == "arguments" && def.scope.uses_arguments;
|
return def.name == "arguments" && def.scope.uses_arguments;
|
||||||
}
|
}
|
||||||
@@ -600,10 +604,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function is_immutable(value) {
|
function is_immutable(value) {
|
||||||
if (!value) return false;
|
if (!value) return false;
|
||||||
return value.is_constant()
|
return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
|
||||||
|| value instanceof AST_Class
|
|
||||||
|| value instanceof AST_Lambda
|
|
||||||
|| value instanceof AST_ObjectIdentity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function has_escaped(d, node, parent) {
|
function has_escaped(d, node, parent) {
|
||||||
@@ -959,6 +960,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
node.properties.filter(function(prop) {
|
node.properties.filter(function(prop) {
|
||||||
|
reset_flags(prop);
|
||||||
if (prop.key instanceof AST_Node) prop.key.walk(tw);
|
if (prop.key instanceof AST_Node) prop.key.walk(tw);
|
||||||
return prop.value;
|
return prop.value;
|
||||||
}).forEach(function(prop) {
|
}).forEach(function(prop) {
|
||||||
@@ -1154,7 +1156,7 @@ merge(Compressor.prototype, {
|
|||||||
d.recursive_refs++;
|
d.recursive_refs++;
|
||||||
} else if (value && ref_once(compressor, d)) {
|
} else if (value && ref_once(compressor, d)) {
|
||||||
d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
|
d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
|
||||||
d.single_use = value instanceof AST_Lambda
|
d.single_use = is_lambda(value)
|
||||||
&& !value.pinned()
|
&& !value.pinned()
|
||||||
&& (!d.in_loop || tw.parent() instanceof AST_Call)
|
&& (!d.in_loop || tw.parent() instanceof AST_Call)
|
||||||
|| !d.in_loop
|
|| !d.in_loop
|
||||||
@@ -3597,7 +3599,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!is_strict(compressor)) return false;
|
if (!is_strict(compressor)) return false;
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
|
if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
|
||||||
return !(this.property == "prototype" && (exp instanceof AST_Class || exp instanceof AST_Lambda));
|
return !(this.property == "prototype" && is_lambda(exp));
|
||||||
});
|
});
|
||||||
def(AST_Lambda, return_false);
|
def(AST_Lambda, return_false);
|
||||||
def(AST_Null, return_true);
|
def(AST_Null, return_true);
|
||||||
@@ -4806,7 +4808,12 @@ merge(Compressor.prototype, {
|
|||||||
|| any(this.body, compressor);
|
|| any(this.body, compressor);
|
||||||
});
|
});
|
||||||
def(AST_Class, function(compressor) {
|
def(AST_Class, function(compressor) {
|
||||||
return this.extends || any(this.properties, compressor);
|
var base = this.extends;
|
||||||
|
if (base) {
|
||||||
|
if (base instanceof AST_SymbolRef) base = base.fixed_value();
|
||||||
|
if (!is_lambda(base) || is_arrow(base)) return true;
|
||||||
|
}
|
||||||
|
return any(this.properties, compressor);
|
||||||
});
|
});
|
||||||
def(AST_ClassProperty, function(compressor) {
|
def(AST_ClassProperty, function(compressor) {
|
||||||
return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
|
return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
|
||||||
@@ -5009,9 +5016,9 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
// determine if expression is constant
|
// determine if expression is constant
|
||||||
(function(def) {
|
(function(def) {
|
||||||
function all_constant(list) {
|
function all_constant(list, scope) {
|
||||||
for (var i = list.length; --i >= 0;)
|
for (var i = list.length; --i >= 0;)
|
||||||
if (!list[i].is_constant_expression())
|
if (!list[i].is_constant_expression(scope))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -5024,11 +5031,13 @@ merge(Compressor.prototype, {
|
|||||||
&& this.right.is_constant_expression()
|
&& this.right.is_constant_expression()
|
||||||
&& (this.operator != "in" || is_object(this.right));
|
&& (this.operator != "in" || is_object(this.right));
|
||||||
});
|
});
|
||||||
def(AST_Class, function() {
|
def(AST_Class, function(scope) {
|
||||||
return !this.extends && all_constant(this.properties);
|
var base = this.extends;
|
||||||
|
if (base && (!is_lambda(base) || is_arrow(base))) return false;
|
||||||
|
return all_constant(this.properties, scope);
|
||||||
});
|
});
|
||||||
def(AST_ClassProperty, function() {
|
def(AST_ClassProperty, function(scope) {
|
||||||
return typeof this.key == "string" && (!this.value || this.value.is_constant_expression());
|
return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope));
|
||||||
});
|
});
|
||||||
def(AST_Constant, return_true);
|
def(AST_Constant, return_true);
|
||||||
def(AST_Lambda, function(scope) {
|
def(AST_Lambda, function(scope) {
|
||||||
@@ -7289,18 +7298,28 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
def(AST_Class, function(compressor, first_in_statement) {
|
def(AST_Class, function(compressor, first_in_statement) {
|
||||||
if (this.extends) return this;
|
|
||||||
var exprs = [], values = [];
|
var exprs = [], values = [];
|
||||||
this.properties.forEach(function(prop) {
|
this.properties.forEach(function(prop) {
|
||||||
if (prop.key instanceof AST_Node) exprs.push(prop.key);
|
if (prop.key instanceof AST_Node) exprs.push(prop.key);
|
||||||
if (prop instanceof AST_ClassField && prop.static && prop.value) values.push(prop.value);
|
if (prop instanceof AST_ClassField && prop.static && prop.value) values.push(prop.value);
|
||||||
});
|
});
|
||||||
|
var base = this.extends;
|
||||||
|
if (base) {
|
||||||
|
if (base instanceof AST_SymbolRef) base = base.fixed_value();
|
||||||
|
base = !is_lambda(base) || is_arrow(base);
|
||||||
|
if (!base) exprs.unshift(this.extends);
|
||||||
|
}
|
||||||
exprs = trim(exprs, compressor, first_in_statement);
|
exprs = trim(exprs, compressor, first_in_statement);
|
||||||
|
if (exprs) first_in_statement = false;
|
||||||
values = trim(values, compressor, first_in_statement);
|
values = trim(values, compressor, first_in_statement);
|
||||||
if (!exprs) {
|
if (!exprs) {
|
||||||
if (!values) return null;
|
if (!base && !values) return null;
|
||||||
exprs = [];
|
exprs = [];
|
||||||
}
|
}
|
||||||
|
if (base) exprs.unshift(make_node(AST_ClassExpression, this, {
|
||||||
|
extends: this.extends,
|
||||||
|
properties: [],
|
||||||
|
}));
|
||||||
if (values) {
|
if (values) {
|
||||||
var fn = make_node(AST_Arrow, this, {
|
var fn = make_node(AST_Arrow, this, {
|
||||||
argnames: [],
|
argnames: [],
|
||||||
@@ -10187,7 +10206,7 @@ merge(Compressor.prototype, {
|
|||||||
function recursive_ref(compressor, def) {
|
function recursive_ref(compressor, def) {
|
||||||
var level = 0, node = compressor.self();
|
var level = 0, node = compressor.self();
|
||||||
do {
|
do {
|
||||||
if (node instanceof AST_Lambda && node.name && node.name.definition() === def) return node;
|
if (is_lambda(node) && node.name && node.name.definition() === def) return node;
|
||||||
} while (node = compressor.parent(level++));
|
} while (node = compressor.parent(level++));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10211,7 +10230,7 @@ merge(Compressor.prototype, {
|
|||||||
var fixed = self.fixed_value();
|
var fixed = self.fixed_value();
|
||||||
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
|
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
|
||||||
if (single_use) {
|
if (single_use) {
|
||||||
if (fixed instanceof AST_Lambda) {
|
if (is_lambda(fixed)) {
|
||||||
if ((def.scope !== self.scope.resolve() || def.in_loop)
|
if ((def.scope !== self.scope.resolve() || def.in_loop)
|
||||||
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
|
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
|
||||||
single_use = false;
|
single_use = false;
|
||||||
@@ -10232,6 +10251,8 @@ merge(Compressor.prototype, {
|
|||||||
} else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
|
} else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
|
||||||
|| fixed.name.name == "yield" && is_generator(fixed))) {
|
|| fixed.name.name == "yield" && is_generator(fixed))) {
|
||||||
single_use = false;
|
single_use = false;
|
||||||
|
} else if (fixed.has_side_effects(compressor)) {
|
||||||
|
single_use = false;
|
||||||
}
|
}
|
||||||
if (single_use) fixed.parent_scope = self.scope;
|
if (single_use) fixed.parent_scope = self.scope;
|
||||||
} else if (!fixed || !fixed.is_constant_expression() || fixed.drop_side_effect_free(compressor)) {
|
} else if (!fixed || !fixed.is_constant_expression() || fixed.drop_side_effect_free(compressor)) {
|
||||||
@@ -10244,7 +10265,7 @@ merge(Compressor.prototype, {
|
|||||||
fixed.single_use = true;
|
fixed.single_use = true;
|
||||||
if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
|
if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
|
||||||
if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
|
if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
|
||||||
if (fixed instanceof AST_Lambda) {
|
if (is_lambda(fixed)) {
|
||||||
var scope = self.scope.resolve();
|
var scope = self.scope.resolve();
|
||||||
fixed.enclosed.forEach(function(def) {
|
fixed.enclosed.forEach(function(def) {
|
||||||
if (fixed.variables.has(def.name)) return;
|
if (fixed.variables.has(def.name)) return;
|
||||||
@@ -10259,11 +10280,19 @@ merge(Compressor.prototype, {
|
|||||||
var defun_def = value.name.definition();
|
var defun_def = value.name.definition();
|
||||||
var lambda_def = value.variables.get(value.name.name);
|
var lambda_def = value.variables.get(value.name.name);
|
||||||
var name = lambda_def && lambda_def.orig[0];
|
var name = lambda_def && lambda_def.orig[0];
|
||||||
if (!(name instanceof AST_SymbolLambda)) {
|
var def_fn_name, symbol_type;
|
||||||
name = make_node(AST_SymbolLambda, value.name, value.name);
|
if (value instanceof AST_Class) {
|
||||||
|
def_fn_name = "def_function";
|
||||||
|
symbol_type = AST_SymbolClass;
|
||||||
|
} else {
|
||||||
|
def_fn_name = "def_variable";
|
||||||
|
symbol_type = AST_SymbolLambda;
|
||||||
|
}
|
||||||
|
if (!(name instanceof symbol_type)) {
|
||||||
|
name = make_node(symbol_type, value.name, value.name);
|
||||||
name.scope = value;
|
name.scope = value;
|
||||||
value.name = name;
|
value.name = name;
|
||||||
lambda_def = value.def_function(name);
|
lambda_def = value[def_fn_name](name);
|
||||||
lambda_def.recursive_refs = def.recursive_refs;
|
lambda_def.recursive_refs = def.recursive_refs;
|
||||||
}
|
}
|
||||||
value.walk(new TreeWalker(function(node) {
|
value.walk(new TreeWalker(function(node) {
|
||||||
@@ -10275,13 +10304,13 @@ merge(Compressor.prototype, {
|
|||||||
} else {
|
} else {
|
||||||
def.single_use = false;
|
def.single_use = false;
|
||||||
var fn = node.fixed_value();
|
var fn = node.fixed_value();
|
||||||
if (!(fn instanceof AST_Lambda)) return;
|
if (!is_lambda(fn)) return;
|
||||||
if (!fn.name) return;
|
if (!fn.name) return;
|
||||||
if (fn.name.definition() !== def) return;
|
if (fn.name.definition() !== def) return;
|
||||||
if (def.scope !== fn.name.scope) return;
|
if (def.scope !== fn.name.scope) return;
|
||||||
if (fixed.variables.get(fn.name.name) !== def) return;
|
if (fixed.variables.get(fn.name.name) !== def) return;
|
||||||
fn.name = fn.name.clone();
|
fn.name = fn.name.clone();
|
||||||
var value_def = value.variables.get(fn.name.name) || value.def_function(fn.name);
|
var value_def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
|
||||||
node.thedef = value_def;
|
node.thedef = value_def;
|
||||||
value_def.references.push(node);
|
value_def.references.push(node);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,6 +269,43 @@ block_scoped: {
|
|||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop_extends: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
passes: 2,
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
var f = () => {};
|
||||||
|
class A extends f {
|
||||||
|
get p() {}
|
||||||
|
}
|
||||||
|
A.q = 42;
|
||||||
|
return class B extends A {};
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
try {
|
||||||
|
(class extends (() => {}) {});
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
keep_extends: {
|
keep_extends: {
|
||||||
options = {
|
options = {
|
||||||
toplevel: true,
|
toplevel: true,
|
||||||
@@ -370,7 +407,7 @@ static_side_effects: {
|
|||||||
node_version: ">=12"
|
node_version: ">=12"
|
||||||
}
|
}
|
||||||
|
|
||||||
single_use: {
|
single_use_1: {
|
||||||
options = {
|
options = {
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
toplevel: true,
|
toplevel: true,
|
||||||
@@ -389,6 +426,163 @@ single_use: {
|
|||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single_use_2: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
class A {
|
||||||
|
f(a) {
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new A().f("PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
new class {
|
||||||
|
f(a) {
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
}().f("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
single_use_3: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
class A {
|
||||||
|
f() {
|
||||||
|
return A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(typeof new A().f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
console.log(typeof new class A {
|
||||||
|
f() {
|
||||||
|
return A;
|
||||||
|
}
|
||||||
|
}().f());
|
||||||
|
}
|
||||||
|
expect_stdout: "function"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
single_use_4: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
console.log(new class A {
|
||||||
|
f() {
|
||||||
|
return typeof A;
|
||||||
|
}
|
||||||
|
}().f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
console.log(new class A {
|
||||||
|
f() {
|
||||||
|
return typeof A;
|
||||||
|
}
|
||||||
|
}().f());
|
||||||
|
}
|
||||||
|
expect_stdout: "function"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
single_use_5: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
class A extends f {
|
||||||
|
f() {
|
||||||
|
console.log("bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("baz");
|
||||||
|
new A().f();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
class A extends f {
|
||||||
|
f() {
|
||||||
|
console.log("bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("baz");
|
||||||
|
new A().f();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"baz",
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
single_use_6: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
class A {
|
||||||
|
[(console.log("foo"), "f")]() {
|
||||||
|
console.log("bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("baz");
|
||||||
|
new A().f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
class A {
|
||||||
|
[(console.log("foo"), "f")]() {
|
||||||
|
console.log("bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("baz");
|
||||||
|
new A().f();
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"foo",
|
||||||
|
"baz",
|
||||||
|
"bar",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
collapse_non_strict: {
|
collapse_non_strict: {
|
||||||
options = {
|
options = {
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user