From 88423f2574bfd4f158caab1c08a5a5bf1718cab4 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 31 Jul 2020 01:09:19 +0100 Subject: [PATCH] validate against multiple parents on `AST_Node` (#4032) - fix related issues in `global_defs`, `ie8` & `reduce_vars` --- lib/ast.js | 7 +++++++ lib/compress.js | 6 +++--- lib/minify.js | 6 +++++- test/compress.js | 14 ++++++++------ test/compress/global_defs.js | 14 ++++++++++++++ test/compress/ie8.js | 23 +++++++++++++++++++++++ test/ufuzz/index.js | 4 +++- 7 files changed, 63 insertions(+), 11 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index 31072a08..093e192c 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -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) { diff --git a/lib/compress.js b/lib/compress.js index 792d5ab2..fbc2bf7d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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()) { diff --git a/lib/minify.js b/lib/minify.js index 13d36de0..3817b634 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -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(); diff --git a/test/compress.js b/test/compress.js index 2fe02806..0afd98a9 100644 --- a/test/compress.js +++ b/test/compress.js @@ -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", diff --git a/test/compress/global_defs.js b/test/compress/global_defs.js index f1ef81d9..006be292 100644 --- a/test/compress/global_defs.js +++ b/test/compress/global_defs.js @@ -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: { diff --git a/test/compress/ie8.js b/test/compress/ie8.js index 19437074..6c0aa9e8 100644 --- a/test/compress/ie8.js +++ b/test/compress/ie8.js @@ -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" +} diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index 8f853c9c..1123d57e 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -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) { -- 2.34.1