diff --git a/src/main/compiler/CompilerScope.ts b/src/main/compiler/CompilerScope.ts index 1cc829a..c2ac97e 100644 --- a/src/main/compiler/CompilerScope.ts +++ b/src/main/compiler/CompilerScope.ts @@ -58,8 +58,9 @@ export class CompilerScope { } private emitNode(node: NodeView) { - this.emitComment(`${node.ref} ${node.nodeUid}`); - const sym = this.symbols.getNodeSym(node.nodeUid); + const nodeUid = node.nodeUid; + this.emitComment(`${node.ref} ${nodeUid}`); + const sym = this.symbols.getNodeSym(nodeUid); this.code.block(`${this.asyncSym(node)}function ${sym}(params, ctx) {`, `}`, () => { if (this.isNodeCached(node)) { this.code.line(`let $c = ctx.cache.get("${node.nodeUid}");`); @@ -73,17 +74,19 @@ export class CompilerScope { this.emitNodeBodyIntrospect(node); } }); + // Emit friendlier function names for stack trace introspection + this.code.line(`Object.defineProperty(${sym}, 'name', { value: ${JSON.stringify(nodeUid)} });`); } private emitNodeBodyIntrospect(node: NodeView) { const resSym = '$r'; + const nodeUid = node.nodeUid; this.code.line(`let ${resSym};`); - this.code.line(`ctx.nodeUid = ${JSON.stringify(node.nodeUid)}`); if (this.options.introspect) { - const nodeUid = node.nodeUid; this.code.block('try {', '}', () => { if (!node.supportsSubgraph()) { - // For subgraphs, pending check is done prior to calling the subgraph the first time. + // For subgraphs, pending check is done prior to calling the subgraph the first time + // (see getSubgraphExpr which calls checkPendingNode instead of doing it here) this.code.line(`ctx.checkPendingNode(${JSON.stringify(nodeUid)});`); } this.code.line(`ctx.nodeEvaluated.emit({` + diff --git a/src/main/runtime/GraphEvalContext.ts b/src/main/runtime/GraphEvalContext.ts index 158d714..0d44cff 100644 --- a/src/main/runtime/GraphEvalContext.ts +++ b/src/main/runtime/GraphEvalContext.ts @@ -17,17 +17,20 @@ export class GraphEvalContext implements t.GraphEvalContext { readonly lib = runtimeLib; - nodeUid = ''; - pendingNodeUids: Set; + // Introspection events delegate to root context. nodeEvaluated: Event; scopeCaptured: Event; + // Pending nodes are stored on root context and inherited by child contexts. + pendingNodeUids: Set; // Each context maintains its own cache. Subscopes have separate caches // and do not delegate to parent contexts. cache = new Map(); // Locals are stored per-context. Lookups delegate up the hierarchy. locals = new Map(); - scopeData: any = undefined; + // Scope data is maintained by each context separately. + // Compiler populates it via setScopeData when emitting nodes with subgraphs. + private scopeData: any = undefined; constructor( readonly parent: GraphEvalContext | null = null, diff --git a/src/main/types/ctx.ts b/src/main/types/ctx.ts index f9161ac..741fefe 100644 --- a/src/main/types/ctx.ts +++ b/src/main/types/ctx.ts @@ -3,14 +3,9 @@ import { Event } from 'nanoevent'; import { RuntimeLib } from './runtime-lib.js'; import { SchemaSpec } from './schema.js'; -export interface Deferred { - resolve: () => unknown; -} - export interface GraphEvalContext { readonly lib: RuntimeLib; - nodeUid: string; cache: Map; locals: Map; nodeEvaluated: Event; @@ -40,6 +35,10 @@ export interface GraphEvalContext { resolveDeferred(value: unknown): unknown; } +export interface Deferred { + resolve: () => unknown; +} + export interface NodeResult { nodeUid: string; result?: any;