Skip to content

Commit

Permalink
feat(error): Implement core of error reporting framework
Browse files Browse the repository at this point in the history
  • Loading branch information
bdunderscore committed Dec 17, 2023
1 parent c7f43a1 commit 6cc58fb
Show file tree
Hide file tree
Showing 37 changed files with 1,084 additions and 210 deletions.
218 changes: 139 additions & 79 deletions Editor/API/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using nadena.dev.ndmf.localization;
using nadena.dev.ndmf.reporting;
using nadena.dev.ndmf.runtime;
using nadena.dev.ndmf.ui;
using nadena.dev.ndmf.util;
using UnityEditor;
using UnityEngine;
Expand All @@ -20,6 +22,24 @@

namespace nadena.dev.ndmf
{
internal sealed class ExecutionScope : IDisposable
{
private readonly ErrorReportScope _errorReportScope;
private readonly RegistryScope _registryScope;

public ExecutionScope(BuildContext ctx)
{
_errorReportScope = new ErrorReportScope(ctx._report);
_registryScope = new RegistryScope(ctx._registry);
}

public void Dispose()
{
_errorReportScope.Dispose();
_registryScope.Dispose();
}
}

/// <summary>
/// The BuildContext is passed to all plugins during the build process. It provides access to the avatar being
/// built, as well as various other context information.
Expand All @@ -30,8 +50,9 @@ public sealed partial class BuildContext
private readonly Transform _avatarRootTransform;

private Stopwatch sw = new Stopwatch();


internal readonly ObjectRegistry _registry;
internal readonly ErrorReport _report;

/// <summary>
/// The root GameObject of the avatar being built.
/// </summary>
Expand Down Expand Up @@ -75,9 +96,11 @@ public T Extension<T>() where T : IExtensionContext
return (T) value;
}

public BuildContext(GameObject obj, string assetRootPath)
public BuildContext(GameObject obj, string assetRootPath, bool isClone = true)
{
BuildEvent.Dispatch(new BuildEvent.BuildStarted(obj));
_registry = new ObjectRegistry(obj.transform);
_report = ErrorReport.Create(obj, isClone);

Debug.Log("Starting processing for avatar: " + obj.name);
sw.Start();
Expand Down Expand Up @@ -118,6 +141,20 @@ public BuildContext(GameObject obj, string assetRootPath)
}

sw.Stop();

// Register all initially-existing GameObjects and Components
using (new RegistryScope(_registry))
{
foreach (Transform xform in _avatarRootTransform.GetComponentsInChildren<Transform>(true))
{
ObjectRegistry.GetReference(xform.gameObject);

foreach (Component c in xform.gameObject.GetComponents<Component>())
{
ObjectRegistry.GetReference(c);
}
}
}
}

