From aebe0cb67f0a845ab96d5267efbb29b5fb27d4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 13 May 2025 14:06:44 +0200 Subject: [PATCH] Remove unused OpenJPEG wasm fallback logic Emscripten generates code that allows the caller to provide the Wasm module (thorugh Module.instantiateWasm), with a fallback in case .instantiateWasm is not provided. We always define instantiateWasm, so we can hard-code the check and let our dead code elimination logic remove the unused fallback. This commit also improved the dead code elimination logic so that if a function declaration becomes unused as a result of removing dead code, the function itself is removed. --- .../babel-plugin-pdfjs-preprocessor.mjs | 71 +++++++++++-------- .../builder/fixtures_babel/blocks-expected.js | 1 + external/builder/fixtures_babel/blocks.js | 1 + .../fixtures_babel/comments-expected.js | 3 + external/builder/fixtures_babel/comments.js | 3 + .../fixtures_babel/deadcode-expected.js | 4 ++ external/builder/fixtures_babel/deadcode.js | 4 ++ .../builder/fixtures_babel/ifs-expected.js | 1 + external/builder/fixtures_babel/ifs.js | 1 + .../builder/fixtures_babel/ignore-expected.js | 4 -- external/builder/fixtures_babel/ignore.js | 1 - .../unused-function-expected.js | 5 ++ .../builder/fixtures_babel/unused-function.js | 14 ++++ gulpfile.mjs | 27 +++++++ 14 files changed, 105 insertions(+), 35 deletions(-) delete mode 100644 external/builder/fixtures_babel/ignore-expected.js delete mode 100644 external/builder/fixtures_babel/ignore.js create mode 100644 external/builder/fixtures_babel/unused-function-expected.js create mode 100644 external/builder/fixtures_babel/unused-function.js diff --git a/external/builder/babel-plugin-pdfjs-preprocessor.mjs b/external/builder/babel-plugin-pdfjs-preprocessor.mjs index d90613e84..acb227e3e 100644 --- a/external/builder/babel-plugin-pdfjs-preprocessor.mjs +++ b/external/builder/babel-plugin-pdfjs-preprocessor.mjs @@ -47,6 +47,25 @@ function handlePreprocessorAction(ctx, actionName, args, path) { } function babelPluginPDFJSPreprocessor(babel, ctx) { + function removeUnusedFunctions(path) { + let removed; + do { + removed = false; + path.scope.crawl(); + for (const name in path.scope.bindings) { + const binding = path.scope.bindings[name]; + if (!binding.referenced) { + const { path: bindingPath } = binding; + if (bindingPath.isFunctionDeclaration()) { + bindingPath.remove(); + removed = true; + } + } + } + // If we removed some functions, there might be new unused ones + } while (removed); + } + return { name: "babel-plugin-pdfjs-preprocessor", manipulateOptions({ parserOpts }) { @@ -173,36 +192,6 @@ function babelPluginPDFJSPreprocessor(babel, ctx) { path.replaceWith(t.importExpression(source)); } }, - NewExpression(path) { - const { node } = path; - - if ( - t.isIdentifier(node.callee, { name: "URL" }) && - node.arguments.length === 2 - ) { - const [arg1, arg2] = node.arguments; - - if ( - arg1.type === "StringLiteral" && - arg1.value.endsWith(".wasm") && - arg2.type === "MemberExpression" - ) { - // This statement is generated by the Emscripten Compiler (emcc), - // however we're manually loading wasm-files and we want to ensure - // that bundlers will leave it alone; this *must* include Webpack. - arg1.leadingComments = [ - { - type: "CommentBlock", - value: "webpackIgnore: true", - }, - { - type: "CommentBlock", - value: "@vite-ignore", - }, - ]; - } - } - }, "BlockStatement|StaticBlock": { // Visit node in post-order so that recursive flattening // of blocks works correctly. @@ -255,6 +244,8 @@ function babelPluginPDFJSPreprocessor(babel, ctx) { // Function body ends with return without arg -- removing it. body.pop(); } + + removeUnusedFunctions(path); }, }, ClassMethod: { @@ -277,6 +268,26 @@ function babelPluginPDFJSPreprocessor(babel, ctx) { } }, }, + Program: { + exit(path) { + if (path.node.sourceType === "module") { + removeUnusedFunctions(path); + } + }, + }, + MemberExpression(path) { + // The Emscripten Compiler (emcc) generates code that allows the caller + // to provide the Wasm module (thorugh Module.instantiateWasm), with + // a fallback in case .instantiateWasm is not provided. + // We always define instantiateWasm, so we can hard-code the check + // and let our dead code elimination logic remove the unused fallback. + if ( + path.parentPath.isIfStatement({ test: path.node }) && + path.matchesPattern("Module.instantiateWasm") + ) { + path.replaceWith(t.booleanLiteral(true)); + } + }, }, }; } diff --git a/external/builder/fixtures_babel/blocks-expected.js b/external/builder/fixtures_babel/blocks-expected.js index 22e7b7434..6d2de6e03 100644 --- a/external/builder/fixtures_babel/blocks-expected.js +++ b/external/builder/fixtures_babel/blocks-expected.js @@ -8,3 +8,4 @@ function test() { } "4"; } +test(); diff --git a/external/builder/fixtures_babel/blocks.js b/external/builder/fixtures_babel/blocks.js index a7f3c360d..5267ee81d 100644 --- a/external/builder/fixtures_babel/blocks.js +++ b/external/builder/fixtures_babel/blocks.js @@ -17,3 +17,4 @@ function test() { "4"; } } +test(); diff --git a/external/builder/fixtures_babel/comments-expected.js b/external/builder/fixtures_babel/comments-expected.js index 24488a4b0..00f966a4f 100644 --- a/external/builder/fixtures_babel/comments-expected.js +++ b/external/builder/fixtures_babel/comments-expected.js @@ -2,10 +2,12 @@ function f1() { "1"; "2"; } +f1(); function f2() { "1"; "2"; } +f2(); function f3() { if ("1") { "1"; @@ -15,3 +17,4 @@ function f3() { "4"; } } +f3(); diff --git a/external/builder/fixtures_babel/comments.js b/external/builder/fixtures_babel/comments.js index 5c7758c6c..4da93e035 100644 --- a/external/builder/fixtures_babel/comments.js +++ b/external/builder/fixtures_babel/comments.js @@ -6,6 +6,7 @@ function f1() { "2"; /* tail */ } +f1(); function f2() { // head @@ -14,6 +15,7 @@ function f2() { "2"; // tail } +f2(); function f3() { if ("1") { // begin block @@ -24,3 +26,4 @@ function f3() { "4"; } } +f3(); diff --git a/external/builder/fixtures_babel/deadcode-expected.js b/external/builder/fixtures_babel/deadcode-expected.js index c948acaba..f947370c3 100644 --- a/external/builder/fixtures_babel/deadcode-expected.js +++ b/external/builder/fixtures_babel/deadcode-expected.js @@ -1,14 +1,18 @@ function f1() {} +f1(); function f2() { return 1; } +f2(); function f3() { var i = 0; throw "test"; } +f3(); function f4() { var i = 0; } +f4(); var obj = { method1() {}, method2() {} diff --git a/external/builder/fixtures_babel/deadcode.js b/external/builder/fixtures_babel/deadcode.js index c1d8c4fb8..4c2796a3c 100644 --- a/external/builder/fixtures_babel/deadcode.js +++ b/external/builder/fixtures_babel/deadcode.js @@ -2,17 +2,20 @@ function f1() { return; var i = 0; } +f1(); function f2() { return 1; var i = 0; } +f2(); function f3() { var i = 0; throw "test"; var j = 0; } +f3(); function f4() { var i = 0; @@ -22,6 +25,7 @@ function f4() { throw "test"; var j = 0; } +f4(); var obj = { method1() { return; var i = 0; }, diff --git a/external/builder/fixtures_babel/ifs-expected.js b/external/builder/fixtures_babel/ifs-expected.js index db9f4af15..424997844 100644 --- a/external/builder/fixtures_babel/ifs-expected.js +++ b/external/builder/fixtures_babel/ifs-expected.js @@ -16,3 +16,4 @@ if ('1') { function f1() { "1"; } +f1(); diff --git a/external/builder/fixtures_babel/ifs.js b/external/builder/fixtures_babel/ifs.js index 187c7f932..bdf085e69 100644 --- a/external/builder/fixtures_babel/ifs.js +++ b/external/builder/fixtures_babel/ifs.js @@ -32,3 +32,4 @@ function f1() { "2"; } } +f1(); diff --git a/external/builder/fixtures_babel/ignore-expected.js b/external/builder/fixtures_babel/ignore-expected.js deleted file mode 100644 index 0db4463ee..000000000 --- a/external/builder/fixtures_babel/ignore-expected.js +++ /dev/null @@ -1,4 +0,0 @@ -const wasmUrl = new URL( -/*webpackIgnore: true*/ -/*@vite-ignore*/ -"qwerty.wasm", import.meta.url); diff --git a/external/builder/fixtures_babel/ignore.js b/external/builder/fixtures_babel/ignore.js deleted file mode 100644 index 74371deac..000000000 --- a/external/builder/fixtures_babel/ignore.js +++ /dev/null @@ -1 +0,0 @@ -const wasmUrl = new URL("qwerty.wasm" , import.meta.url); diff --git a/external/builder/fixtures_babel/unused-function-expected.js b/external/builder/fixtures_babel/unused-function-expected.js new file mode 100644 index 000000000..bfadac746 --- /dev/null +++ b/external/builder/fixtures_babel/unused-function-expected.js @@ -0,0 +1,5 @@ +function usedByUsed() {} +function used() { + usedByUsed(); +} +used(); diff --git a/external/builder/fixtures_babel/unused-function.js b/external/builder/fixtures_babel/unused-function.js new file mode 100644 index 000000000..5e89d4210 --- /dev/null +++ b/external/builder/fixtures_babel/unused-function.js @@ -0,0 +1,14 @@ +function usedByUsed() {} +function usedByUnused() {} +function usedByRemovedCode() {} + +function used() { + usedByUsed(); + return; + usedByRemovedCode(); +} +function unused() { + usedByUnused(); +} + +used(); diff --git a/gulpfile.mjs b/gulpfile.mjs index 0c53f9031..3db31c758 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -339,6 +339,33 @@ function createWebpackConfig( new webpack2.BannerPlugin({ banner: licenseHeaderLibre, raw: true }) ); } + plugins.push({ + /** @param {import('webpack').Compiler} compiler */ + apply(compiler) { + const errors = []; + + compiler.hooks.afterCompile.tap("VerifyImportMeta", compilation => { + for (const asset of compilation.getAssets()) { + if (asset.name.endsWith(".mjs")) { + const source = asset.source.source(); + if ( + typeof source === "string" && + /new URL\([^,)]*,\s*import\.meta\.url/.test(source) + ) { + errors.push( + `Output module ${asset.name} uses new URL(..., import.meta.url)` + ); + } + } + } + }); + compiler.hooks.afterEmit.tap("VerifyImportMeta", compilation => { + // Emit the errors after emitting the files, so that it's possible to + // look at the contents of the invalid bundle. + compilation.errors.push(...errors); + }); + }, + }); const alias = createWebpackAlias(bundleDefines); const experiments = isModule ? { outputModule: true } : undefined;