support class literals (#4658)

This commit is contained in:
Alex Lam S.L
2021-02-23 14:55:08 +00:00
committed by GitHub
parent e535f19189
commit d68d155f93
18 changed files with 1701 additions and 175 deletions

View File

@@ -348,7 +348,7 @@ merge(Compressor.prototype, {
var props = obj.properties;
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if (!(prop instanceof AST_ObjectKeyVal)) return;
if (!can_hoist_property(prop)) return;
if (!value && props[i].key === key) value = props[i].value;
}
}
@@ -600,7 +600,7 @@ merge(Compressor.prototype, {
if (!value) return false;
return value.is_constant()
|| value instanceof AST_Lambda
|| value instanceof AST_This;
|| value instanceof AST_ObjectIdentity;
}
function has_escaped(d, node, parent) {
@@ -933,6 +933,36 @@ merge(Compressor.prototype, {
exp.left.definition().bool_fn++;
}
});
def(AST_Class, function(tw, descend, compressor) {
var node = this;
node.variables.each(function(def) {
reset_def(tw, compressor, def);
});
if (!node.name) return;
var d = node.name.definition();
if (safe_to_assign(tw, d, true)) {
mark(tw, d);
tw.loop_ids[d.id] = tw.in_loop;
d.fixed = function() {
return node;
};
d.fixed.assigns = [ node ];
if (!is_safe_lexical(d)) d.single_use = false;
} else {
d.fixed = false;
}
});
def(AST_ClassField, function(tw) {
var node = this;
if (node.static) return;
if (node.key instanceof AST_Node) node.key.walk(tw);
if (node.value) {
push(tw);
node.value.walk(tw);
pop(tw);
}
return true;
});
def(AST_Conditional, function(tw) {
this.condition.walk(tw);
push(tw);
@@ -1374,12 +1404,7 @@ merge(Compressor.prototype, {
}
function is_lhs_read_only(lhs, compressor) {
if (lhs instanceof AST_This) return true;
if (lhs instanceof AST_SymbolRef) {
if (lhs.is_immutable()) return true;
var def = lhs.definition();
return compressor.exposed(def) && identifier_atom[def.name];
}
if (lhs instanceof AST_ObjectIdentity) return true;
if (lhs instanceof AST_PropAccess) {
lhs = lhs.expression;
if (lhs instanceof AST_SymbolRef) {
@@ -1390,6 +1415,11 @@ merge(Compressor.prototype, {
if (lhs.is_constant()) return true;
return is_lhs_read_only(lhs, compressor);
}
if (lhs instanceof AST_SymbolRef) {
if (lhs.is_immutable()) return true;
var def = lhs.definition();
return compressor.exposed(def) && identifier_atom[def.name];
}
return false;
}
@@ -1472,10 +1502,14 @@ merge(Compressor.prototype, {
return array;
}
function is_lexical_definition(stat) {
return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
}
function as_statement_array(thing) {
if (thing === null) return [];
if (thing instanceof AST_BlockStatement) return all(thing.body, function(stat) {
return !(stat instanceof AST_Const || stat instanceof AST_Let);
return !is_lexical_definition(stat);
}) ? thing.body : [ thing ];
if (thing instanceof AST_EmptyStatement) return [];
if (thing instanceof AST_Statement) return [ thing ];
@@ -1851,7 +1885,12 @@ merge(Compressor.prototype, {
}
function handle_custom_scan_order(node, tt) {
if (!(node instanceof AST_BlockScope)) return;
if (!(node instanceof AST_BlockScope)) {
if (!(node instanceof AST_ClassProperty && !node.static)) return;
// Skip non-static class property values
if (node.key instanceof AST_Node) node.key = node.key.transform(tt);
return node;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
// Stop upon collision with block-scoped variables
@@ -1905,6 +1944,7 @@ merge(Compressor.prototype, {
if (!lhs.equivalent_to(node.expression)) return false;
return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
}
if (node instanceof AST_Class) return !compressor.has_directive("use strict");
if (node instanceof AST_Debugger) return true;
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
@@ -1990,6 +2030,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_Function) {
return compressor.option("ie8") && node.name && lvalues.has(node.name.name);
}
if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
if (node instanceof AST_PropAccess) {
var exp = node.expression;
return side_effects || !value_def && exp.may_throw_on_access(compressor)
@@ -2003,7 +2044,6 @@ merge(Compressor.prototype, {
return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
}
if (node instanceof AST_Template) return node.tag && !is_raw_tag(compressor, node.tag);
if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
if (node instanceof AST_VarDef) {
if (check_destructured(node.name)) return true;
return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
@@ -2079,7 +2119,7 @@ merge(Compressor.prototype, {
}
arg = null;
}
if (node instanceof AST_This && (fn_strict || !tw.find_parent(AST_Scope))) {
if (node instanceof AST_ObjectIdentity && (fn_strict || !tw.find_parent(AST_Scope))) {
arg = null;
return true;
}
@@ -2473,12 +2513,12 @@ merge(Compressor.prototype, {
expr.left.walk(marker);
expr = expr.right;
}
if (expr instanceof AST_ObjectIdentity) return rhs_exact_match;
if (expr instanceof AST_SymbolRef) {
var value = expr.evaluate(compressor);
if (value === expr) return rhs_exact_match;
return rhs_fuzzy_match(value, rhs_exact_match);
}
if (expr instanceof AST_This) return rhs_exact_match;
if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
if (expr.is_constant()) {
var ev = expr.evaluate(compressor);
@@ -2519,7 +2559,7 @@ merge(Compressor.prototype, {
if (!node) return true;
}
if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
return node instanceof AST_PropAccess || node instanceof AST_This;
return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity;
}
function get_lvalues(expr) {
@@ -2531,7 +2571,7 @@ merge(Compressor.prototype, {
var value;
if (node instanceof AST_SymbolRef) {
value = node.fixed_value() || node;
} else if (node instanceof AST_This) {
} else if (node instanceof AST_ObjectIdentity) {
value = node;
}
if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
@@ -2681,7 +2721,7 @@ merge(Compressor.prototype, {
var stat = statements[i];
if (stat instanceof AST_BlockStatement) {
if (all(stat.body, function(stat) {
return !(stat instanceof AST_Const || stat instanceof AST_Let);
return !is_lexical_definition(stat);
})) {
CHANGED = true;
eliminate_spurious_blocks(stat.body);
@@ -3025,7 +3065,7 @@ merge(Compressor.prototype, {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
} else if (stat || line instanceof AST_Const || line instanceof AST_Let) {
} else if (stat || is_lexical_definition(line)) {
return false;
} else {
stat = line;
@@ -3387,7 +3427,7 @@ merge(Compressor.prototype, {
function push(node) {
if (block) {
block.push(node);
if (node instanceof AST_Const || node instanceof AST_Let) block.required = true;
if (is_lexical_definition(node)) block.required = true;
} else {
target.push(node);
}
@@ -3531,6 +3571,9 @@ merge(Compressor.prototype, {
return prop instanceof AST_ObjectKeyVal;
});
});
def(AST_ObjectIdentity, function(compressor) {
return is_strict(compressor) && !this.scope.new;
});
def(AST_Sequence, function(compressor) {
return this.tail_node()._dot_throw(compressor);
});
@@ -3553,9 +3596,6 @@ merge(Compressor.prototype, {
this._dot_throw = return_false;
return false;
});
def(AST_This, function(compressor) {
return is_strict(compressor) && !this.scope.new;
});
def(AST_UnaryPrefix, function() {
return this.operator == "void";
});
@@ -4077,6 +4117,7 @@ merge(Compressor.prototype, {
});
def(AST_Accessor, return_this);
def(AST_BigInt, return_this);
def(AST_Class, return_this);
def(AST_Node, return_this);
def(AST_Constant, function() {
return this.value;
@@ -4595,6 +4636,9 @@ merge(Compressor.prototype, {
}
return basic_negation(this);
});
def(AST_ClassExpression, function() {
return basic_negation(this);
});
def(AST_Conditional, function(compressor, first_in_statement) {
var self = this.clone();
self.consequent = self.consequent.negate(compressor);
@@ -4673,7 +4717,7 @@ merge(Compressor.prototype, {
|| exp instanceof AST_Object && all(exp.properties, function(prop) {
return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
})
|| exp instanceof AST_This
|| exp instanceof AST_ObjectIdentity
|| exp instanceof AST_Unary);
}
@@ -4697,7 +4741,7 @@ merge(Compressor.prototype, {
var lhs = this.left;
if (!(lhs instanceof AST_PropAccess)) return true;
var node = lhs.expression;
return !(node instanceof AST_This)
return !(node instanceof AST_ObjectIdentity)
|| !node.scope.new
|| lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
|| this.right.has_side_effects(compressor);
@@ -4721,6 +4765,13 @@ merge(Compressor.prototype, {
return this.expression.has_side_effects(compressor)
|| any(this.body, compressor);
});
def(AST_Class, function(compressor) {
return this.extends || any(this.properties, compressor);
});
def(AST_ClassProperty, function(compressor) {
return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
|| this.static && this.value && this.value.has_side_effects(compressor);
});
def(AST_Conditional, function(compressor) {
return this.condition.has_side_effects(compressor)
|| this.consequent.has_side_effects(compressor)
@@ -4760,6 +4811,7 @@ merge(Compressor.prototype, {
return spread_side_effects(exp) || exp.has_side_effects(compressor);
});
});
def(AST_ObjectIdentity, return_false);
def(AST_ObjectProperty, function(compressor) {
return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
|| this.value.has_side_effects(compressor);
@@ -4786,7 +4838,6 @@ merge(Compressor.prototype, {
def(AST_Template, function(compressor) {
return this.tag && !is_raw_tag(compressor, this.tag) || any(this.expressions, compressor);
});
def(AST_This, return_false);
def(AST_Try, function(compressor) {
return any(this.body, compressor)
|| this.bcatch && this.bcatch.has_side_effects(compressor)
@@ -4810,8 +4861,8 @@ merge(Compressor.prototype, {
def(AST_Constant, return_false);
def(AST_EmptyStatement, return_false);
def(AST_Lambda, return_false);
def(AST_ObjectIdentity, return_false);
def(AST_SymbolDeclaration, return_false);
def(AST_This, return_false);
function any(list, compressor) {
for (var i = list.length; --i >= 0;)
@@ -4933,6 +4984,12 @@ merge(Compressor.prototype, {
&& this.right.is_constant_expression()
&& (this.operator != "in" || is_object(this.right));
});
def(AST_Class, function() {
return !this.extends && all(this.properties);
});
def(AST_ClassProperty, function() {
return typeof this.key == "string" && (!this.value || this.value.is_constant_expression());
});
def(AST_Constant, return_true);
def(AST_Lambda, function(scope) {
var self = this;
@@ -4965,7 +5022,7 @@ merge(Compressor.prototype, {
result = false;
return true;
}
if (node instanceof AST_This) {
if (node instanceof AST_ObjectIdentity) {
if (scopes.length == 0 && is_arrow(self)) result = false;
return true;
}
@@ -5042,7 +5099,7 @@ merge(Compressor.prototype, {
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
case 1:
var stat = node.body[0];
if (stat instanceof AST_Const || stat instanceof AST_Let) return node;
if (is_lexical_definition(stat)) return node;
if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node;
return stat;
}
@@ -5347,6 +5404,10 @@ merge(Compressor.prototype, {
});
return true;
}
if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) {
references[node.definition().id] = false;
return true;
}
if (node instanceof AST_SymbolRef) {
mark(node, true);
return true;
@@ -5549,6 +5610,12 @@ merge(Compressor.prototype, {
}
}
function to_class_expr(defcl, drop_name) {
var cl = make_node(AST_ClassExpression, defcl, defcl);
cl.name = drop_name ? null : make_node(AST_SymbolClass, defcl.name, defcl.name);
return cl;
}
function to_func_expr(defun, drop_name) {
var ctor;
switch (defun.CTOR) {
@@ -5658,6 +5725,25 @@ merge(Compressor.prototype, {
}
if (node === self) return;
if (scope === self) {
if (node instanceof AST_DefClass) {
var def = node.name.definition();
if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
in_use.push(def);
}
if (node.extends) node.extends.walk(tw);
var is_export = tw.parent() instanceof AST_ExportDefault;
node.properties.forEach(function(prop) {
if (prop.key instanceof AST_Node) prop.key.walk(tw);
if (!prop.value) return;
if (is_export || prop instanceof AST_ClassField && prop.static) {
prop.value.walk(tw);
} else {
initializations.add(def.id, prop.value);
}
});
return true;
}
if (node instanceof AST_LambdaDefinition) {
var def = node.name.definition();
if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
@@ -5855,13 +5941,32 @@ merge(Compressor.prototype, {
}
if (node instanceof AST_Call) calls_to_drop_args.push(node);
if (scope !== self) return;
if (drop_funcs && node !== self && node instanceof AST_DefClass) {
var def = node.name.definition();
if (!(def.id in in_use_ids)) {
log(node.name, "Dropping unused class {name}");
def.eliminated++;
descend(node, tt);
if (parent instanceof AST_ExportDefault) return to_class_expr(node, true);
var trimmed = node.drop_side_effect_free(compressor, true);
if (trimmed === node) trimmed = to_class_expr(node, true);
if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed });
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
}
}
if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
unused_fn_names.push(node);
}
if (node instanceof AST_Lambda) {
if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
var def = node.name.definition();
if (!(def.id in in_use_ids)) {
log(node.name, "Dropping unused function {name}");
def.eliminated++;
if (parent instanceof AST_ExportDefault) return to_func_expr(node, true);
if (parent instanceof AST_ExportDefault) {
descend_scope();
return to_func_expr(node, true);
}
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
}
}
@@ -6151,16 +6256,20 @@ merge(Compressor.prototype, {
return node;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, tt);
scope = save_scope;
descend_scope();
return node;
}
if (node instanceof AST_SymbolImport) {
if (!compressor.option("imports") || node.definition().id in in_use_ids) return node;
return in_list ? List.skip : null;
}
function descend_scope() {
var save_scope = scope;
scope = node;
descend(node, tt);
scope = save_scope;
}
}, function(node, in_list) {
if (node instanceof AST_BlockStatement) {
return trim_block(node, tt.parent(), in_list);
@@ -6740,6 +6849,7 @@ merge(Compressor.prototype, {
if (!compressor.option("booleans")) return;
var bool_returns = map_bool_returns(this);
if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
if (compressor.parent() instanceof AST_ExportDefault) return;
process_boolean_returns(this, compressor);
});
AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
@@ -6915,9 +7025,7 @@ merge(Compressor.prototype, {
if (sym.fixed_value() !== right) return;
return right instanceof AST_Object
&& right.properties.length > 0
&& all(right.properties, function(prop) {
return prop instanceof AST_ObjectKeyVal && typeof prop.key == "string";
})
&& all(right.properties, can_hoist_property)
&& all(def.references, function(ref) {
return ref.fixed_value() === right;
})
@@ -6943,7 +7051,6 @@ merge(Compressor.prototype, {
// returned if nothing changed.
function trim(nodes, compressor, first_in_statement, spread) {
var len = nodes.length;
if (!len) return null;
var ret = [], changed = false;
for (var i = 0; i < len; i++) {
var node = nodes[i];
@@ -6959,7 +7066,7 @@ merge(Compressor.prototype, {
first_in_statement = false;
}
}
return changed ? ret.length ? ret : null : nodes;
return ret.length ? changed ? ret : nodes : null;
}
function array_spread(node, compressor, first_in_statement) {
var exp = node.expression;
@@ -7106,6 +7213,33 @@ merge(Compressor.prototype, {
}
return self;
});
def(AST_Class, function(compressor, first_in_statement) {
if (this.extends) return this;
var exprs = [], values = [];
this.properties.forEach(function(prop) {
if (prop.key instanceof AST_Node) exprs.push(prop.key);
if (prop instanceof AST_ClassField && prop.static && prop.value) values.push(prop.value);
});
exprs = trim(exprs, compressor, first_in_statement);
values = trim(values, compressor, first_in_statement);
if (!exprs) {
if (!values) return null;
exprs = [];
}
if (values) {
var fn = make_node(AST_Arrow, this, {
argnames: [],
body: [],
value: make_sequence(this, values),
});
fn.init_vars(this.parent_scope);
exprs.push(make_node(AST_Call, this, {
args: [],
expression: fn,
}));
}
return make_sequence(this, exprs);
});
def(AST_Conditional, function(compressor) {
var consequent = this.consequent.drop_side_effect_free(compressor);
var alternative = this.alternative.drop_side_effect_free(compressor);
@@ -7178,6 +7312,7 @@ merge(Compressor.prototype, {
}) : node;
}));
});
def(AST_ObjectIdentity, return_null);
def(AST_Sequence, function(compressor, first_in_statement) {
var expressions = trim(this.expressions, compressor, first_in_statement);
if (!expressions) return null;
@@ -7220,7 +7355,6 @@ merge(Compressor.prototype, {
if (expressions.length == 0) return null;
return make_sequence(this, expressions).drop_side_effect_free(compressor, first_in_statement);
});
def(AST_This, return_null);
def(AST_Unary, function(compressor, first_in_statement) {
var exp = this.expression;
if (unary_side_effects[this.operator]) {
@@ -7485,7 +7619,7 @@ merge(Compressor.prototype, {
});
OPT(AST_ForEnumeration, function(self, compressor) {
if (compressor.option("varify") && (self.init instanceof AST_Const || self.init instanceof AST_Let)) {
if (compressor.option("varify") && is_lexical_definition(self.init)) {
var name = self.init.definitions[0].name;
if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
&& !name.match_symbol(function(node) {
@@ -10009,6 +10143,7 @@ merge(Compressor.prototype, {
def.single_use = false;
fixed._squeezed = true;
fixed.single_use = true;
if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
if (fixed instanceof AST_Lambda) {
var scope = self.scope.resolve();
@@ -10719,20 +10854,21 @@ merge(Compressor.prototype, {
|| alternative.expression instanceof AST_PropAccess)
|| is_tail_equivalent(consequent.expression, alternative.expression);
}
if (consequent instanceof AST_Dot) return consequent.property == alternative.property;
if (consequent instanceof AST_Sub) return consequent.property.equivalent_to(alternative.property);
if (!(consequent instanceof AST_PropAccess)) return;
var p = consequent.property;
var q = alternative.property;
return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
&& !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
}
function combine_tail(consequent, alternative, top) {
if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
condition: condition,
consequent: consequent,
alternative: alternative
alternative: alternative,
});
var exp = combine_tail(consequent.expression, alternative.expression);
if (!exp) return;
var node = consequent.clone();
node.expression = exp;
node.expression = combine_tail(consequent.expression, alternative.expression);
return node;
}
@@ -10960,6 +11096,21 @@ merge(Compressor.prototype, {
}
});
AST_Arrow.DEFMETHOD("contains_super", return_false);
AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
AST_Lambda.DEFMETHOD("contains_super", function() {
var result;
var self = this;
self.walk(new TreeWalker(function(node) {
if (result) return true;
if (node instanceof AST_Super) return result = true;
if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
}));
return result;
});
AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
AST_Scope.DEFMETHOD("contains_super", return_false);
AST_Arrow.DEFMETHOD("contains_this", return_false);
AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
AST_Scope.DEFMETHOD("contains_this", function() {
@@ -10973,6 +11124,12 @@ merge(Compressor.prototype, {
return result;
});
function can_hoist_property(prop) {
return prop instanceof AST_ObjectKeyVal
&& typeof prop.key == "string"
&& !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
}
AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
if (!compressor.option("properties")) return;
var expr = this.expression;
@@ -10981,9 +11138,7 @@ merge(Compressor.prototype, {
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if (prop.key != key) continue;
if (!all(props, function(prop) {
return prop instanceof AST_ObjectKeyVal && typeof prop.key == "string";
})) break;
if (!all(props, can_hoist_property)) break;
if (!safe_to_flatten(prop.value, compressor)) break;
return make_node(AST_Sub, this, {
expression: make_node(AST_Array, expr, {
@@ -11145,7 +11300,7 @@ merge(Compressor.prototype, {
key = prop.key = "" + key;
}
}
if (prop instanceof AST_ObjectKeyVal && typeof key == "string") {
if (can_hoist_property(prop)) {
if (prop.value.has_side_effects(compressor)) flush();
keys.add(key, prop);
} else {