aboutsummaryrefslogtreecommitdiff
path: root/lib/compress.js
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2021-05-03 03:08:29 +0100
committerGitHub <noreply@github.com>2021-05-03 10:08:29 +0800
commitf0de9a8b5d577221d3180c9e135d5cbda8a337c3 (patch)
treece7ee35f88c54d4d8b1a6721089e5586af781adf /lib/compress.js
parent203f4b7ad9994f85472402c5be8474755d1d835d (diff)
downloadtracifyjs-f0de9a8b5d577221d3180c9e135d5cbda8a337c3.tar.gz
tracifyjs-f0de9a8b5d577221d3180c9e135d5cbda8a337c3.zip
support optional chaining operator (#4899)
Diffstat (limited to 'lib/compress.js')
-rw-r--r--lib/compress.js86
1 files changed, 61 insertions, 25 deletions
diff --git a/lib/compress.js b/lib/compress.js
index b1971f8a..d58b68bc 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -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)) {