Files
rea/src/domain/javascript/javascriptSemanticCallableStack.ts
T
ShuaixinHuang a2134b3a74 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
2026-11-05 16:52:19 +08:00

85 lines
3.0 KiB
TypeScript

import * as t from "@babel/types";
import { semanticMethodDefinitionExpression } from "./javascriptSemanticEvaluationOrder.js";
import { semanticCallableIdForNode } from "./javascriptSemanticProjection.js";
/**
* Innermost-last stack of admitted callables during one enter/exit traversal.
*
* Single owner for semantic callable ownership. A computed object/class method
* key is evaluated before the method's parameter and body environments, so the
* method is set aside while its key is traversed and the enclosing callable
* owns every fact recovered from the key. Collectors share this tracker so
* calls, captures, effects, promises, and object operations agree.
*/
export interface SemanticCallableStack<Callable> {
/** Admitted callables, outermost first; the innermost owner is last. */
readonly callables: readonly Callable[];
readonly enter: (node: t.Node, parent: t.Node | null) => void;
readonly exit: (node: t.Node) => void;
}
interface CallableFrame<Callable> {
readonly callableId: string;
readonly callable: Callable;
}
/**
* Track callables admitted by `admit`, which maps a callable ID to the value
* kept on the stack or `undefined` to ignore it. A caller-owned `callables`
* array is updated in place, so shared contexts can keep reading it.
*/
export const createSemanticCallableStack = <Callable>(
admit: (callableId: string) => Callable | undefined,
callables: Callable[] = [],
): SemanticCallableStack<Callable> => {
const frames: CallableFrame<Callable>[] = [];
const suspendedMethods = new Map<t.Node, CallableFrame<Callable>>();
callables.length = 0;
const push = (frame: CallableFrame<Callable>): void => {
frames.push(frame);
callables.push(frame.callable);
};
const pop = (): CallableFrame<Callable> | undefined => {
callables.pop();
return frames.pop();
};
return {
callables,
enter: (node, parent) => {
if (
parent !== null &&
semanticMethodDefinitionExpression(parent, node) &&
frames.at(-1)?.callableId === semanticCallableIdForNode(parent)
) {
const method = pop();
if (method !== undefined) suspendedMethods.set(node, method);
}
const callableId = semanticCallableIdForNode(node);
if (callableId === null) return;
const callable = admit(callableId);
if (callable !== undefined) push({ callableId, callable });
},
exit: (node) => {
const callableId = semanticCallableIdForNode(node);
if (callableId !== null && frames.at(-1)?.callableId === callableId)
pop();
const method = suspendedMethods.get(node);
if (method !== undefined) {
suspendedMethods.delete(node);
push(method);
}
},
};
};
/** Track admitted callable IDs, keeping each ID as its stack value. */
export const createSemanticCallableIdStack = (
admitted: ReadonlySet<string>,
callables: string[] = [],
): SemanticCallableStack<string> =>
createSemanticCallableStack(
(callableId) => (admitted.has(callableId) ? callableId : undefined),
callables,
);