import type { TsNode } from "../../ir/tsIR.js"; import type { AgencyNode } from "../../types.js"; import type { ScopeManager } from "./scopeManager.js"; /** Everything a scope's compilation needs from its (possible) finalize * block. Produced by FinalizeCodegen.compileScope for every function and * block scope, whether or not one was declared. */ export type CompiledScopeFinalize = { /** The scope's compiled statements, with the finalize stripped. */ bodyCode: TsNode[]; /** The `const __finalize = ...` closure declaration, or undefined when * the scope declared no finalize. */ decl: TsNode | undefined; /** `decl` rendered for the setup templates; "" when there is no * finalize. Pre-rendered strings (not mustache sections) keep the * no-finalize output byte-identical to the pre-finalize compiler. */ declText: string; /** The catch-site abort return statement. With a finalize it runs the * closure first (`.withFinalize`); without one it returns the plain * AbortedResult. */ abortReturn: string; }; /** * Compiles `finalize { ... }` blocks: the statement-stream split, the * `__finalize` closure, and the abort stop sites that run it. Extracted * from TypeScriptBuilder so every finalize emission lives in one place. * * The model: a finalize is a declaration, not a statement. compileScope * strips it from the scope's statement stream and compiles it into a * `const __finalize = ...` closure. Three stop sites call the closure * through `AbortedResult.withFinalize`: * * 1. the frame catch — `abortReturn`, rendered into the catch template; * 2. the post-call aborted guard — `stopScope`, emitted by the * builder's assignmentAbortedGuard; * 3. the return-position temp check — `interceptedReturn`. * * The closure runs on the container's own frame: locals live there, so * the finalize body reads them with zero passing. Runner step counters * are frame-keyed, so the closure's statements compile at STEP_BASE, a * disjoint id range the main body can never reach. Small ids would * collide with counters the main body already advanced past, silently * skipping the finalize's steps. Advancing the dying frame's counter past * the base is inert, because an aborted frame never resumes. */ export declare class FinalizeCodegen { private readonly scopes; private readonly moduleId; /** TypeScriptBuilder.processBodyAsParts — compiles statements with * step ids starting at `stepBase`. */ private readonly compileBody; /** Step-id base for compiled finalize bodies — far above anything a * main body can reach. See the class doc. */ private static readonly STEP_BASE; /** One entry per function/block scope being compiled, true when that * scope declared a finalize. Pushed/popped by compileScope, in * lockstep with the builder's scope stack. */ private presence; constructor(scopes: ScopeManager, moduleId: string, /** TypeScriptBuilder.processBodyAsParts — compiles statements with * step ids starting at `stepBase`. */ compileBody: (body: AgencyNode[], stepBase: number) => TsNode[]); /** * Compile a scope's body, separating out its finalize block. Call with * the scope already pushed on the ScopeManager; `compileBodyRest` runs * between the presence push/pop so the finalize-aware return and * post-call lowerings see the right flag while the body compiles. */ compileScope(args: { body: AgencyNode[]; scopeName: string; /** The catch template's error binding: `__error` in function catches, * `__blockError` in block catches. */ errorVar: string; /** Compiles the scope's own statements (the stream minus the * finalize). Owns the step base: functions start at 1 (id 0 is the * onFunctionStart hook), blocks at 0. */ compileBodyRest: (rest: AgencyNode[]) => TsNode[]; }): CompiledScopeFinalize; /** True when the innermost function/block scope being compiled declared * a finalize. Drives the finalize-aware post-call guard and the * return-position lowering. */ isActive(): boolean; /** The statements every in-body stop site emits: halt the scope with * the aborted result after running the finalize over it, then leave * the step body. carryThrough applies the salvage rule (drop the * callee's partial, carry this scope's own draft) before the finalize * runs; the finalize's value then outranks the draft. */ stopScope(abortedVar: string): TsNode[]; /** Lower `return ` to a checked temp. A normal value returns * exactly as before. An interrupt result passes through the temp as * the return value, for the caller's post-call check to handle. An * aborted result stops here and runs the finalize — pass-through would * silently skip it. Nothing binds into a local: the value was headed * for the return. */ interceptedReturn(valueNode: TsNode, emit: (value: TsNode) => TsNode): TsNode; /** Compile the finalize body into the `__finalize` closure. The fresh * Runner shares the container's frame and gets a "#finalize" scope * name; the disjoint step base (see the class doc) is what actually * keeps its counters from colliding with the main body's. */ private closure; private abortReturn; /** The frame variable generated scopes bind their own frame to. */ private frameVar; }