From b1502949816e4a343b90ca8a6e0411160dbc33ce Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 5 Oct 2023 16:29:07 -0700 Subject: [PATCH] JSDCE: Add support for destructuring declarations (#20402) Rather than let x = 10, these look like let {x: y, ..} = ... or let [x, y] = ... Fixes https://github.com/emscripten-core/emscripten/issues/20393 --- test/optimizer/JSDCE-objectPattern-output.js | 16 ++++++++++ test/optimizer/JSDCE-objectPattern.js | 28 +++++++++++++++++ test/test_other.py | 1 + tools/acorn-optimizer.js | 32 ++++++++++++++++++-- 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 test/optimizer/JSDCE-objectPattern-output.js create mode 100644 test/optimizer/JSDCE-objectPattern.js diff --git a/test/optimizer/JSDCE-objectPattern-output.js b/test/optimizer/JSDCE-objectPattern-output.js new file mode 100644 index 000000000..fb6ecf8ad --- /dev/null +++ b/test/optimizer/JSDCE-objectPattern-output.js @@ -0,0 +1,16 @@ +let d = 40; + +let z = 50; + +globalThis.f = function(r) { + let {a: a, b: b} = r; + let {z: c} = r; + return g(a, b, c, d, z); +}; + +let d2 = 40; + +globalThis.f2 = function(r2) { + let [a2, b2, c2] = r2; + return g2(a2, b2, c2, d2); +}; diff --git a/test/optimizer/JSDCE-objectPattern.js b/test/optimizer/JSDCE-objectPattern.js new file mode 100644 index 000000000..61ff37638 --- /dev/null +++ b/test/optimizer/JSDCE-objectPattern.js @@ -0,0 +1,28 @@ +// Of all these, only d must remain: the others are defined in the function, and +// used from that scope, not the global scope. +let a = 10; +let b = 20; +let c = 30; +let d = 40; + +// This one must also remain: z in the function is not a local variable, but the +// id in an object, so it does not affect scoping at all, and we use the global +// z. +let z = 50; + +globalThis.f = function(r) { + let { a, b } = r; + let { z: c } = r; + return g(a, b, c, d, z); +}; + +// As above, but now with array destructuring. +let a2 = 10; +let b2 = 20; +let c2 = 30; +let d2 = 40; + +globalThis.f2 = function(r2) { + let [ a2, b2, c2 ] = r2; + return g2(a2, b2, c2, d2); +}; diff --git a/test/test_other.py b/test/test_other.py index cc775be0f..fe75f7683 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -2492,6 +2492,7 @@ int f() { 'JSDCE': ('optimizer/JSDCE.js', ['JSDCE']), 'JSDCE-hasOwnProperty': ('optimizer/JSDCE-hasOwnProperty.js', ['JSDCE']), 'JSDCE-fors': ('optimizer/JSDCE-fors.js', ['JSDCE']), + 'JSDCE-objectPattern': ('optimizer/JSDCE-objectPattern.js', ['JSDCE']), 'AJSDCE': ('optimizer/AJSDCE.js', ['AJSDCE']), 'emitDCEGraph': ('optimizer/emitDCEGraph.js', ['emitDCEGraph', 'noPrint']), 'emitDCEGraph-closure': ('optimizer/emitDCEGraph.js', ['emitDCEGraph', 'noPrint', '--closureFriendly']), diff --git a/tools/acorn-optimizer.js b/tools/acorn-optimizer.js index 5274f3114..281b21b25 100755 --- a/tools/acorn-optimizer.js +++ b/tools/acorn-optimizer.js @@ -319,7 +319,16 @@ function runJSDCE(ast, aggressive) { const old = node.declarations; let removedHere = 0; node.declarations = node.declarations.filter((node) => { - const curr = node.id.name; + assert(node.type === 'VariableDeclarator'); + const id = node.id; + if (id.type === 'ObjectPattern' || id.type === 'ArrayPattern') { + // TODO: DCE into object patterns, that is, things like + // let { a, b } = .. + // let [ a, b ] = .. + return true; + } + assert(id.type === 'Identifier'); + const curr = id.name; const value = node.init; const keep = !(curr in names) || (value && hasSideEffects(value)); if (!keep) removedHere = 1; @@ -390,8 +399,25 @@ function runJSDCE(ast, aggressive) { recursiveWalk(ast, { VariableDeclarator(node, c) { - const name = node.id.name; - ensureData(scopes[scopes.length - 1], name).def = 1; + const id = node.id; + if (id.type === 'ObjectPattern') { + id.properties.forEach((node) => { + const value = node.value; + assert(value.type === 'Identifier'); + const name = value.name; + ensureData(scopes[scopes.length - 1], name).def = 1; + }); + } else if (id.type === 'ArrayPattern') { + id.elements.forEach((node) => { + assert(node.type === 'Identifier'); + const name = node.name; + ensureData(scopes[scopes.length - 1], name).def = 1; + }); + } else { + assert(id.type === 'Identifier'); + const name = id.name; + ensureData(scopes[scopes.length - 1], name).def = 1; + } if (node.init) c(node.init); }, ObjectExpression(node, c) { -- 2.25.1