mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
fix(javascript): preserve loader authority for exact suffixes (#9477)
* fix(javascript): require exact loader suffix case * fix(javascript): share exact loader mode across import resolution * test(process): align symlink fixture with filesystem checkpoints * test(javascript): normalize the module resolution oracle path separators * test(frida): assert the CLI spawn Evidence contract * test(mcp): use an unsupported analysis-view parent * test(browser): separate source-location line bounds from parity * fix(javascript): preserve uncertain loader provenance * test(javascript): cover uncertain require resolution * fix(javascript): retain conditional Electron evidence * fix(javascript): preserve timed semantic provenance * fix(javascript): retain conditional builtin effects * test(javascript): distinguish bundled host authority * fix(javascript): preserve nested loader invocation identity * test(javascript): qualify Electron fixture authority * fix(javascript): preserve loader invocation authority * fix(javascript): respect source order for resolver escapes * fix(javascript): check nested loader callable mutations * fix(javascript): retain conditional constructor requests * fix(reference): retain constructor loader relationships * fix(reference): follow nested loader callable paths * refactor(javascript): share callable mutation interpretation * fix(javascript): require exact package loader suffixes * refactor(javascript): unify conditional relation evidence * refactor(javascript): unify conditional node evidence * fix(javascript): retain unknown authority for mixed case sources * fix(javascript): preserve loader uncertainty for dotfiles * fix(reference): classify javascript suffix dotfiles * test(reference): cover suffix dotfiles through cli * fix(javascript): retain equivalent conditional built-in origins * fix(javascript): preserve wrapped resolver operation * fix(javascript): retain nested conditional built-in receivers * test(javascript): declare CommonJS in native addon fixtures * fix(javascript): retain wrapped resolver candidates * style: format merged native command schema * fix(javascript): preserve uncertain loader authority * test(javascript): require candidate preload evidence * test(javascript): cover reference loader member writes --------- Co-authored-by: ShuaixinHuang <1836656514@qq.com>
This commit is contained in:
1 parent
78a84d75c0
commit
b46f4fcebd
112 files changed
+5156
-797
No files matched your search
@@ -67,12 +67,17 @@ import {
|
||||
} from "./javascriptSemanticTraversal.js";
|
||||
import {
|
||||
semanticRequireOrigin,
|
||||
semanticRequireOriginCandidate,
|
||||
observePossibleCommonJsDelegateWrite,
|
||||
invalidateSemanticRequireDelegateWrites,
|
||||
} from "./javascriptSemanticRequireOrigin.js";
|
||||
import { semanticBundledRequireOrigin } from "./javascriptBundledRequireOrigin.js";
|
||||
import {
|
||||
semanticBundledRequireOrigin,
|
||||
semanticBundledRequireOriginCandidate,
|
||||
} from "./javascriptBundledRequireOrigin.js";
|
||||
import { collectSemanticMemberMutationsSteps } from "./javascriptSemanticMemberMutations.js";
|
||||
import {
|
||||
semanticBindingAtRead,
|
||||
semanticEffectTiming,
|
||||
semanticEffectTimingWithinActivation,
|
||||
} from "./javascriptSemanticMutationInitializers.js";
|
||||
@@ -844,12 +849,16 @@ const resolveGlobalAliasFact = (
|
||||
export interface JavaScriptElectronIdentity {
|
||||
/** Proven export paths keyed by call/construction root offset; absence is unknown. */
|
||||
readonly bindings: ReadonlyMap<number, readonly string[]>;
|
||||
/** Conditional export paths whose require loader is not established. */
|
||||
readonly candidateBindings: ReadonlyMap<number, readonly string[]>;
|
||||
readonly intactExport: (path: readonly (string | null)[]) => boolean;
|
||||
readonly exportPath: (root: t.Node) => readonly string[] | undefined;
|
||||
readonly candidateExportPath: (root: t.Node) => readonly string[] | undefined;
|
||||
readonly invocationPath: (
|
||||
node: t.CallExpression | t.OptionalCallExpression,
|
||||
) => readonly string[] | undefined;
|
||||
readonly mayChangeAppPath: (node: t.Node) => boolean;
|
||||
readonly candidateMayChangeAppPath: (node: t.Node) => boolean;
|
||||
}
|
||||
|
||||
export function* indexJavaScriptElectronIdentitySteps(
|
||||
@@ -857,15 +866,33 @@ export function* indexJavaScriptElectronIdentitySteps(
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): Generator<void, JavaScriptElectronIdentity> {
|
||||
const facts = new Map<number, readonly string[]>();
|
||||
const candidateFacts = new Map<number, readonly string[]>();
|
||||
let hasCandidateElectronSource = false;
|
||||
const interop = createSemanticModuleInterop(state);
|
||||
const writes = new SemanticPropertyPathCoverage();
|
||||
let collectedWrites = false;
|
||||
const exportsByBinding = new Map<string, readonly string[] | null>();
|
||||
const candidatesByBinding = new Map<string, readonly string[] | null>();
|
||||
const originAvailableAtRead = (
|
||||
binding: JavaScriptSemanticBindingState,
|
||||
read: t.Node,
|
||||
): boolean => {
|
||||
if (
|
||||
binding.directOrigins.length > 0 ||
|
||||
semanticStableInitializer(binding, state) === undefined
|
||||
)
|
||||
return true;
|
||||
const access = semanticBindingAtRead(binding, read, state);
|
||||
// An unresolved activation can still carry a possible identity, while an
|
||||
// ordered read before its initializer cannot refer to that value.
|
||||
return access.status !== "ordered" || access.hasDefiniteInitializer;
|
||||
};
|
||||
const rootOrigin = (root: t.Node): readonly string[] | undefined => {
|
||||
if (t.isIdentifier(root)) {
|
||||
if (semanticResolutionBlocked(state, root, root.name)) return undefined;
|
||||
const binding = resolveSemanticBindingState(state, root, root.name);
|
||||
if (binding === undefined) return undefined;
|
||||
if (binding === undefined || !originAvailableAtRead(binding, root))
|
||||
return undefined;
|
||||
let exported = exportsByBinding.get(binding.bindingId);
|
||||
if (exported === undefined) {
|
||||
exported = electronExport(binding, state, interop);
|
||||
@@ -900,13 +927,79 @@ export function* indexJavaScriptElectronIdentitySteps(
|
||||
}
|
||||
return rootOrigin(root);
|
||||
};
|
||||
const candidateRootExport = (root: t.Node): readonly string[] | undefined => {
|
||||
if (t.isIdentifier(root)) {
|
||||
if (semanticResolutionBlocked(state, root, root.name)) return undefined;
|
||||
const binding = resolveSemanticBindingState(state, root, root.name);
|
||||
if (
|
||||
binding === undefined ||
|
||||
(collectedWrites && interop.invalidated(binding))
|
||||
)
|
||||
return undefined;
|
||||
if (binding.directOrigins.length === 0) {
|
||||
if (semanticStableInitializer(binding, state) === undefined)
|
||||
return undefined;
|
||||
if (!originAvailableAtRead(binding, root)) return undefined;
|
||||
}
|
||||
if (
|
||||
state.opaqueEffects.byScope.size > 0 &&
|
||||
(semanticOpaqueBindingReadMayHaveChanged(binding, root, state) ||
|
||||
interop.aliasMayHaveChanged(binding))
|
||||
)
|
||||
return undefined;
|
||||
let candidate = candidatesByBinding.get(binding.bindingId);
|
||||
if (candidate === undefined) {
|
||||
const provenance = evaluateSemanticProvenance(binding, state);
|
||||
const initializer = semanticStableInitializer(binding, state);
|
||||
const bundled =
|
||||
initializer === undefined
|
||||
? undefined
|
||||
: semanticBundledRequireOriginCandidate(initializer.node, state)
|
||||
?.origin;
|
||||
const origin =
|
||||
bundled?.loaderAuthority === "candidate" &&
|
||||
initializer?.projection.every(
|
||||
(key): key is string => typeof key === "string",
|
||||
)
|
||||
? {
|
||||
...bundled,
|
||||
importedPath: [
|
||||
...bundled.importedPath,
|
||||
...initializer.projection,
|
||||
],
|
||||
}
|
||||
: provenance.status === "ambiguous" &&
|
||||
provenance.origins.length === 1
|
||||
? provenance.origins[0]
|
||||
: undefined;
|
||||
candidate =
|
||||
origin?.loaderAuthority === "candidate" &&
|
||||
ELECTRON_MODULE.test(origin.specifier)
|
||||
? electronExportPath(origin.importedPath)
|
||||
: null;
|
||||
candidatesByBinding.set(binding.bindingId, candidate);
|
||||
}
|
||||
return candidate ?? undefined;
|
||||
}
|
||||
const origin = (
|
||||
semanticRequireOriginCandidate(root, state) ??
|
||||
semanticBundledRequireOriginCandidate(root, state)
|
||||
)?.origin;
|
||||
return origin?.loaderAuthority === "candidate" &&
|
||||
ELECTRON_MODULE.test(origin.specifier)
|
||||
? electronExportPath(origin.importedPath)
|
||||
: undefined;
|
||||
};
|
||||
yield* collectJavaScriptIdentityWritesSteps(
|
||||
program,
|
||||
state.erasedSyntaxNodes,
|
||||
(node, kind) => {
|
||||
if (kind === "opaque-call") return [interop.opaqueMutationPath(node)];
|
||||
const { root, members } = electronMemberPath(node);
|
||||
const exported = kind === "binding" ? undefined : rootOrigin(root);
|
||||
const exported =
|
||||
kind === "binding"
|
||||
? undefined
|
||||
: (rootOrigin(root) ?? candidateRootExport(root));
|
||||
const path =
|
||||
exported === undefined
|
||||
? (interop.intrinsicPath(root) ??
|
||||
@@ -936,6 +1029,17 @@ export function* indexJavaScriptElectronIdentitySteps(
|
||||
!t.isNewExpression(node)
|
||||
)
|
||||
return;
|
||||
if (
|
||||
(t.isCallExpression(node) || t.isOptionalCallExpression(node)) &&
|
||||
t.isIdentifier(node.callee, { name: "require" })
|
||||
) {
|
||||
const origin = semanticRequireOriginCandidate(node, state)?.origin;
|
||||
if (
|
||||
origin?.loaderAuthority === "candidate" &&
|
||||
ELECTRON_MODULE.test(origin.specifier)
|
||||
)
|
||||
hasCandidateElectronSource = true;
|
||||
}
|
||||
const { root, members } = electronMemberPath(node.callee);
|
||||
const exported = rootExport(root);
|
||||
if (
|
||||
@@ -943,6 +1047,14 @@ export function* indexJavaScriptElectronIdentitySteps(
|
||||
intactExport(["electron", ...exported, ...members])
|
||||
)
|
||||
facts.set(root.start ?? -1, exported);
|
||||
else {
|
||||
const candidate = candidateRootExport(root);
|
||||
if (
|
||||
candidate !== undefined &&
|
||||
intactExport(["electron", ...candidate, ...members])
|
||||
)
|
||||
candidateFacts.set(root.start ?? -1, candidate);
|
||||
}
|
||||
},
|
||||
});
|
||||
const invocationPath = (
|
||||
@@ -965,14 +1077,23 @@ export function* indexJavaScriptElectronIdentitySteps(
|
||||
};
|
||||
return {
|
||||
bindings: facts,
|
||||
candidateBindings: candidateFacts,
|
||||
intactExport,
|
||||
exportPath: rootExport,
|
||||
candidateExportPath: candidateRootExport,
|
||||
invocationPath,
|
||||
mayChangeAppPath: createSemanticElectronAppPathChangeQuery(
|
||||
state,
|
||||
rootOrigin,
|
||||
interop,
|
||||
),
|
||||
candidateMayChangeAppPath: hasCandidateElectronSource
|
||||
? createSemanticElectronAppPathChangeQuery(
|
||||
state,
|
||||
candidateRootExport,
|
||||
interop,
|
||||
)
|
||||
: () => false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user