mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
fix(electron): honor ordered switch-case completion in call paths (#10057)
fix(electron): honor switch-case completion in wrapper paths
This commit is contained in:
1 parent
827b808248
commit
4073cfc932
3 files changed
+116
-10
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { Script } from "node:vm";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
|
||||
|
||||
@@ -16,6 +17,90 @@ it("retains switch selection on a direct bridge exposure", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it.each(
|
||||
["return", "throw new Error('stop')", "break", "continue"].flatMap(
|
||||
(completion) =>
|
||||
[false, true].flatMap((wrapped) =>
|
||||
[false, true].map((nested) => ({ completion, wrapped, nested })),
|
||||
),
|
||||
),
|
||||
)(
|
||||
"excludes switch-case calls after $completion (wrapped=$wrapped, nested=$nested)",
|
||||
({ completion, wrapped, nested }) => {
|
||||
const exposure = (channel: string) =>
|
||||
`e.contextBridge.exposeInMainWorld("${channel}", {send:()=>e.ipcRenderer.send("${channel}")});`;
|
||||
const body = `for (const item of [0]) { switch (mode) {
|
||||
case "off": ${nested ? "{" : ""} ${completion}; ${wrapped ? 'e.ipcRenderer.send("dead");' : exposure("dead")} ${nested ? "}" : ""}
|
||||
default: ${wrapped ? 'e.ipcRenderer.send("live");' : exposure("live")}
|
||||
} }`;
|
||||
const source = `const e=require("electron");
|
||||
${wrapped ? `e.contextBridge.exposeInMainWorld("api", {run(){${body}}});` : `function install(){${body}}`}
|
||||
`;
|
||||
expect(() => new Script(source)).not.toThrow();
|
||||
const result = analyzeJavaScriptStaticSource(source);
|
||||
expect(
|
||||
result.electron.call_paths?.some(({ channel }) => channel === "dead"),
|
||||
).toBe(false);
|
||||
expect(
|
||||
result.electron.call_paths?.some(({ channel }) => channel === "live"),
|
||||
).toBe(true);
|
||||
// Unreachable syntax remains observed; only a derived invocation path is removed.
|
||||
expect(result.electron.ipc.some(({ channel }) => channel === "dead")).toBe(
|
||||
true,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([false, true])(
|
||||
"retains an earlier case-local return guard (wrapped=%s)",
|
||||
(wrapped) => {
|
||||
const target = wrapped
|
||||
? 'e.ipcRenderer.send("guarded");'
|
||||
: 'e.contextBridge.exposeInMainWorld("api", {send:()=>e.ipcRenderer.send("guarded")});';
|
||||
const body = `switch(mode){case "selected": if(blocked)return; ${target}}`;
|
||||
const source = `const e=require("electron");
|
||||
${wrapped ? `e.contextBridge.exposeInMainWorld("api",{run(){${body}}});` : `function install(){${body}}`}
|
||||
`;
|
||||
expect(() => new Script(source)).not.toThrow();
|
||||
const result = analyzeJavaScriptStaticSource(source);
|
||||
const paths =
|
||||
result.electron.call_paths?.filter(
|
||||
({ channel }) => channel === "guarded",
|
||||
) ?? [];
|
||||
expect(paths.length).toBeGreaterThan(0);
|
||||
expect(
|
||||
paths.every(({ conditions }) =>
|
||||
conditions.some(({ expression }) => expression === "!(blocked)"),
|
||||
),
|
||||
).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(
|
||||
[
|
||||
"while (true) { break; }",
|
||||
"switch (inner) { case 0: break; }",
|
||||
"for (const nested of [0]) { continue; }",
|
||||
"local: { break local; }",
|
||||
].flatMap((inner) => [false, true].map((wrapped) => ({ inner, wrapped }))),
|
||||
)(
|
||||
"retains outer-case continuation after $inner (wrapped=$wrapped)",
|
||||
({ inner, wrapped }) => {
|
||||
const target = wrapped
|
||||
? 'e.ipcRenderer.send("live");'
|
||||
: 'e.contextBridge.exposeInMainWorld("api", {send:()=>e.ipcRenderer.send("live")});';
|
||||
const body = `switch (mode) { case "selected": ${inner} ${target} }`;
|
||||
const source = `const e=require("electron");
|
||||
${wrapped ? `e.contextBridge.exposeInMainWorld("api", {run(){${body}}});` : `function install(){${body}}`}
|
||||
`;
|
||||
expect(() => new Script(source)).not.toThrow();
|
||||
const result = analyzeJavaScriptStaticSource(source);
|
||||
expect(
|
||||
result.electron.call_paths?.some(({ channel }) => channel === "live"),
|
||||
).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it("retains a conservative selection for stacked and fallthrough cases", () => {
|
||||
const result = analyzeJavaScriptStaticSource(`
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
Reference in new issue
Block a user