diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2021-05-03 03:08:29 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-03 10:08:29 +0800 |
commit | f0de9a8b5d577221d3180c9e135d5cbda8a337c3 (patch) | |
tree | ce7ee35f88c54d4d8b1a6721089e5586af781adf /lib/compress.js | |
parent | 203f4b7ad9994f85472402c5be8474755d1d835d (diff) | |
download | tracifyjs-f0de9a8b5d577221d3180c9e135d5cbda8a337c3.tar.gz tracifyjs-f0de9a8b5d577221d3180c9e135d5cbda8a337c3.zip |
support optional chaining operator (#4899)
Diffstat (limited to 'lib/compress.js')
-rw-r--r-- | lib/compress.js | 86 |
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)) { |