validate against multiple parents on `AST_Node` (#4032)
authorAlex Lam S.L <alexlamsl@gmail.com>
Fri, 31 Jul 2020 00:09:19 +0000 (01:09 +0100)
committerGitHub <noreply@github.com>
Fri, 31 Jul 2020 00:09:19 +0000 (08:09 +0800)
- fix related issues in `global_defs`, `ie8` & `reduce_vars`

lib/ast.js
lib/compress.js
lib/minify.js
test/compress.js
test/compress/global_defs.js
test/compress/ie8.js
test/ufuzz/index.js

index 31072a0..093e192 100644 (file)
@@ -120,6 +120,13 @@ var AST_Node = DEFNODE("Node", "start end", {
             ctor.prototype._validate.call(this);
         } while (ctor = ctor.BASE);
     },
+    validate_ast: function() {
+        var marker = {};
+        this.walk(new TreeWalker(function(node) {
+            if (node.validate_visited === marker) throw new Error("invalid node reuse: " + node);
+            node.validate_visited = marker;
+        }));
+    },
 }, null);
 
 (AST_Node.log_function = function(fn, verbose) {
index 792d5ab..fbc2bf7 100644 (file)
@@ -3072,7 +3072,7 @@ merge(Compressor.prototype, {
 
     (function(def) {
         function to_node(value, orig) {
-            if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
+            if (value instanceof AST_Node) return value.clone(true);
             if (Array.isArray(value)) return make_node(AST_Array, orig, {
                 elements: value.map(function(value) {
                     return to_node(value, orig);
@@ -7642,6 +7642,8 @@ merge(Compressor.prototype, {
                         single_use = false;
                     } else if (recursive_ref(compressor, def)) {
                         single_use = false;
+                    } else if (compressor.option("ie8") && fixed.name && def !== fixed.name.definition()) {
+                        single_use = false;
                     } else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
                         single_use = fixed.is_constant_expression(self.scope);
                         if (single_use == "f") {
@@ -7650,8 +7652,6 @@ merge(Compressor.prototype, {
                                 scope.inlined = true;
                             } while (scope = scope.parent_scope);
                         }
-                    } else if (compressor.option("ie8") && fixed.name && def !== fixed.name.definition()) {
-                        single_use = false;
                     }
                     if (single_use) fixed.parent_scope = self.scope;
                 } else if (!fixed || !fixed.is_constant_expression()) {
index 13d36de..3817b63 100644 (file)
@@ -178,13 +178,17 @@ function minify(files, options) {
             toplevel = toplevel[action](option);
             files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
         });
+        if (options.validate) toplevel.validate_ast();
         if (timings) timings.rename = Date.now();
         if (options.rename) {
             toplevel.figure_out_scope(options.mangle);
             toplevel.expand_names(options.mangle);
         }
         if (timings) timings.compress = Date.now();
-        if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
+        if (options.compress) {
+            toplevel = new Compressor(options.compress).compress(toplevel);
+            if (options.validate) toplevel.validate_ast();
+        }
         if (timings) timings.scope = Date.now();
         if (options.mangle) toplevel.figure_out_scope(options.mangle);
         if (timings) timings.mangle = Date.now();
index 2fe0280..0afd98a 100644 (file)
@@ -269,6 +269,7 @@ function test_case(test) {
         quote_style: 3,
     });
     try {
+        input.validate_ast();
         U.parse(input_code);
     } catch (ex) {
         log([
@@ -315,8 +316,8 @@ function test_case(test) {
             output = U.mangle_properties(output, test.mangle.properties);
         }
     }
-    output = make_code(output, output_options);
-    if (expect != output) {
+    var output_code = make_code(output, output_options);
+    if (expect != output_code) {
         log([
             "!!! failed",
             "---INPUT---",
@@ -329,14 +330,15 @@ function test_case(test) {
             "",
         ].join("\n"), {
             input: input_formatted,
-            output: output,
+            output: output_code,
             expected: expect
         });
         return false;
     }
     // expect == output
     try {
-        U.parse(output);
+        output.validate_ast();
+        U.parse(output_code);
     } catch (ex) {
         log([
             "!!! Test matched expected result but cannot parse output",
@@ -350,7 +352,7 @@ function test_case(test) {
             "",
         ].join("\n"), {
             input: input_formatted,
-            output: output,
+            output: output_code,
             error: ex,
         });
         return false;
@@ -409,7 +411,7 @@ function test_case(test) {
             });
             return false;
         }
-        actual = run_code(output, toplevel);
+        actual = run_code(output_code, toplevel);
         if (!sandbox.same_stdout(test.expect_stdout, actual)) {
             log([
                 "!!! failed",
index f1ef81d..006be29 100644 (file)
@@ -12,6 +12,20 @@ must_replace: {
     }
 }
 
+repeated_nodes: {
+    options = {
+        global_defs: {
+            "@N": "rand()",
+        },
+    }
+    input: {
+        console.log(N, N);
+    }
+    expect: {
+        console.log(rand(), rand());
+    }
+}
+
 keyword: {
     options = {
         global_defs: {
index 1943707..6c0aa9e 100644 (file)
@@ -2691,3 +2691,26 @@ issue_4028: {
     }
     expect_stdout: "string"
 }
+
+issue_2737: {
+    options = {
+        ie8: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        (function(a) {
+            a();
+        })(function f() {
+            console.log(typeof f);
+        });
+    }
+    expect: {
+        (function(a) {
+            a();
+        })(function f() {
+            console.log(typeof f);
+        });
+    }
+    expect_stdout: "function"
+}
index 8f853c9..1123d57 100644 (file)
@@ -1148,7 +1148,9 @@ function log(options) {
         }
     }
     errorln("//-------------------------------------------------------------");
-    var reduced = reduce_test(original_code, JSON.parse(options), {
+    var reduce_options = JSON.parse(options);
+    reduce_options.validate = true;
+    var reduced = reduce_test(original_code, reduce_options, {
         verbose: false,
     }).code;
     if (reduced) {