mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
fix(javascript): resolve module-level Electron options in functions (#8960)
* fix(javascript): resolve module-level Electron options in functions * fix(javascript): limit module option ancestry walk * fix(javascript): preserve uncertain captured argument ordering * fix(javascript): retain hoisted activation uncertainty * fix(javascript): retain function ownership through loops * fix(javascript): account for classic global activations * fix(javascript): bind first activations to lexical identities * fix(javascript): retain uncertain global activation timing * fix(javascript): isolate receiver facts and share computed reads * fix(javascript): retain projected and opaque global activations * fix(javascript): refresh activation bounds as opaque effects grow * fix(ci): format website watch directories without changing configuration * fix(javascript): share source reads across opaque-effect updates * fix(javascript): retain constructed-code global activation bounds * fix(javascript): share runtime activation and receiver facts * fix(javascript): reuse pattern reads and function positions * fix(javascript): isolate copied paths and preserve runtime reads * style(javascript): format migrated reference imports * fix(javascript): normalize constructor activation and receiver effects * fix(javascript): retain aliased receiver timing and lexical global this * fix(javascript): preserve effect-free receiver identity across activations * fix(javascript): retain outer activation and prototype effects * fix(javascript): index implicit reads without deep path rescans * fix(javascript): retain aliased prototype and parameter read uncertainty * fix(javascript): index reflected reads through intrinsic invocation facts * fix(javascript): retain unresolved global activation reads * chore: refresh PR branch after review fix publication * fix(javascript): retain projected receiver reads and cache method facts * fix(javascript): retain inherited and implicit activation reads * fix(javascript): retain caller-owned this uncertainty * fix(javascript): retain reflected reads with unresolved spread arguments * fix(javascript): preserve enclosing activation for decorators * fix(javascript): track intrinsic literal prototype effects * fix(javascript): retain catch activation from implicit reads * perf(javascript): reuse effective keys for projected reads * fix(javascript): retain possible caller-owned ambient effects
This commit is contained in:
1 parent
ce07d358cd
commit
a2134b3a74
40 files changed
+2585
-191
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { semanticPatternPropertyReadSources } from "./javascriptSemanticIterationSources.js";
|
||||
import {
|
||||
createSemanticOpaqueGlobalObserver,
|
||||
indexSemanticOpaqueEffects,
|
||||
@@ -9,7 +10,7 @@ import type { JavaScriptOpenReceiverFact } from "./javascriptSemanticReceiverFac
|
||||
import * as t from "@babel/types";
|
||||
|
||||
import {
|
||||
semanticComputedMethodKey,
|
||||
semanticMethodDefinitionExpression,
|
||||
semanticBranchMayBeSkipped,
|
||||
semanticPriorStatementMayExit,
|
||||
semanticIterationCardinality,
|
||||
@@ -48,6 +49,8 @@ import type {
|
||||
} from "./javascriptSemanticState.js";
|
||||
import {
|
||||
currentSemanticScope,
|
||||
semanticPrototypeOwners,
|
||||
type SemanticPrototypeOwner,
|
||||
resolveSemanticBindingState,
|
||||
semanticResolutionBlocked,
|
||||
resolveSemanticBindingFromScope,
|
||||
@@ -228,6 +231,7 @@ function* indexSemanticMemberMutationFactsSteps(
|
||||
(site, reference, path) =>
|
||||
referenceEffectsMayHaveChanged(state, site, reference, path),
|
||||
);
|
||||
state.memberMutationFactsComplete = true;
|
||||
indexedMemberMutationStates.add(state);
|
||||
invalidateSemanticRequireDelegateWrites(state);
|
||||
}
|
||||
@@ -1041,9 +1045,10 @@ const createState = (
|
||||
};
|
||||
return {
|
||||
arrayIterationUnknown: false,
|
||||
memberMutationFactsComplete: false,
|
||||
program,
|
||||
opaqueEffects: {
|
||||
mayReferenceGlobalEval: false,
|
||||
mayReferenceGlobalCode: false,
|
||||
sites: new WeakSet(),
|
||||
sloppySites: new WeakSet(),
|
||||
byScope: new Map(),
|
||||
@@ -1063,6 +1068,8 @@ const createState = (
|
||||
bindingWrites: new Map(),
|
||||
prototypeRequireMutationObserved: false,
|
||||
prototypeEnumerationMayAffect: false,
|
||||
prototypeMutationSites: [],
|
||||
implicitPropertyReads: new WeakMap(),
|
||||
callables: [],
|
||||
callableNodesById: new Map(),
|
||||
moduleLinks: [],
|
||||
@@ -1081,8 +1088,9 @@ const createState = (
|
||||
};
|
||||
};
|
||||
|
||||
const globalEvalRoots = new Set([
|
||||
const globalCodeRoots = new Set([
|
||||
"eval",
|
||||
"Function",
|
||||
"globalThis",
|
||||
"global",
|
||||
"window",
|
||||
@@ -1120,6 +1128,8 @@ function* collectDefinitionsSteps(
|
||||
scope: JavaScriptSemanticScopeState;
|
||||
}[] = [];
|
||||
let needsPrototypeFacts = false;
|
||||
const implicitReadCalls: (t.CallExpression | t.OptionalCallExpression)[] = [];
|
||||
let mayReferenceReflect = false;
|
||||
const functionBodies: {
|
||||
readonly body: t.BlockStatement;
|
||||
readonly scope: JavaScriptSemanticScopeState;
|
||||
@@ -1138,15 +1148,17 @@ function* collectDefinitionsSteps(
|
||||
if (
|
||||
t.isWithStatement(node) ||
|
||||
t.isForInStatement(node) ||
|
||||
t.isForOfStatement(node)
|
||||
t.isForOfStatement(node) ||
|
||||
t.isArrayExpression(node) ||
|
||||
t.isFunction(node)
|
||||
)
|
||||
needsPrototypeFacts = true;
|
||||
let parentScope = currentSemanticScope(stack);
|
||||
if (
|
||||
semanticComputedMethodKey(parent ?? undefined, node) &&
|
||||
semanticMethodDefinitionExpression(parent ?? undefined, node) &&
|
||||
parentScope.parentScopeId !== null
|
||||
) {
|
||||
// A computed method key is evaluated before entering the method's
|
||||
// A method definition expression is evaluated before entering the method's
|
||||
// parameter and body environments.
|
||||
const keyScope = state.scopesById.get(parentScope.parentScopeId);
|
||||
if (keyScope !== undefined) {
|
||||
@@ -1211,12 +1223,26 @@ function* collectDefinitionsSteps(
|
||||
state,
|
||||
});
|
||||
bindInnerDeclaration(node, parent, scope, state);
|
||||
if (
|
||||
t.isIdentifier(node, { name: "Reflect" }) ||
|
||||
t.isStringLiteral(node, { value: "Reflect" }) ||
|
||||
(t.isTemplateLiteral(node) &&
|
||||
semanticStaticPropertyKey(node, true) === "Reflect")
|
||||
)
|
||||
mayReferenceReflect = true;
|
||||
// Aliases and optional/call/apply/bind forms share the intrinsic owner;
|
||||
// callee spelling alone cannot identify the property-read operation.
|
||||
if (t.isCallExpression(node) || t.isOptionalCallExpression(node))
|
||||
implicitReadCalls.push(node);
|
||||
if (t.isThrowStatement(node))
|
||||
thrownValues.push({ node, ancestors: readAncestors() });
|
||||
// Every supported global eval origin starts with one of these decoded
|
||||
// Every supported global-code origin starts with one of these decoded
|
||||
// identifiers, including computed properties and destructured aliases.
|
||||
if (t.isIdentifier(node) && globalEvalRoots.has(node.name))
|
||||
state.opaqueEffects.mayReferenceGlobalEval = true;
|
||||
if (
|
||||
t.isThisExpression(node) ||
|
||||
(t.isIdentifier(node) && globalCodeRoots.has(node.name))
|
||||
)
|
||||
state.opaqueEffects.mayReferenceGlobalCode = true;
|
||||
if (
|
||||
t.isCallExpression(node) &&
|
||||
t.isIdentifier(unwrapJavaScriptExpression(node.callee).node, {
|
||||
@@ -1328,6 +1354,15 @@ function* collectDefinitionsSteps(
|
||||
parent.handler != null &&
|
||||
child === parent.block
|
||||
) {
|
||||
if (parent.handler.param != null)
|
||||
state.implicitPropertyReads.set(
|
||||
node,
|
||||
semanticPatternPropertyReadSources(
|
||||
parent.handler.param,
|
||||
{ node: node.argument, projection: [] },
|
||||
node,
|
||||
),
|
||||
);
|
||||
const scope = state.scopeByNode.get(parent.handler);
|
||||
if (scope !== undefined && parent.handler.param != null)
|
||||
bindPattern({
|
||||
@@ -1398,36 +1433,85 @@ function* collectDefinitionsSteps(
|
||||
if (t.isIdentifier(identifier))
|
||||
copyParameterToBody({ identifier, body, scope, state });
|
||||
}
|
||||
const readInterop = mayReferenceReflect
|
||||
? createSemanticModuleInterop(state)
|
||||
: undefined;
|
||||
for (const node of readInterop === undefined ? [] : implicitReadCalls) {
|
||||
const invocation = readInterop?.intrinsicInvocation(node);
|
||||
if (
|
||||
invocation?.kind !== "known" ||
|
||||
invocation.path.join(".") !== "intrinsic.Reflect.get"
|
||||
)
|
||||
continue;
|
||||
const [receiver, key] = invocation.arguments ?? [];
|
||||
const receiverUnknown =
|
||||
invocation.arguments === undefined || t.isSpreadElement(receiver);
|
||||
if (!receiverUnknown && !t.isExpression(receiver)) continue;
|
||||
const selected =
|
||||
!receiverUnknown && (t.isStringLiteral(key) || t.isNumericLiteral(key))
|
||||
? key.value
|
||||
: null;
|
||||
state.implicitPropertyReads.set(node, [
|
||||
{
|
||||
node: receiverUnknown ? node : (receiver ?? node),
|
||||
projection: receiverUnknown ? [null, selected] : [selected],
|
||||
mutation: node,
|
||||
lookup: "prototype",
|
||||
},
|
||||
]);
|
||||
}
|
||||
indexSemanticOpaqueEffects(opaqueCandidates, state);
|
||||
if (state.opaqueEffects.byScope.size > 0) clearSemanticValueCaches(state);
|
||||
}
|
||||
|
||||
type PrototypeOwner = "Object" | "Array" | "String";
|
||||
|
||||
const prototypeOwners = ["Object", "Array", "String"] as const;
|
||||
|
||||
/** Preserve uncertainty when source can change or expose a built-in prototype. */
|
||||
function* indexPrototypeUncertaintySteps(
|
||||
program: t.Program,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
): Generator<void, void> {
|
||||
const taint = (owner: PrototypeOwner): void => {
|
||||
let effectSite: t.Node = program;
|
||||
const seen = new Map<SemanticPrototypeOwner, WeakSet<t.Node>>();
|
||||
const possiblePrototypeReferences = createAmbientPathResolver(state, false);
|
||||
const taint = (owner: SemanticPrototypeOwner, inherited = true): void => {
|
||||
let sites = seen.get(owner);
|
||||
if (sites === undefined) {
|
||||
sites = new WeakSet();
|
||||
seen.set(owner, sites);
|
||||
}
|
||||
if (inherited && !sites.has(effectSite)) {
|
||||
sites.add(effectSite);
|
||||
state.prototypeMutationSites.push({ owner, node: effectSite });
|
||||
}
|
||||
if (owner === "Object") state.prototypeRequireMutationObserved = undefined;
|
||||
state.prototypeEnumerationMayAffect = undefined;
|
||||
};
|
||||
const globalOwner = (node: t.Node): PrototypeOwner | undefined => {
|
||||
const globalOwner = (node: t.Node): SemanticPrototypeOwner | undefined => {
|
||||
const value = unwrapJavaScriptExpression(node).node;
|
||||
for (const owner of prototypeOwners) {
|
||||
if (isUnmodifiedGlobalReference(value, state, owner)) return owner;
|
||||
if (
|
||||
t.isIdentifier(value, { name: owner }) &&
|
||||
resolveSemanticBindingState(state, value, owner) === undefined
|
||||
)
|
||||
return owner;
|
||||
const member =
|
||||
t.isMemberExpression(value) || t.isOptionalMemberExpression(value);
|
||||
const key = t.isIdentifier(value)
|
||||
? value.name
|
||||
: member
|
||||
? semanticStaticPropertyKey(value.property, value.computed)
|
||||
: null;
|
||||
const owner = semanticPrototypeOwners.find(
|
||||
(candidate) => candidate === key,
|
||||
);
|
||||
if (owner === undefined) return undefined;
|
||||
if (isUnmodifiedGlobalReference(value, state, owner)) return owner;
|
||||
if (member) {
|
||||
const { paths, callerPaths } = possiblePrototypeReferences(value);
|
||||
const selectsOwner = (path: readonly (string | null)[]) =>
|
||||
path.length === 2 && path[0] === "global" && path[1] === owner;
|
||||
return paths.some(selectsOwner) || callerPaths.some(selectsOwner)
|
||||
? owner
|
||||
: undefined;
|
||||
}
|
||||
return undefined;
|
||||
return resolveSemanticBindingState(state, value, owner) === undefined
|
||||
? owner
|
||||
: undefined;
|
||||
};
|
||||
const prototypeOwner = (node: t.Node): PrototypeOwner | undefined => {
|
||||
const prototypeOwner = (node: t.Node): SemanticPrototypeOwner | undefined => {
|
||||
const value = unwrapJavaScriptExpression(node).node;
|
||||
return (t.isMemberExpression(value) ||
|
||||
t.isOptionalMemberExpression(value)) &&
|
||||
@@ -1451,6 +1535,7 @@ function* indexPrototypeUncertaintySteps(
|
||||
]);
|
||||
yield* traverseJavaScriptValueAstSteps(program, state.erasedSyntaxNodes, {
|
||||
enter: (node, parent) => {
|
||||
effectSite = node;
|
||||
const write = semanticWriteSiteTarget(node);
|
||||
if (write !== null) {
|
||||
for (const target of semanticWriteTargets(write)) {
|
||||
@@ -1459,9 +1544,15 @@ function* indexPrototypeUncertaintySteps(
|
||||
t.isMemberExpression(value) ||
|
||||
t.isOptionalMemberExpression(value)
|
||||
) {
|
||||
const owner =
|
||||
prototypeOwner(value.object) ?? globalOwner(value.object);
|
||||
if (owner !== undefined) taint(owner);
|
||||
const prototype = prototypeOwner(value.object);
|
||||
const owner = prototype ?? globalOwner(value.object);
|
||||
if (owner !== undefined)
|
||||
taint(
|
||||
owner,
|
||||
prototype !== undefined ||
|
||||
semanticStaticPropertyKey(value.property, value.computed) ===
|
||||
"prototype",
|
||||
);
|
||||
} else {
|
||||
const owner = globalOwner(value);
|
||||
if (owner !== undefined) taint(owner);
|
||||
|
||||
Reference in new issue
Block a user