mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(wasm): retain glue query and fragment across line terminators (#6802)
fix(wasm): retain multiline glue query and fragment literals Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
@@ -227,8 +227,23 @@ try {
|
||||
const candidate = join(candidateRoot, "module.wasm");
|
||||
await writeFile(join(candidateRoot, "source.wat"), "(module (memory 2))");
|
||||
await tool("wat2wasm", ["source.wat", "-o", "module.wasm"], candidateRoot);
|
||||
const glueSource =
|
||||
'fetch("./module.wasm"); new URL("https://example.invalid/module.wasm?version=1", import.meta.url);';
|
||||
const glueValues = [
|
||||
"./module.wasm",
|
||||
"https://example.invalid/module.wasm?version=1",
|
||||
...["\n", "\r", "\r\n", "\u2028", "\u2029"].flatMap((separator) => [
|
||||
`https://example.invalid/module.wasm?query=a${separator}b`,
|
||||
`https://example.invalid/module.wasm#fragment=a${separator}b`,
|
||||
]),
|
||||
];
|
||||
const glueLiterals = glueValues.map((value) =>
|
||||
JSON.stringify(value)
|
||||
.replace(/\u2028/gu, "\\u2028")
|
||||
.replace(/\u2029/gu, "\\u2029"),
|
||||
);
|
||||
const glueSource = [
|
||||
`fetch(${glueLiterals[0]}); new URL(${glueLiterals[1]}, import.meta.url);`,
|
||||
...glueLiterals.slice(2).map((literal) => `fetch(${literal});`),
|
||||
].join("\n");
|
||||
const glueSources = [
|
||||
glueSource,
|
||||
`\uFEFF${glueSource}`,
|
||||
@@ -283,21 +298,32 @@ try {
|
||||
assert.equal(observed.artifact.bytes, Buffer.byteLength(source));
|
||||
assert.equal(await readFile(gluePaths[index], "utf8"), source);
|
||||
assert.equal(observed.parse_status, "complete");
|
||||
assert.deepEqual(
|
||||
observed.references.map(({ value }) => value),
|
||||
glueValues,
|
||||
);
|
||||
assert.deepEqual(
|
||||
observed.references.map(({ candidate_paths }) => candidate_paths),
|
||||
[[path], [path, candidate]],
|
||||
glueValues.map((_, index) =>
|
||||
index === 0 ? [path] : [path, candidate],
|
||||
),
|
||||
);
|
||||
assert.deepEqual(
|
||||
observed.references.map(({ association }) => association),
|
||||
glueValues.map((_, index) =>
|
||||
index === 0 ? "local-path-candidate" : "basename-candidates",
|
||||
),
|
||||
);
|
||||
assert.deepEqual(
|
||||
observed.references.map(({ line, column }) => ({ line, column })),
|
||||
["./module.wasm", "https://example.invalid/module.wasm?version=1"].map(
|
||||
(value) => {
|
||||
const offset = source.indexOf(`"${value}"`);
|
||||
return {
|
||||
line: source.slice(0, offset).split("\n").length,
|
||||
column: offset - source.lastIndexOf("\n", offset) - 1,
|
||||
};
|
||||
},
|
||||
),
|
||||
glueLiterals.map((literal) => {
|
||||
const offset = source.indexOf(literal);
|
||||
assert.notEqual(offset, -1);
|
||||
return {
|
||||
line: source.slice(0, offset).split("\n").length,
|
||||
column: offset - source.lastIndexOf("\n", offset) - 1,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
assert.notEqual(report.candidates[0].sha256, report.candidates[1].sha256);
|
||||
|
||||
@@ -421,11 +421,58 @@ it("matches a CLI-selected ./ module exactly against its glue reference", () =>
|
||||
]);
|
||||
});
|
||||
|
||||
it.each(["\n", "\r", "\r\n", "\u2028", "\u2029"])(
|
||||
"retains complete .wasm query and fragment literals containing %j",
|
||||
(separator) => {
|
||||
const candidates = ["/artifacts/module.wasm", "/other/module.wasm"];
|
||||
for (const qualifier of ["?query=", "#fragment="]) {
|
||||
const value = `https://example.invalid/module.wasm${qualifier}a${separator}b`;
|
||||
const result = associateWasmGlue(
|
||||
`fetch(${JSON.stringify(value)});`,
|
||||
"/artifacts/glue.js",
|
||||
candidates,
|
||||
);
|
||||
expect(result).toEqual({
|
||||
parse_status: "complete",
|
||||
references: [
|
||||
{
|
||||
value,
|
||||
line: 1,
|
||||
column: 6,
|
||||
candidate_paths: candidates,
|
||||
association: "basename-candidates",
|
||||
evidence_kind: "static-literal",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["\n", "\r", "\r\n", "\u2028", "\u2029"])(
|
||||
"does not accept a path-ending separator without a query or fragment: %j",
|
||||
(separator) => {
|
||||
const value = `https://example.invalid/module.wasm${separator}`;
|
||||
expect(
|
||||
associateWasmGlue(`fetch(${JSON.stringify(value)});`, "/glue.js", []),
|
||||
).toEqual({ parse_status: "complete", references: [] });
|
||||
},
|
||||
);
|
||||
|
||||
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();
|
||||
const result = associateWasmGlue(
|
||||
`const s = ${JSON.stringify(value)};`,
|
||||
"/glue.js",
|
||||
[],
|
||||
);
|
||||
expect(result.parse_status).toBe("complete");
|
||||
expect(result.references).toHaveLength(1);
|
||||
expect(result.references[0]?.value).toBe(value);
|
||||
const negative = `${".wasm!".repeat(32_000)}\nend`;
|
||||
expect(
|
||||
associateWasmGlue(`const s = ${JSON.stringify(value)};`, "/glue.js", []),
|
||||
associateWasmGlue(`const s = ${JSON.stringify(negative)};`, "/glue.js", []),
|
||||
).toEqual({ parse_status: "complete", references: [] });
|
||||
expect(performance.now() - started).toBeLessThan(1_000);
|
||||
expect(
|
||||
|
||||
@@ -5,21 +5,12 @@ import { traverseJavaScriptAst } from "../domain/javascript/javascriptSemanticTr
|
||||
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.
|
||||
* Query/fragment content may contain line terminators. With dotAll, the first
|
||||
* `.wasm?` or `.wasm#` consumes the rest without retrying overlapping suffixes;
|
||||
* absent that qualifier, each candidate requires only a fixed suffix check.
|
||||
*/
|
||||
const endsWithWasmReference = (value: string): boolean =>
|
||||
/\.wasm(?:[?#].*)?$/iu.test(
|
||||
value.slice(
|
||||
Math.max(
|
||||
value.lastIndexOf("\n"),
|
||||
value.lastIndexOf("\r"),
|
||||
value.lastIndexOf("
"),
|
||||
value.lastIndexOf("
"),
|
||||
) + 1,
|
||||
),
|
||||
);
|
||||
/\.wasm(?:[?#].*)?$/isu.test(value);
|
||||
|
||||
/** Associate visible literals with selected candidates without asserting data flow or URL identity. */
|
||||
export const associateWasmGlue = (
|
||||
|
||||
Reference in New Issue
Block a user