Files
rea/src/domain/javascript/electronStaticOptionObjects.ts
T
Ysopwnable d72d856f58 feat(electron): resolve stable local constant window options
Resolve stable local constant BrowserWindow options with retained provenance and shared early-exit mutation queries. Preserve source ordering and unknown results for relevant mutations or escapes.

Includes real Joplin correctness/cost receipts and regression coverage.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 14:52:33 +08:00

66 lines
2.3 KiB
TypeScript

import * as t from "@babel/types";
import { unwrapJavaScriptExpression } from "./javascriptAstValues.js";
import { semanticBindingAtRead } from "./javascriptSemanticMutationInitializers.js";
import {
semanticStableInitializer,
type JavaScriptBindingIndex,
} from "./javascriptSemanticState.js";
/** Lexical candidates alone do not establish that object contents are intact. */
const optionObjectCandidate = (
input: t.Node | undefined,
index: JavaScriptBindingIndex,
):
| {
readonly object: t.ObjectExpression;
readonly references: readonly t.Identifier[];
}
| undefined => {
if (input === undefined) return undefined;
let node = unwrapJavaScriptExpression(input).node;
const seen = new Set<t.Node>();
const references: t.Identifier[] = [];
while (t.isIdentifier(node)) {
if (seen.has(node)) return undefined;
seen.add(node);
const binding = index.bindingForReferenceAt(node);
if (binding === undefined) return undefined;
const initializer = semanticStableInitializer(binding, index.bindings);
if (
binding.mutable ||
binding.declarationKinds.size !== 1 ||
!binding.declarationKinds.has("const") ||
initializer === undefined ||
initializer.projection.length !== 0 ||
!semanticBindingAtRead(binding, node, index.bindings.parentsByNode)
.hasDefiniteInitializer
)
return undefined;
references.push(node);
node = unwrapJavaScriptExpression(initializer.node).node;
}
return t.isObjectExpression(node) ? { object: node, references } : undefined;
};
/** Avoid building mutation facts for parameters, missing or unresolved objects. */
export const hasElectronOptionObjectCandidate = (
input: t.Node | undefined,
index: JavaScriptBindingIndex,
): boolean => optionObjectCandidate(input, index) !== undefined;
/** Select an inert const object using the shared lexical and mutation owner. */
export const resolveElectronOptionObject = (
input: t.Node | undefined,
site: t.Node,
index: JavaScriptBindingIndex,
propertyPath: readonly string[] = [],
): t.ObjectExpression | undefined => {
const candidate = optionObjectCandidate(input, index);
if (candidate === undefined) return undefined;
for (const reference of candidate.references) {
if (index.referenceMayHaveChangedAt(site, reference, propertyPath))
return undefined;
}
return candidate.object;
};