From 6b55802df66cf25d61e652f0549bd83ef260ebfd Mon Sep 17 00:00:00 2001 From: Dragolone <160818051+Dragolone@users.noreply.github.com> Date: Fri, 23 Oct 2026 05:21:12 +1100 Subject: [PATCH] fix(wasm): test glue literals for a .wasm suffix in linear time (#6237) associateWasmGlue tested every glue string literal with /\.wasm(?:[?#].*)?$/iu. Each .wasm? or .wasm# occurrence scanned to the next line terminator and failed at $, so a multi-line literal with many such occurrences took quadratic time on the event loop (about 2.6 s for 192 KB, within the 8 MiB glue limit). Because . cannot cross a line terminator and $ only matches at the end, test the literal's last line only; results are unchanged. --- src/wasm/WabtOutput.test.ts | 13 +++++++++++++ src/wasm/WasmGlueAdapter.ts | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/wasm/WabtOutput.test.ts b/src/wasm/WabtOutput.test.ts index f1aabe95a9..9fc83b987e 100644 --- a/src/wasm/WabtOutput.test.ts +++ b/src/wasm/WabtOutput.test.ts @@ -381,3 +381,16 @@ it("matches a CLI-selected ./ module exactly against its glue reference", () => }, ]); }); + +it("tests long multi-line glue literals for a .wasm suffix in linear time", () => { + const value = `${".wasm#".repeat(32_000)}\nend`; + const started = performance.now(); + expect( + associateWasmGlue(`const s = ${JSON.stringify(value)};`, "/glue.js", []), + ).toEqual({ parse_status: "complete", references: [] }); + expect(performance.now() - started).toBeLessThan(1_000); + expect( + associateWasmGlue("const s = `first\nmodule.wasm?v=1`;", "/glue.js", []) + .references, + ).toMatchObject([{ value: "first\nmodule.wasm?v=1", line: 1 }]); +}); diff --git a/src/wasm/WasmGlueAdapter.ts b/src/wasm/WasmGlueAdapter.ts index 4a130508ed..e860efff84 100644 --- a/src/wasm/WasmGlueAdapter.ts +++ b/src/wasm/WasmGlueAdapter.ts @@ -3,6 +3,24 @@ import * as t from "@babel/types"; import { parseJavaScriptSource } from "../domain/javascript/javascriptSourceParser.js"; import { traverseJavaScriptAst } from "../domain/javascript/javascriptSemanticTraversal.js"; import { absoluteCallerPath } from "../filesystem/callerPath.js"; + +/** + * `.` cannot cross a line terminator and `$` only matches at the end of the + * literal, so a `.wasm` suffix can only occur on its last line. Testing that + * line alone keeps long multi-line literals linear. + */ +const endsWithWasmReference = (value: string): boolean => + /\.wasm(?:[?#].*)?$/iu.test( + value.slice( + Math.max( + value.lastIndexOf("\n"), + value.lastIndexOf("\r"), + value.lastIndexOf("
"), + value.lastIndexOf("
"), + ) + 1, + ), + ); + /** Associate visible literals with selected candidates without asserting data flow or URL identity. */ export const associateWasmGlue = ( source: string, @@ -43,7 +61,7 @@ export const associateWasmGlue = ( if ( value === undefined || value === null || - !/\.wasm(?:[?#].*)?$/iu.test(value) || + !endsWithWasmReference(value) || node.loc == null ) return;