diff --git a/lib/ast.js b/lib/ast.js index 76323482..70acb69d 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -142,6 +142,7 @@ DEF_BITPROPS(AST_Node, [ "_squeezed", // AST_Call "call_only", + // AST_Lambda "collapse_scanning", // AST_SymbolRef "defined", diff --git a/lib/compress.js b/lib/compress.js index 9e76120d..af462ba9 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -8715,6 +8715,7 @@ Compressor.prototype.compress = function(node) { }); function mark_locally_defined(condition, consequent, alternative) { + if (condition instanceof AST_Sequence) condition = condition.tail_node(); if (!(condition instanceof AST_Binary)) return; if (!(condition.left instanceof AST_String)) { switch (condition.operator) { @@ -8749,6 +8750,8 @@ Compressor.prototype.compress = function(node) { var abort = false; var def = sym.definition(); var fn; + var refs = []; + var scanned = []; var tw = new TreeWalker(function(node, descend) { if (abort) return true; if (node instanceof AST_Assign) { @@ -8765,11 +8768,40 @@ Compressor.prototype.compress = function(node) { if (node instanceof AST_Call) { descend(); fn = node.expression.tail_node(); - if (fn instanceof AST_Lambda) { - fn.walk(tw); - } else { - abort = true; + var save; + if (fn instanceof AST_SymbolRef) { + fn = fn.fixed_value(); + save = refs.length; } + if (!(fn instanceof AST_Lambda)) { + abort = true; + } else if (push_uniq(scanned, fn)) { + fn.walk(tw); + } + if (save >= 0) refs.length = save; + return true; + } + if (node instanceof AST_DWLoop) { + var save = refs.length; + descend(); + if (abort) refs.length = save; + return true; + } + if (node instanceof AST_For) { + if (node.init) node.init.walk(tw); + var save = refs.length; + if (node.condition) node.condition.walk(tw); + node.body.walk(tw); + if (node.step) node.step.walk(tw); + if (abort) refs.length = save; + return true; + } + if (node instanceof AST_ForEnumeration) { + node.object.walk(tw); + var save = refs.length; + node.init.walk(tw); + node.body.walk(tw); + if (abort) refs.length = save; return true; } if (node instanceof AST_Scope) { @@ -8777,11 +8809,14 @@ Compressor.prototype.compress = function(node) { return true; } if (node instanceof AST_SymbolRef) { - if (node.definition() === def) node.defined = true; + if (node.definition() === def) refs.push(node); return true; } }); body.walk(tw); + refs.forEach(function(ref) { + ref.defined = true; + }); function negate(node) { if (!(node instanceof AST_Binary)) return; diff --git a/test/compress/typeof.js b/test/compress/typeof.js index 6b4b8d25..67226565 100644 --- a/test/compress/typeof.js +++ b/test/compress/typeof.js @@ -527,6 +527,102 @@ reassign_conditional: { node_version: ">=15" } +reassign_do: { + options = { + comparisons: true, + conditionals: true, + if_return: true, + passes: 2, + reduce_vars: true, + typeofs: true, + } + input: { + A = console; + (function() { + if ("undefined" == typeof A) + return; + var a = A, i = 2; + do { + console.log(void 0 === A, void 0 === a); + A = void 0; + } while (--i); + })(); + } + expect: { + A = console; + (function() { + if ("undefined" != typeof A) { + var a = A, i = 2; + do { + console.log(void 0 === A, (a, false)); + A = void 0; + } while (--i); + } + })(); + } + expect_stdout: [ + "false false", + "true false", + ] +} + +reassign_for: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + reduce_vars: true, + toplevel: true, + typeofs: true, + } + input: { + if (A = console, "undefined" != typeof A) + for (var a = A, i = 0; i < 2; i++) + console.log(void 0 === A, void 0 === a), + A = void 0; + } + expect: { + if (A = console, "undefined" != typeof A) + for (var a = A, i = 0; i < 2; i++) + console.log(void 0 === A, (a, false)), + A = void 0; + } + expect_stdout: [ + "false false", + "true false", + ] +} + +reassign_for_in: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + reduce_vars: true, + typeofs: true, + } + input: { + (A = console) && "undefined" != typeof A && function(a) { + for (var k in [ a = A, 42 ]) { + console.log(void 0 === A, void 0 === a); + A = void 0; + } + }(); + } + expect: { + (A = console) && "undefined" != typeof A && function(a) { + for (var k in [ a = A, 42 ]) { + console.log(void 0 === A, (a, false)); + A = void 0; + } + }(); + } + expect_stdout: [ + "false false", + "true false", + ] +} + reassign_iife: { options = { comparisons: true, @@ -564,7 +660,7 @@ reassign_property: { console.log("FAIL 1"); else { A.p = void 0; - while (console.log(void 0 === A ? "FAIL 2" : "PASS")); + console.log(void 0 === A ? "FAIL 2" : "PASS"); } } expect: { @@ -573,7 +669,7 @@ reassign_property: { console.log("FAIL 1"); else { A.p = void 0; - while (console.log((A, false) ? "FAIL 2" : "PASS")); + console.log((A, false) ? "FAIL 2" : "PASS"); } } expect_stdout: "PASS"