/** * CallableInvocationStrategy * * Encapsulates the three-phase callable invocation protocol: * Phase 1 – validate: guard non-callable targets before any evaluation * Phase 2 – bind: delegate to ArgumentsBinder for spread-aware binding * Phase 3 – invoke: own the call-stack frame try/catch/finally * * Cached on `EvalState.invocationStrategy` and lazily constructed on first * use, so a given evaluation context allocates at most one strategy instance * rather than reallocating it on every call-site invocation. * * Error codes: * RILL-R001 – non-callable target, argument binding failure * Original code preserved – callable body RillError, re-thrown after * frame is appended to callStack * Wrapped as RuntimeError – callable body non-Rill error * * @internal */ import type { SourceLocation, ExpressionNode, SpreadArgNode } from '../../../../types.js'; import type { RillCallable } from '../../callable.js'; import type { RuntimeContext } from '../../types/runtime.js'; import type { RillValue } from '../../types/structures.js'; import type { ArgumentsBinder, BoundArguments } from './arguments-binder.js'; /** * Injected executor: performs the actual callable dispatch. * Provided by the evaluator so the strategy stays decoupled from * invokeScriptCallable / invokeFnCallable internals. */ export type InvocationCaller = (callable: RillCallable, args: RillValue[], location: SourceLocation | undefined, functionName?: string) => Promise; /** * Four-phase callable invocation strategy. * * Construct once per evaluation context and reuse across calls. * Each public method covers exactly one phase; callers sequence them. */ export declare class CallableInvocationStrategy { private readonly getCtx; private readonly binder; private readonly caller; constructor(getCtx: () => RuntimeContext, binder: ArgumentsBinder, caller: InvocationCaller); /** * Guard non-callable targets before any argument evaluation. * * throws RILL-R001 when `target` is not a callable, * embedding `path` and `location` in the error context. */ validate(target: RillCallable, path: string, location: SourceLocation): void; /** * Evaluate and bind arguments to the callable's parameter list. * * Delegates to `ArgumentsBinder` as the single spread-detection owner. * binding failures surface from ArgumentsBinder unchanged. */ bind(callable: RillCallable, args: (ExpressionNode | SpreadArgNode)[], pipeInput: RillValue | undefined, evaluate: (node: ExpressionNode) => Promise, location: SourceLocation): Promise; /** * Push a call-stack frame, execute the callable, then pop the frame. * * Owns the try/catch/finally pattern from closures.ts:478-523. * Callers must NOT re-catch for frame enrichment — this method is * the single frame-enrichment site. * * `finally` guarantees frame pop on both success and failure. * each call pushes/pops its own frame, preventing cross-call * contamination in re-entrant (nested) invocations. * * body RillError — callStack snapshot appended, error re-thrown. * body non-Rill error — wrapped as RuntimeError (RILL-R001). */ invoke(callable: RillCallable, args: BoundArguments, location: SourceLocation, functionName?: string): Promise; }