mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
* 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
149 lines
5.3 KiB
TypeScript
149 lines
5.3 KiB
TypeScript
import type * as t from "@babel/types";
|
|
import type { JavaScriptSemanticAnalysisState } from "./javascriptSemanticState.js";
|
|
import {
|
|
semanticArrayIndex,
|
|
semanticPropertyPathKeyMatches,
|
|
type JavaScriptSemanticPropertyPath as PropertyPath,
|
|
} from "./javascriptSemanticPropertyPaths.js";
|
|
import {
|
|
evaluateSemanticExpression,
|
|
semanticValueDefinitelyDefined,
|
|
} from "./javascriptSemanticValues.js";
|
|
import { semanticSlotAtPath } from "./javascriptSemanticSlots.js";
|
|
import type { JavaScriptSemanticValue } from "./javascriptSemanticValueTypes.js";
|
|
import { semanticEffectTiming } from "./javascriptSemanticMutationInitializers.js";
|
|
|
|
/**
|
|
* Node's wrapper module inherits require. A copy cannot read that delegate
|
|
* unless an earlier own-slot write or opaque module escape may have exposed it.
|
|
*/
|
|
export const semanticCommonJsRequireOwnEnumerableAt = (
|
|
capture: t.Node,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): boolean => {
|
|
const selected = ["commonjs-module", "require"];
|
|
const mayAffect = (
|
|
effect: JavaScriptSemanticAnalysisState["ambientWriteEffects"][number],
|
|
) =>
|
|
effect.path.length <= selected.length &&
|
|
effect.path.every((key, index) =>
|
|
semanticPropertyPathKeyMatches(key, selected[index] ?? ""),
|
|
) &&
|
|
semanticEffectTiming(capture, effect.node, state) !== "after";
|
|
return (
|
|
state.ambientWriteEffects.some(mayAffect) ||
|
|
state.ambientEscapeEffects.some(mayAffect)
|
|
);
|
|
};
|
|
|
|
interface ReferenceSource {
|
|
readonly node: t.Node;
|
|
readonly projection: PropertyPath;
|
|
readonly iterationSources?: readonly ReferenceSource[];
|
|
}
|
|
|
|
/** Native array iteration preserves numeric slots only while its iterator is intact. */
|
|
export const semanticKnownArrayIteration = (
|
|
value: JavaScriptSemanticValue | undefined,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): boolean =>
|
|
!state.arrayIterationUnknown &&
|
|
value?.status === "array" &&
|
|
value.items.every((item) => semanticArrayIndex(item.name) !== null);
|
|
|
|
/** Invocation spreads use the same native-iteration proof at their source read. */
|
|
export const semanticKnownArrayIterationAt = (
|
|
node: t.Node,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): boolean =>
|
|
semanticKnownArrayIteration(
|
|
evaluateSemanticExpression(node, state, node),
|
|
state,
|
|
);
|
|
|
|
/** Numeric destructuring projections do not select slots of custom iterators. */
|
|
export const semanticReferenceIterationUnknown = (
|
|
source: { readonly iterationSources?: readonly ReferenceSource[] },
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): boolean =>
|
|
source.iterationSources?.some(
|
|
(iterable) => !selectedReferenceIterationKnown(iterable, state),
|
|
) === true;
|
|
|
|
const selectedReferenceIterationKnown = (
|
|
source: ReferenceSource,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): boolean => {
|
|
let values = [evaluateSemanticExpression(source.node, state, source.node)];
|
|
for (const key of source.projection) {
|
|
if (typeof key === "object" && key !== null) return false;
|
|
const selected = new Set<JavaScriptSemanticValue>();
|
|
for (const value of values) {
|
|
if (key === null) {
|
|
// for-of projects any position of a native array. Its finite slot
|
|
// inventory can prove every possible nested iterable without treating
|
|
// a computed object property or custom iterator as an own-index read.
|
|
if (
|
|
!semanticKnownArrayIteration(value, state) ||
|
|
value.status !== "array" ||
|
|
value.unknownItems
|
|
)
|
|
return false;
|
|
for (const slot of value.items) {
|
|
if (slot.presence !== "present") return false;
|
|
selected.add(slot.value);
|
|
}
|
|
} else {
|
|
const slot = semanticSlotAtPath(value, [String(key)]);
|
|
if (slot.presence !== "present") return false;
|
|
selected.add(slot.value);
|
|
}
|
|
}
|
|
values = [...selected];
|
|
}
|
|
return values.every((value) => semanticKnownArrayIteration(value, state));
|
|
};
|
|
|
|
/** Exclude references whose source slot or destructuring fallback is absent. */
|
|
export const semanticReferenceSourcesInactive = (
|
|
reference: {
|
|
readonly iterationSources?: readonly ReferenceSource[];
|
|
readonly requiredSources?: readonly ReferenceSource[];
|
|
readonly fallbackSources?: readonly ReferenceSource[];
|
|
},
|
|
state: JavaScriptSemanticAnalysisState,
|
|
): boolean =>
|
|
reference.requiredSources?.some(
|
|
(source) =>
|
|
!semanticReferenceIterationUnknown(source, state) &&
|
|
semanticSelectedReferenceSlot(source, state, source.node)?.presence ===
|
|
"absent",
|
|
) === true ||
|
|
reference.fallbackSources?.some((source) => {
|
|
// Selection precedes effects through the chosen reference. A later escape
|
|
// cannot make a fallback that was excluded at destructuring reachable.
|
|
const slot = semanticSelectedReferenceSlot(source, state, source.node);
|
|
return (
|
|
!semanticReferenceIterationUnknown(source, state) &&
|
|
slot?.presence === "present" &&
|
|
semanticValueDefinitelyDefined(slot.value)
|
|
);
|
|
}) === true;
|
|
|
|
/** Select a retained source slot at its capture, before later effects. */
|
|
export const semanticSelectedReferenceSlot = (
|
|
source: ReferenceSource,
|
|
state: JavaScriptSemanticAnalysisState,
|
|
capturePoint?: t.Node,
|
|
) => {
|
|
const path: string[] = [];
|
|
for (const key of source.projection) {
|
|
if (key === null || typeof key === "object") return undefined;
|
|
path.push(String(key));
|
|
}
|
|
return semanticSlotAtPath(
|
|
evaluateSemanticExpression(source.node, state, capturePoint),
|
|
path,
|
|
);
|
|
};
|