validate against multiple parents on AST_Node (#4032)

- fix related issues in `global_defs`, `ie8` & `reduce_vars`
This commit is contained in:
Alex Lam S.L
2020-07-31 01:09:19 +01:00
committed by GitHub
parent ee632a5519
commit 88423f2574
7 changed files with 63 additions and 11 deletions

View File

@@ -120,6 +120,13 @@ var AST_Node = DEFNODE("Node", "start end", {
ctor.prototype._validate.call(this); ctor.prototype._validate.call(this);
} while (ctor = ctor.BASE); } 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); }, null);
(AST_Node.log_function = function(fn, verbose) { (AST_Node.log_function = function(fn, verbose) {

View File

@@ -3072,7 +3072,7 @@ merge(Compressor.prototype, {
(function(def) { (function(def) {
function to_node(value, orig) { 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, { if (Array.isArray(value)) return make_node(AST_Array, orig, {
elements: value.map(function(value) { elements: value.map(function(value) {
return to_node(value, orig); return to_node(value, orig);
@@ -7642,6 +7642,8 @@ merge(Compressor.prototype, {
single_use = false; single_use = false;
} else if (recursive_ref(compressor, def)) { } else if (recursive_ref(compressor, def)) {
single_use = false; 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) { } else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
single_use = fixed.is_constant_expression(self.scope); single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") { if (single_use == "f") {
@@ -7650,8 +7652,6 @@ merge(Compressor.prototype, {
scope.inlined = true; scope.inlined = true;
} while (scope = scope.parent_scope); } 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; if (single_use) fixed.parent_scope = self.scope;
} else if (!fixed || !fixed.is_constant_expression()) { } else if (!fixed || !fixed.is_constant_expression()) {

View File

@@ -178,13 +178,17 @@ function minify(files, options) {
toplevel = toplevel[action](option); toplevel = toplevel[action](option);
files[toplevel.start.file] = toplevel.print_to_string().replace(orig, ""); files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
}); });
if (options.validate) toplevel.validate_ast();
if (timings) timings.rename = Date.now(); if (timings) timings.rename = Date.now();
if (options.rename) { if (options.rename) {
toplevel.figure_out_scope(options.mangle); toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle); toplevel.expand_names(options.mangle);
} }
if (timings) timings.compress = Date.now(); 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 (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle); if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now(); if (timings) timings.mangle = Date.now();

View File

@@ -269,6 +269,7 @@ function test_case(test) {
quote_style: 3, quote_style: 3,
}); });
try { try {
input.validate_ast();
U.parse(input_code); U.parse(input_code);
} catch (ex) { } catch (ex) {
log([ log([
@@ -315,8 +316,8 @@ function test_case(test) {
output = U.mangle_properties(output, test.mangle.properties); output = U.mangle_properties(output, test.mangle.properties);
} }
} }
output = make_code(output, output_options); var output_code = make_code(output, output_options);
if (expect != output) { if (expect != output_code) {
log([ log([
"!!! failed", "!!! failed",
"---INPUT---", "---INPUT---",
@@ -329,14 +330,15 @@ function test_case(test) {
"", "",
].join("\n"), { ].join("\n"), {
input: input_formatted, input: input_formatted,
output: output, output: output_code,
expected: expect expected: expect
}); });
return false; return false;
} }
// expect == output // expect == output
try { try {
U.parse(output); output.validate_ast();
U.parse(output_code);
} catch (ex) { } catch (ex) {
log([ log([
"!!! Test matched expected result but cannot parse output", "!!! Test matched expected result but cannot parse output",
@@ -350,7 +352,7 @@ function test_case(test) {
"", "",
].join("\n"), { ].join("\n"), {
input: input_formatted, input: input_formatted,
output: output, output: output_code,
error: ex, error: ex,
}); });
return false; return false;
@@ -409,7 +411,7 @@ function test_case(test) {
}); });
return false; return false;
} }
actual = run_code(output, toplevel); actual = run_code(output_code, toplevel);
if (!sandbox.same_stdout(test.expect_stdout, actual)) { if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([ log([
"!!! failed", "!!! failed",

View 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: { keyword: {
options = { options = {
global_defs: { global_defs: {

View File

@@ -2691,3 +2691,26 @@ issue_4028: {
} }
expect_stdout: "string" 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"
}

View File

@@ -1148,7 +1148,9 @@ function log(options) {
} }
} }
errorln("//-------------------------------------------------------------"); 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, verbose: false,
}).code; }).code;
if (reduced) { if (reduced) {