fix corner cases in `properties` (#5178)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 16 Nov 2021 20:21:44 +0000 (20:21 +0000)
committerGitHub <noreply@github.com>
Tue, 16 Nov 2021 20:21:44 +0000 (04:21 +0800)
fixes #5177

lib/compress.js
lib/output.js
lib/parse.js
test/compress/awaits.js
test/compress/properties.js
test/compress/yields.js

index a4232ed..d1f17ae 100644 (file)
@@ -9255,6 +9255,13 @@ merge(Compressor.prototype, {
         }) : arg);
     }
 
+    function avoid_await_yield(parent_scope) {
+        var avoid = [];
+        if (is_async(parent_scope)) avoid.push("await");
+        if (is_generator(parent_scope)) avoid.push("yield");
+        return avoid.length && makePredicate(avoid);
+    }
+
     OPT(AST_Call, function(self, compressor) {
         var exp = self.expression;
         var terminated = trim_optional_chain(self, compressor);
@@ -9770,16 +9777,8 @@ merge(Compressor.prototype, {
             return args;
         }
 
-        function avoid_await_yield() {
-            var avoid = [];
-            var parent_scope = scope || compressor.find_parent(AST_Scope);
-            if (is_async(parent_scope)) avoid.push("await");
-            if (is_generator(parent_scope)) avoid.push("yield");
-            return avoid.length && makePredicate(avoid);
-        }
-
         function safe_from_await_yield(node) {
-            var avoid = avoid_await_yield();
+            var avoid = avoid_await_yield(scope || compressor.find_parent(AST_Scope));
             if (!avoid) return true;
             var safe = true;
             var tw = new TreeWalker(function(node) {
@@ -9856,7 +9855,7 @@ merge(Compressor.prototype, {
                 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
             })) return;
             var abort = false;
-            var avoid = avoid_await_yield();
+            var avoid = avoid_await_yield(scope || compressor.find_parent(AST_Scope));
             var begin;
             var in_order = [];
             var side_effects = false;
@@ -12373,19 +12372,33 @@ merge(Compressor.prototype, {
             for (var i = props.length; --i >= 0;) {
                 var prop = props[i];
                 if (prop.key !== key) continue;
-                if (!all(props, can_hoist_property)) break;
-                if (!safe_to_flatten(prop.value, compressor)) break;
-                props = props.map(function(prop) {
-                    return prop.value;
-                });
-                if (prop instanceof AST_ObjectMethod
-                    && prop.value instanceof AST_Function
-                    && !(compressor.parent() instanceof AST_Call)) {
-                    if (prop.value.uses_arguments) break;
-                    props[i] = make_node(AST_Arrow, prop.value, prop.value);
+                if (!all(props, can_hoist_property)) return;
+                if (!safe_to_flatten(prop.value, compressor)) return;
+                var values = [];
+                for (var j = 0; j < props.length; j++) {
+                    var value = props[j].value;
+                    if (props[j] instanceof AST_ObjectMethod) {
+                        var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
+                        if (arrow) {
+                            var avoid = avoid_await_yield(compressor.find_parent(AST_Scope));
+                            value.each_argname(function(argname) {
+                                if (avoid[argname.name]) arrow = false;
+                            });
+                        }
+                        var ctor;
+                        if (arrow) {
+                            ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
+                        } else if (i === j && !(compressor.parent() instanceof AST_Call)) {
+                            return;
+                        } else {
+                            ctor = value.CTOR;
+                        }
+                        value = make_node(ctor, value, value);
+                    }
+                    values.push(value);
                 }
                 return make_node(AST_Sub, this, {
-                    expression: make_node(AST_Array, expr, { elements: props }),
+                    expression: make_node(AST_Array, expr, { elements: values }),
                     property: make_node(AST_Number, this, { value: i }),
                 });
             }
index c35c8ff..d7927db 100644 (file)
@@ -1152,8 +1152,9 @@ function OutputStream(options) {
         });
     }
     function print_arrow(self, output) {
-        if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg && !self.rest) {
-            self.argnames[0].print(output);
+        var argname = self.argnames.length == 1 && !self.rest && self.argnames[0];
+        if (argname instanceof AST_SymbolFunarg && argname.name != "yield") {
+            argname.print(output);
         } else {
             print_funargs(self, output);
         }
index 25487fe..1b23dc2 100644 (file)
@@ -1316,6 +1316,11 @@ function parse($TEXT, options) {
         }
         if (node instanceof AST_SymbolFunarg) return node;
         if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
+        if (node instanceof AST_Yield) return new AST_SymbolFunarg({
+            start: node.start,
+            name: "yield",
+            end: node.end,
+        });
         token_error(node.start, "Invalid arrow parameter");
     }
 
index 466abc3..7d79fe1 100644 (file)
@@ -453,7 +453,7 @@ object_function: {
         }).f();
     }
     expect: {
-        (async function() {
+        (async () => {
             console.log("PASS");
         })();
     }
@@ -2212,3 +2212,29 @@ issue_5159_2: {
     ]
     node_version: ">=8"
 }
+
+issue_5177: {
+    options = {
+        properties: true,
+    }
+    input: {
+        (async function() {
+            return {
+                p(await) {},
+            }.p;
+        })().then(function(a) {
+            console.log(typeof a);
+        });
+    }
+    expect: {
+        (async function() {
+            return {
+                p(await) {},
+            }.p;
+        })().then(function(a) {
+            console.log(typeof a);
+        });
+    }
+    expect_stdout: "function"
+    node_version: ">=8"
+}
index fb0db99..cbbe898 100644 (file)
@@ -1510,3 +1510,71 @@ issue_5093_quote_style: {
     expect_exact: 'console.log({a:true,\'42\':"PASS","null":[]}[6*7]);'
     expect_stdout: "PASS"
 }
+
+object_methods: {
+    options = {
+        properties: true,
+    }
+    input: {
+        ({
+            p() {
+                console.log("FAIL 1");
+            },
+            *q() {
+                console.log("FAIL 2");
+            },
+            async r() {
+                console.log("FAIL 3");
+            },
+            async *s() {
+                console.log("PASS");
+            },
+        }).s().next();
+    }
+    expect: {
+        [
+            () => {
+                console.log("FAIL 1");
+            },
+            function*() {
+                console.log("FAIL 2");
+            },
+            async () => {
+                console.log("FAIL 3");
+            },
+            async function*() {
+                console.log("PASS");
+            },
+        ][3]().next();
+    }
+    expect_stdout: "PASS"
+    node_version: ">=10"
+}
+
+issue_5177: {
+    options = {
+        properties: true,
+    }
+    input: {
+        var a = "FAIL";
+        var o = { a: "PASS" };
+        o.p = {
+            q() {
+                return this.a;
+            },
+        }.q;
+        console.log(o.p());
+    }
+    expect: {
+        var a = "FAIL";
+        var o = { a: "PASS" };
+        o.p = {
+            q() {
+                return this.a;
+            },
+        }.q;
+        console.log(o.p());
+    }
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}
index 37fd30e..d10321e 100644 (file)
@@ -96,7 +96,7 @@ pause_resume: {
     node_version: ">=4"
 }
 
-arrow_yield: {
+arrow_yield_1: {
     input: {
         yield = "PASS";
         console.log(function*() {
@@ -108,6 +108,18 @@ arrow_yield: {
     node_version: ">=4"
 }
 
+arrow_yield_2: {
+    input: {
+        console.log(typeof function *() {
+            // Syntax error on Node.js v6+
+            return (yield) => {};
+        }().next().value);
+    }
+    expect_exact: "console.log(typeof function*(){return(yield)=>{}}().next().value);"
+    expect_stdout: "function"
+    node_version: "4"
+}
+
 for_of: {
     input: {
         function* f() {
@@ -1275,3 +1287,25 @@ issue_5076: {
     expect_stdout: "PASS"
     node_version: ">=6"
 }
+
+issue_5177: {
+    options = {
+        properties: true,
+    }
+    input: {
+        console.log(typeof function*() {
+            return {
+                p(yield) {},
+            }.p;
+        }().next().value);
+    }
+    expect: {
+        console.log(typeof function*() {
+            return {
+                p(yield) {},
+            }.p;
+        }().next().value);
+    }
+    expect_stdout: "function"
+    node_version: ">=4"
+}