using System; using System.Collections.Generic; using UnityEngine; namespace Unity.VisualScripting { public static class GraphsExceptionUtility { // Note: Checking hasDebugData here instead of enableDebug, // because we always want exceptions to register, even if // background debug is disabled. private const string handledKey = "Bolt.Core.Handled"; public static Exception GetException(this IGraphElementWithDebugData element, GraphPointer pointer) { if (!pointer.hasDebugData) { return null; } var debugData = pointer.GetElementDebugData(element); return debugData.runtimeException; } public static void SetException(this IGraphElementWithDebugData element, GraphPointer pointer, Exception ex) { if (!pointer.hasDebugData) { return; } var debugData = pointer.GetElementDebugData(element); debugData.runtimeException = ex; } public static void HandleException(this IGraphElementWithDebugData element, GraphPointer pointer, Exception ex) { Ensure.That(nameof(ex)).IsNotNull(ex); if (pointer == null) { Debug.LogError("Caught exception with null graph pointer (flow was likely disposed):\n" + ex); return; } var reference = pointer.AsReference(); if (!ex.HandledIn(reference)) { element.SetException(pointer, ex); } while (reference.isChild) { var parentElement = reference.parentElement; reference = reference.ParentReference(true); if (parentElement is IGraphElementWithDebugData debuggableParentElement) { if (!ex.HandledIn(reference)) { debuggableParentElement.SetException(reference, ex); } } } } private static bool HandledIn(this Exception ex, GraphReference reference) { Ensure.That(nameof(ex)).IsNotNull(ex); if (!ex.Data.Contains(handledKey)) { ex.Data.Add(handledKey, new HashSet()); } var handled = (HashSet)ex.Data[handledKey]; if (handled.Contains(reference)) { return true; } else { handled.Add(reference); return false; } } } }