Files
reverse-engineering-anything/src/domain/referenceSourceRequireBindings.ts
T
morlutoandCursor Agent cb17828dd1 fix(reference): retain proven loader calls and unresolved targets (#9920)
* fix(reference): retain proven loader calls and unresolved targets

* refactor(reference): remove unused ancestor lookup

* fix(reference): drop loader calls that return a reassigned member

An immediate call could still use a member's original require initializer
because mutation facts were not built for a call-valued callee. A definite
assignment that precedes the call now replaces that initializer.

* fix(reference): select returned loaders from indexed read facts

* fix(reference): refresh loader facts for wrapped returned members

A sequence such as (0, box.load) still used the original require
initializer. Follow sequence, conditional, and logical returns to the
member before deciding whether a prior assignment replaced it.

* test(reference): record unresolved CommonJS targets in source evidence

unknown.cjs calls require with a non-literal target. The inventory now
keeps that coverage limitation on the file entry, and the boundary
fixture asserts it beside the resolved member relationships.

* fix(reference): cover opaque apply lists and assigned returned members

require.apply(null, args) has no normalized argument list, so the
inventory now records an unresolved target instead of complete coverage.
An assignment return such as f = box.load is followed to the member
before a prior replacement is trusted.

* fix(reference): keep self-defaulted module delegates loadable

Timing a member read at a later call made `box.load = box.load || null` observe its own write and drop the native delegate. Reads that already fed an earlier assignment stay at the member.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-11 18:23:15 +08:00

236 lines
8.4 KiB
TypeScript

import * as t from "@babel/types";
import { posix } from "node:path";
import {
semanticMemberChain,
semanticStaticPropertyKey,
unwrapJavaScriptExpression,
} from "./javascript/javascriptAstValues.js";
import { isUnmodifiedGlobalReference } from "./javascript/javascriptSemanticAmbientWrites.js";
import {
indexJavaScriptBindingsWithReceiversSteps,
type JavaScriptReceiverIndex,
} from "./javascript/javascriptSemanticAnalysis.js";
import type { JavaScriptSemanticAnalysisState } from "./javascript/javascriptSemanticState.js";
import {
semanticRequireCallAt,
semanticRequireLoaderAt,
semanticSelectedMemberMayHaveChanged,
type JavaScriptRequireCallFact,
} from "./javascript/javascriptSemanticRequireOrigin.js";
import {
completeSemanticSteps,
traverseJavaScriptAst,
} from "./javascript/javascriptSemanticTraversal.js";
import {
javaScriptRuntimeContext,
type ParsedJavaScriptSource,
} from "./javascript/javascriptSourceParser.js";
export type ReferenceSourceRequireResolutionBase =
| "source-file"
| { readonly kind: "source-relative-file"; readonly path: string }
| "unknown";
export interface ReferenceSourceRequireCall {
readonly arguments: JavaScriptRequireCallFact["arguments"];
readonly resolutionBase: ReferenceSourceRequireResolutionBase;
readonly limitation?: string;
}
export type ReferenceSourceRequireInvocation =
| t.CallExpression
| t.OptionalCallExpression
| t.NewExpression;
/** Identify exact calls through a source-owned Node loader and their resolver base. */
export const referenceSourceRequireCalls = (
file: Pick<ParsedJavaScriptSource, "program">,
path: string,
): ReadonlyMap<
ReferenceSourceRequireInvocation,
ReferenceSourceRequireCall
> => {
// Unmarked paths depend on package scope or compiler options. Preserve their
// candidate CommonJS edges; explicit module paths have no wrapper loader.
const runtimeContext = javaScriptRuntimeContext(file.program);
const index = completeSemanticSteps(
indexJavaScriptBindingsWithReceiversSteps(
file.program,
runtimeContext === "module" ? undefined : "commonjs-wrapper",
),
);
const factoryBindings: ReadonlySet<string> = new Set();
const candidates: {
readonly call: ReferenceSourceRequireInvocation;
readonly arguments: JavaScriptRequireCallFact["arguments"];
readonly resolutionBase: ReferenceSourceRequireResolutionBase;
readonly limitation?: string;
}[] = [];
traverseJavaScriptAst(file.program, {
enter: (node) => {
if (
!t.isCallExpression(node) &&
!t.isOptionalCallExpression(node) &&
!t.isNewExpression(node)
)
return;
const callee = unwrapJavaScriptExpression(node.callee).node;
if (
!t.isCallExpression(callee) &&
!t.isOptionalCallExpression(callee) &&
!hasIdentifierReceiver(callee)
)
return;
if (t.isCallExpression(callee) || t.isOptionalCallExpression(callee))
completeSemanticSteps(index.indexMemberMutationFactsSteps());
let fact = semanticRequireLoaderAt(node, index.bindings, factoryBindings);
if (
fact === "create-require" ||
((t.isMemberExpression(callee) ||
t.isOptionalMemberExpression(callee)) &&
["call", "apply", "resolve"].includes(
semanticStaticPropertyKey(callee.property, callee.computed) ?? "",
))
) {
completeSemanticSteps(index.indexMemberMutationFactsSteps());
fact = semanticRequireLoaderAt(node, index.bindings, factoryBindings);
}
const callFact = semanticRequireCallAt(
node,
index.bindings,
factoryBindings,
index,
);
if (callFact === undefined || callFact.loader === "local") return;
if (
fact !== "create-require" &&
fact !== "commonjs-loader" &&
callFact.loader !== "create-require" &&
callFact.loader !== "commonjs-loader"
)
return;
const sourceRelativePath = callFact.sourceRelativePath;
const normalizedSourceRelativePath =
sourceRelativePath === undefined
? undefined
: posix.normalize(
posix.join(posix.dirname(path), sourceRelativePath),
);
const base: ReferenceSourceRequireResolutionBase =
runtimeContext === "unknown"
? "unknown"
: callFact.loader === "create-require"
? sourceRelativePath === undefined
? callFact.sourceRelative === true
? "source-file"
: "unknown"
: normalizedSourceRelativePath === undefined ||
normalizedSourceRelativePath === ".." ||
normalizedSourceRelativePath.startsWith("../")
? "unknown"
: normalizedSourceRelativePath === path
? "source-file"
: {
kind: "source-relative-file",
path: normalizedSourceRelativePath,
}
: callFact.loader === "commonjs-loader" &&
runtimeContext === "commonjs"
? "source-file"
: "unknown";
const searchBase = requireResolveSearchBase(
callFact.operation,
callFact.arguments,
index.bindings,
base,
runtimeContext === "commonjs",
);
candidates.push({
call: node,
arguments: callFact.arguments,
resolutionBase: searchBase,
...(runtimeContext === "unknown"
? {
limitation: `Module context is unknown in ${path}; the requested dependency was retained without a proven loader base.`,
}
: {}),
});
},
});
if (candidates.some(({ call }) => hasMemberCallee(call)))
completeSemanticSteps(index.indexMemberMutationFactsSteps());
return new Map(
candidates.map(
({ call, arguments: callArguments, resolutionBase, limitation }) =>
[
call,
{
arguments: callArguments,
...(limitation === undefined ? {} : { limitation }),
resolutionBase:
hasMemberCallee(call) && hasPriorMemberMutation(index, call)
? "unknown"
: resolutionBase,
},
] as const,
),
);
};
/** `require.resolve` options replace the default search paths unless exact current-directory paths are supplied. */
const requireResolveSearchBase = (
operation: JavaScriptRequireCallFact["operation"],
callArguments: JavaScriptRequireCallFact["arguments"],
bindings: JavaScriptSemanticAnalysisState,
loaderBase: ReferenceSourceRequireResolutionBase,
hasCommonJsWrapper: boolean,
): ReferenceSourceRequireResolutionBase => {
const arguments_ = callArguments ?? [];
if (operation !== "resolve" || arguments_.length < 2) return loaderBase;
if (arguments_.length !== 2) return "unknown";
const options = t.isExpression(arguments_[1]) ? arguments_[1] : undefined;
if (options === undefined || !t.isObjectExpression(options)) return "unknown";
if (options.properties.length === 0) return loaderBase;
if (options.properties.length !== 1) return "unknown";
const property = options.properties[0];
if (!t.isObjectProperty(property)) return "unknown";
if (semanticStaticPropertyKey(property.key, property.computed) !== "paths")
return "unknown";
const value = t.isExpression(property.value) ? property.value : undefined;
if (!t.isArrayExpression(value) || value.elements.length !== 1)
return "unknown";
const directory = value.elements[0];
return hasCommonJsWrapper &&
t.isIdentifier(directory, { name: "__dirname" }) &&
isUnmodifiedGlobalReference(directory, bindings, "__dirname")
? "source-file"
: "unknown";
};
const hasPriorMemberMutation = (
index: JavaScriptReceiverIndex,
site: ReferenceSourceRequireInvocation,
): boolean => {
const callee = unwrapJavaScriptExpression(site.callee).node;
return (
semanticMemberChain(callee) === undefined ||
semanticSelectedMemberMayHaveChanged(index, site, callee)
);
};
const hasMemberCallee = (call: ReferenceSourceRequireInvocation): boolean => {
const callee = unwrapJavaScriptExpression(call.callee).node;
return t.isMemberExpression(callee) || t.isOptionalMemberExpression(callee);
};
const hasIdentifierReceiver = (callee: t.Node): boolean => {
let expression = callee;
while (
t.isMemberExpression(expression) ||
t.isOptionalMemberExpression(expression)
)
expression = unwrapJavaScriptExpression(expression.object).node;
return t.isIdentifier(expression);
};