support optional chaining operator (#4899)
This commit is contained in:
@@ -82,6 +82,7 @@ function Compressor(options, false_by_default) {
|
||||
merge_vars : !false_by_default,
|
||||
negate_iife : !false_by_default,
|
||||
objects : !false_by_default,
|
||||
optional_chains : !false_by_default,
|
||||
passes : 1,
|
||||
properties : !false_by_default,
|
||||
pure_funcs : null,
|
||||
@@ -698,9 +699,7 @@ merge(Compressor.prototype, {
|
||||
if (save) fixed = function() {
|
||||
return make_node(AST_Sub, node, {
|
||||
expression: save(),
|
||||
property: make_node(AST_Number, node, {
|
||||
value: index
|
||||
})
|
||||
property: make_node(AST_Number, node, { value: index }),
|
||||
});
|
||||
};
|
||||
node.walk(scanner);
|
||||
@@ -958,15 +957,18 @@ merge(Compressor.prototype, {
|
||||
exp.walk(tw);
|
||||
if (iife) delete exp.reduce_vars;
|
||||
return true;
|
||||
} else if (exp instanceof AST_SymbolRef) {
|
||||
}
|
||||
if (exp instanceof AST_SymbolRef) {
|
||||
var def = exp.definition();
|
||||
if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++;
|
||||
if (!(def.fixed instanceof AST_LambdaDefinition)) return;
|
||||
var defun = mark_defun(tw, def);
|
||||
if (!defun) return;
|
||||
descend();
|
||||
defun.walk(tw);
|
||||
return true;
|
||||
if (def.fixed instanceof AST_LambdaDefinition) {
|
||||
var defun = mark_defun(tw, def);
|
||||
if (defun) {
|
||||
descend();
|
||||
defun.walk(tw);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (this.TYPE == "Call"
|
||||
&& exp instanceof AST_Assign
|
||||
&& exp.operator == "="
|
||||
@@ -974,6 +976,14 @@ merge(Compressor.prototype, {
|
||||
&& tw.in_boolean_context()) {
|
||||
exp.left.definition().bool_fn++;
|
||||
}
|
||||
if (!this.optional) return;
|
||||
exp.walk(tw);
|
||||
push(tw);
|
||||
this.args.forEach(function(arg) {
|
||||
arg.walk(tw);
|
||||
});
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
def(AST_Class, function(tw, descend, compressor) {
|
||||
var node = this;
|
||||
@@ -1143,6 +1153,14 @@ merge(Compressor.prototype, {
|
||||
walk_defuns(tw, fn);
|
||||
return true;
|
||||
});
|
||||
def(AST_Sub, function(tw) {
|
||||
if (!this.optional) return;
|
||||
this.expression.walk(tw);
|
||||
push(tw);
|
||||
this.property.walk(tw);
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
def(AST_Switch, function(tw, descend, compressor) {
|
||||
this.variables.each(function(def) {
|
||||
reset_def(tw, compressor, def);
|
||||
@@ -2068,9 +2086,11 @@ merge(Compressor.prototype, {
|
||||
function in_conditional(node, parent) {
|
||||
if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
|
||||
if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
|
||||
if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
|
||||
if (parent instanceof AST_Case) return parent.expression !== node;
|
||||
if (parent instanceof AST_Conditional) return parent.condition !== node;
|
||||
return parent instanceof AST_If && parent.condition !== node;
|
||||
if (parent instanceof AST_If) return parent.condition !== node;
|
||||
if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
|
||||
}
|
||||
|
||||
function is_last_node(node, parent) {
|
||||
@@ -2132,7 +2152,8 @@ merge(Compressor.prototype, {
|
||||
var exp = node.expression;
|
||||
return side_effects
|
||||
|| exp instanceof AST_SymbolRef && is_arguments(exp.definition())
|
||||
|| !value_def && (in_try || !lhs_local) && exp.may_throw_on_access(compressor);
|
||||
|| !value_def && (in_try || !lhs_local)
|
||||
&& !node.optional && exp.may_throw_on_access(compressor);
|
||||
}
|
||||
if (node instanceof AST_Spread) return true;
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
@@ -4983,7 +5004,7 @@ merge(Compressor.prototype, {
|
||||
return any(this.properties, compressor);
|
||||
});
|
||||
def(AST_Dot, function(compressor) {
|
||||
return this.expression.may_throw_on_access(compressor)
|
||||
return !this.optional && this.expression.may_throw_on_access(compressor)
|
||||
|| this.expression.has_side_effects(compressor);
|
||||
});
|
||||
def(AST_EmptyStatement, return_false);
|
||||
@@ -5014,7 +5035,7 @@ merge(Compressor.prototype, {
|
||||
return this.body.has_side_effects(compressor);
|
||||
});
|
||||
def(AST_Sub, function(compressor) {
|
||||
return this.expression.may_throw_on_access(compressor)
|
||||
return !this.optional && this.expression.may_throw_on_access(compressor)
|
||||
|| this.expression.has_side_effects(compressor)
|
||||
|| this.property.has_side_effects(compressor);
|
||||
});
|
||||
@@ -5102,7 +5123,7 @@ merge(Compressor.prototype, {
|
||||
return any(this.definitions, compressor);
|
||||
});
|
||||
def(AST_Dot, function(compressor) {
|
||||
return this.expression.may_throw_on_access(compressor)
|
||||
return !this.optional && this.expression.may_throw_on_access(compressor)
|
||||
|| this.expression.may_throw(compressor);
|
||||
});
|
||||
def(AST_If, function(compressor) {
|
||||
@@ -5130,7 +5151,7 @@ merge(Compressor.prototype, {
|
||||
return this.body.may_throw(compressor);
|
||||
});
|
||||
def(AST_Sub, function(compressor) {
|
||||
return this.expression.may_throw_on_access(compressor)
|
||||
return !this.optional && this.expression.may_throw_on_access(compressor)
|
||||
|| this.expression.may_throw(compressor)
|
||||
|| this.property.may_throw(compressor);
|
||||
});
|
||||
@@ -7592,7 +7613,7 @@ merge(Compressor.prototype, {
|
||||
def(AST_Constant, return_null);
|
||||
def(AST_Dot, function(compressor, first_in_statement) {
|
||||
var expr = this.expression;
|
||||
if (expr.may_throw_on_access(compressor)) return this;
|
||||
if (!this.optional && expr.may_throw_on_access(compressor)) return this;
|
||||
return expr.drop_side_effect_free(compressor, first_in_statement);
|
||||
});
|
||||
def(AST_Function, function(compressor) {
|
||||
@@ -8510,6 +8531,18 @@ merge(Compressor.prototype, {
|
||||
OPT(AST_Const, varify);
|
||||
OPT(AST_Let, varify);
|
||||
|
||||
function trim_optional_chain(self, compressor) {
|
||||
if (!compressor.option("optional_chains")) return;
|
||||
if (!self.optional) return;
|
||||
var expr = self.expression;
|
||||
var ev = expr.evaluate(compressor, true);
|
||||
if (ev == null) return make_node(AST_UnaryPrefix, self, {
|
||||
operator: "void",
|
||||
expression: expr,
|
||||
}).optimize(compressor);
|
||||
if (!(ev instanceof AST_Node)) self.optional = false;
|
||||
}
|
||||
|
||||
function lift_sequence_in_expression(node, compressor) {
|
||||
var exp = node.expression;
|
||||
if (!(exp instanceof AST_Sequence)) return node;
|
||||
@@ -8616,6 +8649,8 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Call, function(self, compressor) {
|
||||
var exp = self.expression;
|
||||
var terminated = trim_optional_chain(self, compressor);
|
||||
if (terminated) return terminated;
|
||||
if (compressor.option("sequences")) {
|
||||
if (exp instanceof AST_PropAccess) {
|
||||
var seq = lift_sequence_in_expression(exp, compressor);
|
||||
@@ -8828,7 +8863,7 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Call, self, {
|
||||
expression: make_node(AST_Dot, exp, {
|
||||
expression: exp.expression,
|
||||
property: "call"
|
||||
property: "call",
|
||||
}),
|
||||
args: args
|
||||
}).optimize(compressor);
|
||||
@@ -11370,6 +11405,8 @@ merge(Compressor.prototype, {
|
||||
OPT(AST_Sub, function(self, compressor) {
|
||||
var expr = self.expression;
|
||||
var prop = self.property;
|
||||
var terminated = trim_optional_chain(self, compressor);
|
||||
if (terminated) return terminated;
|
||||
if (compressor.option("properties")) {
|
||||
var key = prop.evaluate(compressor);
|
||||
if (key !== prop) {
|
||||
@@ -11388,8 +11425,9 @@ merge(Compressor.prototype, {
|
||||
if (is_identifier_string(property)
|
||||
&& property.length <= prop.print_to_string().length + 1) {
|
||||
return make_node(AST_Dot, self, {
|
||||
optional: self.optional,
|
||||
expression: expr,
|
||||
property: property
|
||||
property: property,
|
||||
}).optimize(compressor);
|
||||
}
|
||||
}
|
||||
@@ -11496,12 +11534,8 @@ merge(Compressor.prototype, {
|
||||
values.push(retValue);
|
||||
return make_sequence(self, values).optimize(compressor);
|
||||
} else return make_node(AST_Sub, self, {
|
||||
expression: make_node(AST_Array, expr, {
|
||||
elements: values
|
||||
}),
|
||||
property: make_node(AST_Number, prop, {
|
||||
value: index
|
||||
})
|
||||
expression: make_node(AST_Array, expr, { elements: values }),
|
||||
property: make_node(AST_Number, prop, { value: index }),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -11597,6 +11631,8 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
var parent = compressor.parent();
|
||||
if (is_lhs(compressor.self(), parent)) return self;
|
||||
var terminated = trim_optional_chain(self, compressor);
|
||||
if (terminated) return terminated;
|
||||
if (compressor.option("sequences")
|
||||
&& parent.TYPE != "Call"
|
||||
&& !(parent instanceof AST_ForEnumeration && parent.init === self)) {
|
||||
|
||||
Reference in New Issue
Block a user