diff --git a/src/domain/javascript/javascriptSemanticValues.ts b/src/domain/javascript/javascriptSemanticValues.ts index 87f6ad540e..d001f8a867 100644 --- a/src/domain/javascript/javascriptSemanticValues.ts +++ b/src/domain/javascript/javascriptSemanticValues.ts @@ -53,7 +53,7 @@ import { interface EvaluationContext { readonly state: JavaScriptSemanticAnalysisState; - readonly bindings: ReadonlySet; + readonly bindings: Set; readonly expressionDepth: number; readonly capturePoint?: t.Node; readonly primitiveBindingValues: Map; @@ -196,20 +196,26 @@ const evaluateBinding = ( const initializer = binding.initializers[0]; if (initializer === undefined) return { status: "unknown", reason: "Missing binding initializer." }; - const nested = nestedContext(context, binding.bindingId); - let value = projectValue( - evaluateExpression(initializer.node, nested), - initializer.projection, - ); - if (!isPrimitive(value)) { - const captureContext = primitiveCaptureContext(initializer.node, nested); - if (captureContext !== undefined) { - const captured = projectValue( - evaluateExpression(initializer.node, captureContext), - initializer.projection, - ); - if (isPrimitive(captured)) value = captured; + context.bindings.add(binding.bindingId); + let value: JavaScriptSemanticValue; + try { + const nested = nestedContext(context); + value = projectValue( + evaluateExpression(initializer.node, nested), + initializer.projection, + ); + if (!isPrimitive(value)) { + const captureContext = primitiveCaptureContext(initializer.node, nested); + if (captureContext !== undefined) { + const captured = projectValue( + evaluateExpression(initializer.node, captureContext), + initializer.projection, + ); + if (isPrimitive(captured)) value = captured; + } } + } finally { + context.bindings.delete(binding.bindingId); } let projected = invalidateSemanticMutationPaths(value, binding.mutatedPaths); for (const escape of binding.escapedPaths) { @@ -586,10 +592,16 @@ const provenanceForBinding = ( "unknown", "Dynamic provenance projection.", ); - const resolved = provenanceForExpression( - initializer.node, - nestedContext(context, binding.bindingId), - ); + context.bindings.add(binding.bindingId); + let resolved: JavaScriptBindingProvenance; + try { + resolved = provenanceForExpression( + initializer.node, + nestedContext(context), + ); + } finally { + context.bindings.delete(binding.bindingId); + } if (resolved.status !== "module" || initializer.projection.length === 0) return resolved; return semanticOriginsProvenance( @@ -718,20 +730,14 @@ const mergeValues = ( : { status: "ambiguous", reason: "Branches have incompatible values." }; }; -const nestedContext = ( - context: EvaluationContext, - bindingId?: string, -): EvaluationContext => ({ +const nestedContext = (context: EvaluationContext): EvaluationContext => ({ state: context.state, primitiveBindingValues: context.primitiveBindingValues, ...(context.capturePoint === undefined ? {} : { capturePoint: context.capturePoint }), expressionDepth: context.expressionDepth + 1, - bindings: - bindingId === undefined - ? context.bindings - : new Set([...context.bindings, bindingId]), + bindings: context.bindings, }); const isPrimitive = (value: JavaScriptSemanticValue): boolean =>