private static readonly Regex WindowsReservedFileNames = new Regex(
Expand Down Expand Up @@ -227,70 +264,77 @@ public void DeactivateExtensionContext<T>() where T : IExtensionContext

public void DeactivateExtensionContext(Type t)
{
if (_activeExtensions.ContainsKey(t))
using (new ExecutionScope(this))
{
var ctx = _activeExtensions[t];
Profiler.BeginSample("NDMF Deactivate: " + t);
try
{
ctx.OnDeactivate(this);
}
finally
if (_activeExtensions.ContainsKey(t))
{
Profiler.EndSample();
var ctx = _activeExtensions[t];
Profiler.BeginSample("NDMF Deactivate: " + t);
try
{
ctx.OnDeactivate(this);
}
finally
{
Profiler.EndSample();
}

_activeExtensions.Remove(t);
}
_activeExtensions.Remove(t);
}
}

internal void RunPass(ConcretePass pass)
{
sw.Start();
using (new ExecutionScope(this))
{
sw.Start();

ImmutableDictionary<Type, double> deactivationTimes = ImmutableDictionary<Type, double>.Empty;
ImmutableDictionary<Type, double> deactivationTimes = ImmutableDictionary<Type, double>.Empty;

foreach (var ty in pass.DeactivatePlugins)
{
Stopwatch sw2 = new Stopwatch();
sw2.Start();
DeactivateExtensionContext(ty);
deactivationTimes = deactivationTimes.Add(ty, sw2.Elapsed.TotalMilliseconds);
}
foreach (var ty in pass.DeactivatePlugins)
{
Stopwatch sw2 = new Stopwatch();
sw2.Start();
DeactivateExtensionContext(ty);
deactivationTimes = deactivationTimes.Add(ty, sw2.Elapsed.TotalMilliseconds);
}

ImmutableDictionary<Type, double> activationTimes = ImmutableDictionary<Type, double>.Empty;
foreach (var ty in pass.ActivatePlugins)
{
Stopwatch sw2 = new Stopwatch();
sw2.Start();
ActivateExtensionContext(ty);
activationTimes = activationTimes.Add(ty, sw2.Elapsed.TotalMilliseconds);
}
ImmutableDictionary<Type, double> activationTimes = ImmutableDictionary<Type, double>.Empty;
foreach (var ty in pass.ActivatePlugins)
{
Stopwatch sw2 = new Stopwatch();
sw2.Start();
ActivateExtensionContext(ty);
activationTimes = activationTimes.Add(ty, sw2.Elapsed.TotalMilliseconds);
}

Stopwatch passTimer = new Stopwatch();
passTimer.Start();
Profiler.BeginSample(pass.Description);
try
{
pass.Execute(this);
}
catch (Exception e)
{
pass.Plugin.OnUnhandledException(e);
}
finally
{
Profiler.EndSample();
passTimer.Stop();
}

BuildEvent.Dispatch(new BuildEvent.PassExecuted(
pass.InstantiatedPass.QualifiedName,
passTimer.Elapsed.TotalMilliseconds,
activationTimes,
deactivationTimes
));
Stopwatch passTimer = new Stopwatch();
passTimer.Start();
Profiler.BeginSample(pass.Description);
try
{
pass.Execute(this);
}
catch (Exception e)
{
pass.Plugin.OnUnhandledException(e);
}
finally
{
Profiler.EndSample();
passTimer.Stop();
}

sw.Stop();
BuildEvent.Dispatch(new BuildEvent.PassExecuted(
pass.InstantiatedPass.QualifiedName,
passTimer.Elapsed.TotalMilliseconds,
activationTimes,
deactivationTimes
));

sw.Stop();
}
}

public T ActivateExtensionContext<T>() where T : IExtensionContext
Expand All @@ -300,47 +344,63 @@ public T ActivateExtensionContext<T>() where T : IExtensionContext

public IExtensionContext ActivateExtensionContext(Type ty)
{
if (!_extensions.TryGetValue(ty, out var ctx))
using (new ExecutionScope(this))
{
ctx = (IExtensionContext) ty.GetConstructor(Type.EmptyTypes).Invoke(Array.Empty<object>());
}

if (!_activeExtensions.ContainsKey(ty))
{
Profiler.BeginSample("NDMF Activate: " + ty);
try
if (!_extensions.TryGetValue(ty, out var ctx))
{
ctx.OnActivate(this);
ctx = (IExtensionContext)ty.GetConstructor(Type.EmptyTypes).Invoke(Array.Empty<object>());
}
finally

if (!_activeExtensions.ContainsKey(ty))
{
Profiler.EndSample();
Profiler.BeginSample("NDMF Activate: " + ty);
try
{
ctx.OnActivate(this);
}
finally
{
Profiler.EndSample();
}

_activeExtensions.Add(ty, ctx);
}

_activeExtensions.Add(ty, ctx);
return _activeExtensions[ty];
}

return _activeExtensions[ty];
}

internal void Finish()
{
sw.Start();
foreach (var kvp in _activeExtensions.ToList())
using (new ExecutionScope(this))
{
kvp.Value.OnDeactivate(this);
if (kvp.Value is IDisposable d)
sw.Start();
foreach (var kvp in _activeExtensions.ToList())
{
d.Dispose();
}
kvp.Value.OnDeactivate(this);
if (kvp.Value is IDisposable d)
{
d.Dispose();
}

_activeExtensions.Remove(kvp.Key);
}
_activeExtensions.Remove(kvp.Key);
}

Serialize();
sw.Stop();
Serialize();
sw.Stop();

BuildEvent.Dispatch(new BuildEvent.BuildEnded(sw.ElapsedMilliseconds, true));
BuildEvent.Dispatch(new BuildEvent.BuildEnded(sw.ElapsedMilliseconds, true));

ErrorReport.ReportError(NDMFLocales.L, ErrorCategory.Information, "ndmf.test1");
ErrorReport.ReportError(NDMFLocales.L, ErrorCategory.NonFatal, "ndmf.test2");
ErrorReport.ReportError(NDMFLocales.L, ErrorCategory.Error, "ndmf.test3");
ErrorReport.ReportError(NDMFLocales.L, ErrorCategory.InternalError, "ndmf.test4");

if (!Application.isBatchMode && _report.Errors.Count > 0)
{
ErrorReportWindow.ShowReport(_report);
}
}
}
}
}
47 changes: 47 additions & 0 deletions Editor/API/ObjectReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#region

using System;
using System.Runtime.CompilerServices;
using Object = UnityEngine.Object;

#endregion

namespace nadena.dev.ndmf
{
/// <summary>
/// The ObjectReference class gives you a way to reference an object that may have been moved or destroyed since
/// its initial creation.
/// </summary>
public sealed class ObjectReference
{
private readonly Object _obj;
private readonly string _path;
private readonly Type _type;

internal ObjectReference(Object obj, string path)
{
_obj = obj;
_path = path;
_type = obj.GetType();
}

public Object Object => _obj;
public string Path => _path;
public Type Type => _type;

private bool Equals(ObjectReference other)
{
return ReferenceEquals(_obj, other._obj);
}

public override bool Equals(object obj)
{
return ReferenceEquals(this, obj) || obj is ObjectReference other && Equals(other);
}

public override int GetHashCode()
{
return RuntimeHelpers.GetHashCode(_obj);
}
}
}
3 changes: 3 additions & 0 deletions Editor/API/ObjectReference.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6cc58fb

Please sign in to comment.