mirror of
https://github.com/morluto/rea.git
synced 2026-11-13 02:02:36 +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
@@ -17,6 +17,7 @@ import {
|
||||
semanticResolutionBlocked,
|
||||
semanticStableInitializer,
|
||||
type JavaScriptSemanticAnalysisState,
|
||||
type JavaScriptBindingIndex,
|
||||
} from "./javascriptSemanticState.js";
|
||||
import {
|
||||
commonJsWrapperBindingDeclaration,
|
||||
@@ -173,7 +174,7 @@ const requireLoaderValueAt = (
|
||||
if (loader.name !== "require") return "local";
|
||||
if (binding === undefined) {
|
||||
if (
|
||||
javaScriptRuntimeContext(state.program) === "module" ||
|
||||
javaScriptRuntimeContext(state.program) !== "commonjs" ||
|
||||
semanticResolutionBlocked(state, loader, "require")
|
||||
)
|
||||
return "unknown";
|
||||
@@ -199,7 +200,10 @@ const requireLoaderValueAt = (
|
||||
t.isNewExpression(node)) &&
|
||||
isWrapperRequireInitializerCall(binding, node, state)
|
||||
)
|
||||
return commonJsLoaderAt(node, state);
|
||||
return binding.hostOrigin === "commonjs-wrapper" ||
|
||||
javaScriptRuntimeContext(state.program) === "commonjs"
|
||||
? commonJsLoaderAt(node, state)
|
||||
: "unknown";
|
||||
const wrapperBinding = commonJsWrapperBindingDeclaration(
|
||||
loader,
|
||||
"require",
|
||||
@@ -376,13 +380,14 @@ const selectedRequireSourcesAt = (
|
||||
):
|
||||
| Pick<
|
||||
JavaScriptRequireCallFact,
|
||||
"loader" | "sourceRelative" | "sourceRelativePath"
|
||||
"loader" | "sourceRelative" | "sourceRelativePath" | "loaderAuthority"
|
||||
>
|
||||
| undefined => {
|
||||
const pending: ModuleObjectSource[] = [{ node: root, path: [] }];
|
||||
const seen = new WeakMap<t.Node, Set<string>>();
|
||||
let count = 0;
|
||||
let found = false;
|
||||
let candidate = false;
|
||||
let loaderBinding = false;
|
||||
let selectedLoader: JavaScriptRequireLoaderFact | undefined;
|
||||
let unknown = false;
|
||||
@@ -432,6 +437,10 @@ const selectedRequireSourcesAt = (
|
||||
? commonJsLoaderAt(site, state)
|
||||
: captured;
|
||||
found = true;
|
||||
candidate ||=
|
||||
invoked === "unknown" &&
|
||||
t.isIdentifier(node, { name: "require" }) &&
|
||||
isConditionalRequireAt(node, node, state);
|
||||
unknown ||=
|
||||
invoked === "unknown" ||
|
||||
(selectedLoader !== undefined && selectedLoader !== invoked);
|
||||
@@ -655,6 +664,7 @@ const selectedRequireSourcesAt = (
|
||||
? (selectedLoader ?? "unknown")
|
||||
: "local",
|
||||
sourceRelative: found && sourceRelative && !unknown,
|
||||
...(candidate ? { loaderAuthority: "candidate" as const } : {}),
|
||||
...(found &&
|
||||
!unknown &&
|
||||
sourceRelative &&
|
||||
@@ -701,23 +711,41 @@ export interface JavaScriptRequireCallFact {
|
||||
| readonly t.CallExpression["arguments"][number][]
|
||||
| undefined;
|
||||
readonly sourceRelative: boolean;
|
||||
readonly loaderAuthority?: "candidate";
|
||||
readonly memberUnproven?: true;
|
||||
/** A createRequire filename relative to the current source directory, when proven. */
|
||||
readonly sourceRelativePath?: string;
|
||||
}
|
||||
|
||||
/** A child-property write does not replace the selected callable itself. */
|
||||
export const semanticSelectedMemberMayHaveChanged = (
|
||||
index: JavaScriptBindingIndex,
|
||||
site: t.Node,
|
||||
member: t.Node,
|
||||
): boolean => {
|
||||
const effects = index.effectsForReferenceAt(site, member);
|
||||
const targetDepth = semanticMemberChain(member)?.members.length;
|
||||
return (
|
||||
!effects.memberEffectsIndexed ||
|
||||
effects.effects.some(
|
||||
({ kind, timing, path }) =>
|
||||
timing !== "after" &&
|
||||
(kind === "escape" ||
|
||||
(kind === "member-write" &&
|
||||
(targetDepth === undefined ||
|
||||
path === undefined ||
|
||||
path.length <= targetDepth))),
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/** Classify the selected loader and normalize invocation arguments once. */
|
||||
export const semanticRequireCallAt = (
|
||||
node: t.CallExpression | t.OptionalCallExpression | t.NewExpression,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
factoryBindingIds: ReadonlySet<string>,
|
||||
index?: JavaScriptBindingIndex,
|
||||
): JavaScriptRequireCallFact | undefined => {
|
||||
if (semanticModuleRequireCallAt(node, state))
|
||||
return {
|
||||
operation: "load",
|
||||
loader: "unknown",
|
||||
arguments: undefined,
|
||||
sourceRelative: false,
|
||||
};
|
||||
const callee = unwrapJavaScriptExpression(node.callee).node;
|
||||
if (
|
||||
(t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) &&
|
||||
@@ -733,6 +761,59 @@ export const semanticRequireCallAt = (
|
||||
arguments: undefined,
|
||||
sourceRelative: false,
|
||||
};
|
||||
if (index !== undefined) {
|
||||
let member = callee;
|
||||
while (
|
||||
t.isMemberExpression(member) ||
|
||||
t.isOptionalMemberExpression(member)
|
||||
) {
|
||||
const key = semanticStaticPropertyKey(member.property, member.computed);
|
||||
if (key === "call" || key === "apply" || key === "resolve") {
|
||||
const loader = requireLoaderValueAt(
|
||||
member.object,
|
||||
node,
|
||||
state,
|
||||
factoryBindingIds,
|
||||
);
|
||||
const selected =
|
||||
loader === "local"
|
||||
? selectedRequireSourcesAt(
|
||||
member.object,
|
||||
node,
|
||||
state,
|
||||
factoryBindingIds,
|
||||
)
|
||||
: undefined;
|
||||
if (
|
||||
loader === "local" &&
|
||||
(selected === undefined || selected.loader === "local")
|
||||
) {
|
||||
member = unwrapJavaScriptExpression(member.object).node;
|
||||
continue;
|
||||
}
|
||||
if (semanticSelectedMemberMayHaveChanged(index, node, member))
|
||||
return {
|
||||
operation: "load",
|
||||
loader: "unknown",
|
||||
// A changed call/apply member has no trustworthy argument mapping.
|
||||
arguments:
|
||||
member === callee && key === "resolve"
|
||||
? node.arguments
|
||||
: undefined,
|
||||
sourceRelative: false,
|
||||
memberUnproven: true,
|
||||
};
|
||||
}
|
||||
member = unwrapJavaScriptExpression(member.object).node;
|
||||
}
|
||||
}
|
||||
if (semanticModuleRequireCallAt(node, state))
|
||||
return {
|
||||
operation: "load",
|
||||
loader: "unknown",
|
||||
arguments: undefined,
|
||||
sourceRelative: false,
|
||||
};
|
||||
if (
|
||||
(t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee)) &&
|
||||
semanticStaticPropertyKey(callee.property, callee.computed) === "resolve"
|
||||
@@ -743,13 +824,19 @@ export const semanticRequireCallAt = (
|
||||
state,
|
||||
factoryBindingIds,
|
||||
);
|
||||
if (loader === "unknown")
|
||||
if (loader === "unknown") {
|
||||
const receiver = unwrapJavaScriptExpression(callee.object).node;
|
||||
const candidate =
|
||||
t.isIdentifier(receiver, { name: "require" }) &&
|
||||
isConditionalRequireAt(receiver, node, state);
|
||||
return {
|
||||
operation: "load",
|
||||
operation: "resolve",
|
||||
loader,
|
||||
arguments: undefined,
|
||||
arguments: candidate ? node.arguments : undefined,
|
||||
sourceRelative: false,
|
||||
...(candidate ? { loaderAuthority: "candidate" as const } : {}),
|
||||
};
|
||||
}
|
||||
// Bundler factory loaders can share the `require` shape, but their
|
||||
// `.resolve` member is not Node's module resolver.
|
||||
if (loader === "commonjs-loader" || loader === "create-require") {
|
||||
@@ -790,6 +877,9 @@ export const semanticRequireCallAt = (
|
||||
loader: selected?.loader ?? loader,
|
||||
arguments: node.arguments,
|
||||
sourceRelative: selected?.sourceRelative ?? loader !== "create-require",
|
||||
...(selected?.loaderAuthority === "candidate"
|
||||
? { loaderAuthority: "candidate" as const }
|
||||
: {}),
|
||||
...(selected?.sourceRelativePath === undefined
|
||||
? {}
|
||||
: { sourceRelativePath: selected.sourceRelativePath }),
|
||||
@@ -804,8 +894,19 @@ export const semanticRequireCallAt = (
|
||||
state,
|
||||
factoryBindingIds,
|
||||
);
|
||||
if (loader !== "local" && loader !== "create-require")
|
||||
return ["require-loader", loader, "relative"];
|
||||
if (loader !== "local" && loader !== "create-require") {
|
||||
const root = unwrapJavaScriptExpression(callee).node;
|
||||
const candidate =
|
||||
loader === "unknown" &&
|
||||
t.isIdentifier(root, { name: "require" }) &&
|
||||
isConditionalRequireAt(root, node, state);
|
||||
return [
|
||||
"require-loader",
|
||||
loader,
|
||||
"relative",
|
||||
...(candidate ? ["candidate"] : []),
|
||||
];
|
||||
}
|
||||
const selected = selectedRequireSourcesAt(
|
||||
callee,
|
||||
node,
|
||||
@@ -817,6 +918,7 @@ export const semanticRequireCallAt = (
|
||||
"require-loader",
|
||||
selected.loader,
|
||||
selected.sourceRelative ? "relative" : "unknown",
|
||||
...(selected.loaderAuthority === "candidate" ? ["candidate"] : []),
|
||||
];
|
||||
return interop.intrinsicPath(callee);
|
||||
});
|
||||
@@ -836,6 +938,9 @@ export const semanticRequireCallAt = (
|
||||
? "unknown"
|
||||
: (invocation.path[1] as JavaScriptRequireLoaderFact),
|
||||
arguments: invocation.arguments,
|
||||
...(invocation.kind === "known" && invocation.path[3] === "candidate"
|
||||
? { loaderAuthority: "candidate" as const }
|
||||
: {}),
|
||||
sourceRelative:
|
||||
invocation.kind === "known" && invocation.path[2] === "relative",
|
||||
...(selected?.sourceRelativePath === undefined
|
||||
@@ -1267,6 +1372,18 @@ const hasCommonJsModuleSource = (
|
||||
commonJsWrapperBindingDeclaration(value, "require", state) !==
|
||||
undefined &&
|
||||
!read.hasDefiniteInitializer;
|
||||
// A top-level `var require` may refer to a host wrapper on an unknown
|
||||
// runtime. Preserve a possible member-loader call without resolving it.
|
||||
if (
|
||||
includeUnknown &&
|
||||
mainModulePath &&
|
||||
javaScriptRuntimeContext(state.program) === "unknown" &&
|
||||
state.scopesById.get(binding.scopeId)?.kind === "program" &&
|
||||
binding.declarationKinds.size === 1 &&
|
||||
binding.declarationKinds.has("var") &&
|
||||
!read.hasDefiniteInitializer
|
||||
)
|
||||
return true;
|
||||
if (
|
||||
value.name === "module" &&
|
||||
copyAdmitsModuleDelegate &&
|
||||
@@ -1549,40 +1666,89 @@ export const semanticGlobalProcessPathOrigin = (
|
||||
};
|
||||
};
|
||||
|
||||
/** Check one unknown-host wrapper candidate at its source-ordered read. */
|
||||
const isConditionalRequireAt = (
|
||||
root: t.Identifier,
|
||||
node: t.Node,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
allowAmbientModule = false,
|
||||
): boolean => {
|
||||
const runtime = javaScriptRuntimeContext(state.program);
|
||||
if (
|
||||
(runtime !== "unknown" && !(allowAmbientModule && runtime === "module")) ||
|
||||
semanticResolutionBlocked(state, root, "require") ||
|
||||
commonJsDelegateChangedAt(node, state)
|
||||
)
|
||||
return false;
|
||||
const binding = resolveSemanticBindingState(state, root, "require");
|
||||
if (
|
||||
binding !== undefined &&
|
||||
(binding.hostOrigin === "commonjs-wrapper" ||
|
||||
state.scopesById.get(binding.scopeId)?.kind !== "program" ||
|
||||
binding.declarationKinds.size !== 1 ||
|
||||
!binding.declarationKinds.has("var") ||
|
||||
(semanticBindingHasPriorValueWrite(node, binding, state) &&
|
||||
!(
|
||||
(t.isCallExpression(node) ||
|
||||
t.isOptionalCallExpression(node) ||
|
||||
t.isNewExpression(node)) &&
|
||||
isWrapperRequireInitializerCall(binding, node, state)
|
||||
)))
|
||||
)
|
||||
return false;
|
||||
return !(state.ambientBindingWrites.get("require") ?? []).some(
|
||||
(write) =>
|
||||
!semanticTopLevelEffectFollowsCapture(node, write, state.parentsByNode),
|
||||
);
|
||||
};
|
||||
|
||||
/** Recover an unshadowed literal require origin and its exact member path. */
|
||||
export const semanticRequireOrigin = (
|
||||
export const semanticRequireOriginCandidate = (
|
||||
node: t.Node | null | undefined,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
allowAmbientModule = false,
|
||||
): JavaScriptRequireOrigin | undefined => {
|
||||
const chain = semanticMemberChain(node);
|
||||
const current = chain?.root;
|
||||
if (
|
||||
chain === undefined ||
|
||||
!t.isCallExpression(current) ||
|
||||
(!t.isCallExpression(current) && !t.isOptionalCallExpression(current)) ||
|
||||
!t.isIdentifier(current.callee, { name: "require" })
|
||||
)
|
||||
return undefined;
|
||||
if (commonJsDelegateChangedAt(current, state)) return undefined;
|
||||
const requireBinding = resolveSemanticBindingState(
|
||||
state,
|
||||
current.callee,
|
||||
"require",
|
||||
);
|
||||
const binding = resolveSemanticBindingState(state, current.callee, "require");
|
||||
const runtime = javaScriptRuntimeContext(state.program);
|
||||
const candidate =
|
||||
(runtime === "unknown" ||
|
||||
(allowAmbientModule && runtime === "module" && binding === undefined)) &&
|
||||
binding?.hostOrigin !== "commonjs-wrapper";
|
||||
if (
|
||||
(requireBinding !== undefined &&
|
||||
!isUnmodifiedCommonJsWrapperBinding(current.callee, "require", state) &&
|
||||
!isWrapperRequireInitializerCall(requireBinding, current, state)) ||
|
||||
(requireBinding === undefined &&
|
||||
(javaScriptRuntimeContext(state.program) === "module" ||
|
||||
semanticResolutionBlocked(state, current.callee, "require"))) ||
|
||||
(state.ambientBindingWrites.get("require") ?? []).some(
|
||||
(write) =>
|
||||
!semanticTopLevelEffectFollowsCapture(
|
||||
candidate
|
||||
? !isConditionalRequireAt(
|
||||
current.callee,
|
||||
current,
|
||||
write,
|
||||
state.parentsByNode,
|
||||
),
|
||||
)
|
||||
state,
|
||||
allowAmbientModule,
|
||||
)
|
||||
: commonJsDelegateChangedAt(current, state) ||
|
||||
(binding !== undefined &&
|
||||
!isUnmodifiedCommonJsWrapperBinding(
|
||||
current.callee,
|
||||
"require",
|
||||
state,
|
||||
) &&
|
||||
!isWrapperRequireInitializerCall(binding, current, state)) ||
|
||||
(binding === undefined &&
|
||||
(javaScriptRuntimeContext(state.program) === "module" ||
|
||||
semanticResolutionBlocked(state, current.callee, "require"))) ||
|
||||
(state.ambientBindingWrites.get("require") ?? []).some(
|
||||
(write) =>
|
||||
!semanticTopLevelEffectFollowsCapture(
|
||||
current,
|
||||
write,
|
||||
state.parentsByNode,
|
||||
),
|
||||
)
|
||||
)
|
||||
return undefined;
|
||||
const specifier = stringValue(current.arguments[0]);
|
||||
@@ -1593,11 +1759,21 @@ export const semanticRequireOrigin = (
|
||||
specifier,
|
||||
importedPath: chain.members,
|
||||
resolutionMode: "require",
|
||||
...(candidate ? { loaderAuthority: "candidate" as const } : {}),
|
||||
},
|
||||
call: current,
|
||||
};
|
||||
};
|
||||
|
||||
/** Only a proven host loader may establish Node builtin identity. */
|
||||
export const semanticRequireOrigin = (
|
||||
node: t.Node | null | undefined,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): JavaScriptRequireOrigin | undefined => {
|
||||
const result = semanticRequireOriginCandidate(node, state);
|
||||
return result?.origin.loaderAuthority === "candidate" ? undefined : result;
|
||||
};
|
||||
|
||||
/** Node's exported child objects retain their direct builtin-module identity. */
|
||||
export const semanticRequireExportsIdentity = <Key>(
|
||||
specifier: string,
|
||||
@@ -1656,4 +1832,5 @@ export const semanticMutableBuiltinOrigin = <Key = never>(
|
||||
};
|
||||
|
||||
semanticRequireOriginReaders.origin = semanticRequireOrigin;
|
||||
semanticRequireOriginReaders.candidate = semanticRequireOriginCandidate;
|
||||
semanticRequireOriginReaders.loader = semanticRequireLoaderAt;
|
||||
Reference in new issue
Block a user