[Add] VContainer
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 08f5f24dc5b90493d9e57618faa4868f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c9d3500c8eb84e07acfb1cb574d9f9e2
|
||||||
|
timeCreated: 1632057810
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VContainer.Editor.Diagnostics
|
||||||
|
{
|
||||||
|
// reflection call of UnityEditor.SplitterGUILayout
|
||||||
|
static class SplitterGUILayout
|
||||||
|
{
|
||||||
|
static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
|
||||||
|
|
||||||
|
static readonly Lazy<Type> SplitterStateType = new Lazy<Type>(() =>
|
||||||
|
{
|
||||||
|
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterState");
|
||||||
|
return type;
|
||||||
|
});
|
||||||
|
|
||||||
|
static readonly Lazy<ConstructorInfo> SplitterStateCtor = new Lazy<ConstructorInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = SplitterStateType.Value;
|
||||||
|
return type.GetConstructor(flags, null, new Type[] { typeof(float[]), typeof(int[]), typeof(int[]) }, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
static readonly Lazy<Type> SplitterGUILayoutType = new Lazy<Type>(() =>
|
||||||
|
{
|
||||||
|
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterGUILayout");
|
||||||
|
return type;
|
||||||
|
});
|
||||||
|
|
||||||
|
static readonly Lazy<MethodInfo> BeginVerticalSplitInfo = new Lazy<MethodInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = SplitterGUILayoutType.Value;
|
||||||
|
return type.GetMethod("BeginVerticalSplit", flags, null, new Type[] { SplitterStateType.Value, typeof(GUILayoutOption[]) }, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
static readonly Lazy<MethodInfo> EndVerticalSplitInfo = new Lazy<MethodInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = SplitterGUILayoutType.Value;
|
||||||
|
return type.GetMethod("EndVerticalSplit", flags, null, Type.EmptyTypes, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
static readonly Lazy<MethodInfo> BeginHorizontalSplitInfo = new Lazy<MethodInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = SplitterGUILayoutType.Value;
|
||||||
|
return type.GetMethod("BeginHorizontalSplit", flags, null, new Type[] { SplitterStateType.Value, typeof(GUILayoutOption[]) }, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
static readonly Lazy<MethodInfo> EndHorizontalSplitInfo = new Lazy<MethodInfo>(() =>
|
||||||
|
{
|
||||||
|
var type = SplitterGUILayoutType.Value;
|
||||||
|
return type.GetMethod("EndHorizontalSplit", flags, null, Type.EmptyTypes, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
public static object CreateSplitterState(float[] relativeSizes, int[] minSizes, int[] maxSizes)
|
||||||
|
{
|
||||||
|
return SplitterStateCtor.Value.Invoke(new object[] { relativeSizes, minSizes, maxSizes });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BeginVerticalSplit(object splitterState, params GUILayoutOption[] options)
|
||||||
|
{
|
||||||
|
BeginVerticalSplitInfo.Value.Invoke(null, new object[] { splitterState, options });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EndVerticalSplit()
|
||||||
|
{
|
||||||
|
EndVerticalSplitInfo.Value.Invoke(null, Array.Empty<object>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BeginHorizontalSplit(object splitterState, params GUILayoutOption[] options)
|
||||||
|
{
|
||||||
|
BeginHorizontalSplitInfo.Value.Invoke(null, new object[] { splitterState, options });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EndHorizontalSplit()
|
||||||
|
{
|
||||||
|
EndHorizontalSplitInfo.Value.Invoke(null, Array.Empty<object>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af963129cd84433ba74949eeb1c00542
|
||||||
|
timeCreated: 1633102883
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace VContainer.Editor.Diagnostics
|
||||||
|
{
|
||||||
|
static class TypeNameHelper
|
||||||
|
{
|
||||||
|
static readonly IReadOnlyDictionary<Type, string> TypeAlias = new Dictionary<Type, string>
|
||||||
|
{
|
||||||
|
{ typeof(bool), "bool" },
|
||||||
|
{ typeof(byte), "byte" },
|
||||||
|
{ typeof(char), "char" },
|
||||||
|
{ typeof(decimal), "decimal" },
|
||||||
|
{ typeof(double), "double" },
|
||||||
|
{ typeof(float), "float" },
|
||||||
|
{ typeof(int), "int" },
|
||||||
|
{ typeof(long), "long" },
|
||||||
|
{ typeof(object), "object" },
|
||||||
|
{ typeof(sbyte), "sbyte" },
|
||||||
|
{ typeof(short), "short" },
|
||||||
|
{ typeof(string), "string" },
|
||||||
|
{ typeof(uint), "uint" },
|
||||||
|
{ typeof(ulong), "ulong" },
|
||||||
|
{ typeof(void), "void" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetTypeAlias(Type type)
|
||||||
|
{
|
||||||
|
if (TypeAlias.TryGetValue(type, out var alias))
|
||||||
|
{
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
var typeParameters = type.GetGenericArguments().Select(GetTypeAlias);
|
||||||
|
var typeNameBase = type.Name.Split('`')[0];
|
||||||
|
return $"{typeNameBase}<{string.Join(",", typeParameters)}>";
|
||||||
|
}
|
||||||
|
return type.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 639563c4569244d8a54a150c42c8de33
|
||||||
|
timeCreated: 1633621836
|
||||||
+323
@@ -0,0 +1,323 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using VContainer.Diagnostics;
|
||||||
|
using VContainer.Unity;
|
||||||
|
|
||||||
|
namespace VContainer.Editor.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class DiagnosticsInfoTreeViewItem : TreeViewItem
|
||||||
|
{
|
||||||
|
public string ScopeName { get; set; }
|
||||||
|
public DiagnosticsInfo DiagnosticsInfo { get; }
|
||||||
|
|
||||||
|
public RegistrationBuilder RegistrationBuilder => DiagnosticsInfo.RegisterInfo.RegistrationBuilder;
|
||||||
|
public Registration Registration => DiagnosticsInfo.ResolveInfo.Registration;
|
||||||
|
public int? RefCount => DiagnosticsInfo.ResolveInfo.RefCount;
|
||||||
|
public long ResolveTime => DiagnosticsInfo.ResolveInfo.ResolveTime;
|
||||||
|
|
||||||
|
public string TypeSummary => TypeNameHelper.GetTypeAlias(Registration.ImplementationType);
|
||||||
|
|
||||||
|
public string ContractTypesSummary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Registration.InterfaceTypes != null)
|
||||||
|
{
|
||||||
|
var values = Registration.InterfaceTypes.Select(TypeNameHelper.GetTypeAlias);
|
||||||
|
return string.Join(", ", values);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RegisterSummary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (RegistrationBuilder == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var type = RegistrationBuilder.GetType();
|
||||||
|
if (type == typeof(RegistrationBuilder))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeName = type.Name;
|
||||||
|
var suffixIndex = typeName.IndexOf("Builder");
|
||||||
|
if (suffixIndex > 0)
|
||||||
|
{
|
||||||
|
typeName = typeName.Substring(0, suffixIndex);
|
||||||
|
}
|
||||||
|
suffixIndex = typeName.IndexOf("Registration");
|
||||||
|
if (suffixIndex > 0)
|
||||||
|
{
|
||||||
|
typeName = typeName.Substring(0, suffixIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeName.StartsWith("Instance") && TypeSummary.StartsWith("Func<"))
|
||||||
|
{
|
||||||
|
return "FuncFactory";
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticsInfoTreeViewItem(DiagnosticsInfo info)
|
||||||
|
{
|
||||||
|
ScopeName = info.ScopeName;
|
||||||
|
DiagnosticsInfo = info;
|
||||||
|
displayName = TypeSummary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class VContainerDiagnosticsInfoTreeView : TreeView
|
||||||
|
{
|
||||||
|
static readonly MultiColumnHeaderState.Column[] Columns =
|
||||||
|
{
|
||||||
|
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Type") },
|
||||||
|
new MultiColumnHeaderState.Column { headerContent = new GUIContent("ContractTypes"), canSort = false },
|
||||||
|
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Lifetime"), width = 15f },
|
||||||
|
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Register"), width = 15f },
|
||||||
|
new MultiColumnHeaderState.Column { headerContent = new GUIContent("RefCount"), width = 5f },
|
||||||
|
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Scope"), width = 20f },
|
||||||
|
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Time"), width = 20f },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int idSeed;
|
||||||
|
static int NextId() => ++idSeed;
|
||||||
|
|
||||||
|
const string SessionStateKeySortedColumnIndex = "VContainer.Editor.DiagnosticsInfoTreeView:sortedColumnIndex";
|
||||||
|
|
||||||
|
public bool Flatten
|
||||||
|
{
|
||||||
|
get => flatten;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
flatten = value;
|
||||||
|
multiColumnHeader.ResizeToFit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool flatten;
|
||||||
|
|
||||||
|
public VContainerDiagnosticsInfoTreeView()
|
||||||
|
: this(new TreeViewState(), new MultiColumnHeader(new MultiColumnHeaderState(Columns)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VContainerDiagnosticsInfoTreeView(TreeViewState state, MultiColumnHeader header)
|
||||||
|
: base(state, header)
|
||||||
|
{
|
||||||
|
rowHeight = 20;
|
||||||
|
showAlternatingRowBackgrounds = true;
|
||||||
|
showBorder = true;
|
||||||
|
header.sortingChanged += OnSortedChanged;
|
||||||
|
|
||||||
|
header.ResizeToFit();
|
||||||
|
Reload();
|
||||||
|
|
||||||
|
header.sortedColumnIndex = Math.Min(
|
||||||
|
header.state.columns.Length - 1,
|
||||||
|
SessionState.GetInt(SessionStateKeySortedColumnIndex, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticsInfoTreeViewItem GetSelectedItem()
|
||||||
|
{
|
||||||
|
if (state.selectedIDs.Count <= 0) return null;
|
||||||
|
|
||||||
|
var selectedId = state.selectedIDs[0];
|
||||||
|
return GetRows().FirstOrDefault(x => x.id == selectedId) as DiagnosticsInfoTreeViewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadAndSort()
|
||||||
|
{
|
||||||
|
var currentSelected = state.selectedIDs;
|
||||||
|
Reload();
|
||||||
|
OnSortedChanged(multiColumnHeader);
|
||||||
|
state.selectedIDs = currentSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnSortedChanged(MultiColumnHeader multiColumnHeader)
|
||||||
|
{
|
||||||
|
var columnIndex = multiColumnHeader.sortedColumnIndex;
|
||||||
|
if (columnIndex < 0) return;
|
||||||
|
|
||||||
|
SessionState.SetInt(SessionStateKeySortedColumnIndex, columnIndex);
|
||||||
|
var ascending = multiColumnHeader.IsSortedAscending(columnIndex);
|
||||||
|
|
||||||
|
if (Flatten)
|
||||||
|
{
|
||||||
|
var items = rootItem.children.Cast<DiagnosticsInfoTreeViewItem>();
|
||||||
|
rootItem.children = new List<TreeViewItem>(Sort(items, columnIndex, ascending));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var sectionHeaderItem in rootItem.children)
|
||||||
|
{
|
||||||
|
var items = sectionHeaderItem.children.Cast<DiagnosticsInfoTreeViewItem>();
|
||||||
|
sectionHeaderItem.children = new List<TreeViewItem>(Sort(items, columnIndex, ascending));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuildRows(rootItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TreeViewItem BuildRoot()
|
||||||
|
{
|
||||||
|
var root = new TreeViewItem { depth = -1 };
|
||||||
|
var children = new List<TreeViewItem>();
|
||||||
|
|
||||||
|
if (VContainerSettings.DiagnosticsEnabled)
|
||||||
|
{
|
||||||
|
if (Flatten)
|
||||||
|
{
|
||||||
|
var infos = DiagnositcsContext.GetDiagnosticsInfos();
|
||||||
|
foreach (var info in infos)
|
||||||
|
{
|
||||||
|
children.Add(new DiagnosticsInfoTreeViewItem(info)
|
||||||
|
{
|
||||||
|
id = NextId(),
|
||||||
|
depth = 0,
|
||||||
|
ScopeName = info.ScopeName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var grouped = DiagnositcsContext.GetGroupedDiagnosticsInfos();
|
||||||
|
foreach (var scope in grouped)
|
||||||
|
{
|
||||||
|
var sectionHeaderItem = new TreeViewItem(NextId(), 0, scope.Key);
|
||||||
|
children.Add(sectionHeaderItem);
|
||||||
|
SetExpanded(sectionHeaderItem.id, true);
|
||||||
|
|
||||||
|
foreach (var info in scope)
|
||||||
|
{
|
||||||
|
AddDependencyItemsRecursive(info, sectionHeaderItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.children = children;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanMultiSelect(TreeViewItem item) => false;
|
||||||
|
|
||||||
|
protected override void RowGUI(RowGUIArgs args)
|
||||||
|
{
|
||||||
|
var item = args.item as DiagnosticsInfoTreeViewItem;
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
var cellRect = args.GetCellRect(0);
|
||||||
|
GUI.BeginGroup(cellRect);
|
||||||
|
{
|
||||||
|
args.rowRect = new Rect(0, 0, cellRect.width, cellRect.height);
|
||||||
|
base.RowGUI(args);
|
||||||
|
}
|
||||||
|
GUI.EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var visibleColumnIndex = 0; visibleColumnIndex < args.GetNumVisibleColumns(); visibleColumnIndex++)
|
||||||
|
{
|
||||||
|
var cellRect = args.GetCellRect(visibleColumnIndex);
|
||||||
|
// CenterRectUsingSingleLineHeight(ref cellRect);
|
||||||
|
var columnIndex = args.GetColumn(visibleColumnIndex);
|
||||||
|
|
||||||
|
var labelStyle = args.selected ? EditorStyles.whiteLabel : EditorStyles.label;
|
||||||
|
labelStyle.alignment = TextAnchor.MiddleLeft;
|
||||||
|
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
GUI.BeginGroup(cellRect);
|
||||||
|
{
|
||||||
|
args.rowRect = new Rect(0, 0, cellRect.width, cellRect.height);
|
||||||
|
base.RowGUI(args);
|
||||||
|
}
|
||||||
|
GUI.EndGroup();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EditorGUI.LabelField(cellRect, item.ContractTypesSummary, labelStyle);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
EditorGUI.LabelField(cellRect, item.Registration.Lifetime.ToString(), labelStyle);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
EditorGUI.LabelField(cellRect, item.RegisterSummary, labelStyle);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
EditorGUI.LabelField(cellRect, item.RefCount.ToString(), labelStyle);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
EditorGUI.LabelField(cellRect, item.ScopeName, labelStyle);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
EditorGUI.LabelField(cellRect, item.ResolveTime.ToString(), labelStyle);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDependencyItemsRecursive(DiagnosticsInfo info, TreeViewItem parent)
|
||||||
|
{
|
||||||
|
var item = new DiagnosticsInfoTreeViewItem(info)
|
||||||
|
{
|
||||||
|
id = NextId(),
|
||||||
|
depth = parent.depth + 1
|
||||||
|
};
|
||||||
|
parent.AddChild(item);
|
||||||
|
SetExpanded(item.id, item.depth <= 1);
|
||||||
|
|
||||||
|
foreach (var dependency in info.Dependencies)
|
||||||
|
{
|
||||||
|
AddDependencyItemsRecursive(dependency, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<DiagnosticsInfoTreeViewItem> Sort(
|
||||||
|
IEnumerable<DiagnosticsInfoTreeViewItem> items,
|
||||||
|
int sortedColumnIndex,
|
||||||
|
bool ascending)
|
||||||
|
{
|
||||||
|
switch (sortedColumnIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return ascending
|
||||||
|
? items.OrderBy(x => x.TypeSummary)
|
||||||
|
: items.OrderByDescending(x => x.TypeSummary);
|
||||||
|
case 2:
|
||||||
|
return ascending
|
||||||
|
? items.OrderBy(x => x.Registration.Lifetime)
|
||||||
|
: items.OrderByDescending(x => x.Registration.Lifetime);
|
||||||
|
case 3:
|
||||||
|
return ascending
|
||||||
|
? items.OrderBy(x => x.RegisterSummary)
|
||||||
|
: items.OrderByDescending(x => x.RegisterSummary);
|
||||||
|
case 4:
|
||||||
|
return ascending
|
||||||
|
? items.OrderBy(x => x.RefCount)
|
||||||
|
: items.OrderByDescending(x => x.RefCount);
|
||||||
|
case 5:
|
||||||
|
return ascending
|
||||||
|
? items.OrderBy(x => x.ScopeName)
|
||||||
|
: items.OrderByDescending(x => x.ScopeName);
|
||||||
|
case 6:
|
||||||
|
return ascending
|
||||||
|
? items.OrderBy(x => x.ResolveTime)
|
||||||
|
: items.OrderByDescending(x => x.ResolveTime);
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(sortedColumnIndex), sortedColumnIndex, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0d65b5790cc048599cb6e65bb859e6aa
|
||||||
|
timeCreated: 1632058224
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using UnityEngine;
|
||||||
|
using VContainer.Diagnostics;
|
||||||
|
using VContainer.Unity;
|
||||||
|
|
||||||
|
namespace VContainer.Editor.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class VContainerDiagnosticsWindow : EditorWindow
|
||||||
|
{
|
||||||
|
static VContainerDiagnosticsWindow window;
|
||||||
|
|
||||||
|
static readonly GUIContent FlattenHeadContent = EditorGUIUtility.TrTextContent("Flatten", "Flatten dependencies");
|
||||||
|
static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View");
|
||||||
|
|
||||||
|
internal static bool EnableAutoReload;
|
||||||
|
internal static bool EnableCaptureStackTrace;
|
||||||
|
|
||||||
|
[MenuItem("Window/VContainer Diagnostics")]
|
||||||
|
public static void OpenWindow()
|
||||||
|
{
|
||||||
|
if (window != null)
|
||||||
|
{
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
GetWindow<VContainerDiagnosticsWindow>("VContainer Diagnostics").Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUIStyle TableListStyle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var style = new GUIStyle("CN Box");
|
||||||
|
style.margin.top = 0;
|
||||||
|
style.padding.left = 3;
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUIStyle DetailsStyle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var detailsStyle = new GUIStyle("CN Message");
|
||||||
|
detailsStyle.wordWrap = false;
|
||||||
|
detailsStyle.stretchHeight = true;
|
||||||
|
detailsStyle.margin.right = 15;
|
||||||
|
return detailsStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VContainerDiagnosticsInfoTreeView treeView;
|
||||||
|
VContainerInstanceTreeView instanceTreeView;
|
||||||
|
SearchField searchField;
|
||||||
|
|
||||||
|
object verticalSplitterState;
|
||||||
|
object horizontalSplitterState;
|
||||||
|
Vector2 tableScrollPosition;
|
||||||
|
Vector2 detailsScrollPosition;
|
||||||
|
Vector2 instanceScrollPosition;
|
||||||
|
|
||||||
|
public void Reload(IObjectResolver resolver)
|
||||||
|
{
|
||||||
|
treeView.ReloadAndSort();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPlayModeStateChange(PlayModeStateChange state)
|
||||||
|
{
|
||||||
|
treeView.ReloadAndSort();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnEnable()
|
||||||
|
{
|
||||||
|
window = this; // set singleton.
|
||||||
|
verticalSplitterState = SplitterGUILayout.CreateSplitterState(new [] { 75f, 25f }, new [] { 32, 32 }, null);
|
||||||
|
horizontalSplitterState = SplitterGUILayout.CreateSplitterState(new[] { 75, 25f }, new[] { 32, 32 }, null);
|
||||||
|
treeView = new VContainerDiagnosticsInfoTreeView();
|
||||||
|
instanceTreeView = new VContainerInstanceTreeView();
|
||||||
|
searchField = new SearchField();
|
||||||
|
|
||||||
|
DiagnositcsContext.OnContainerBuilt += Reload;
|
||||||
|
EditorApplication.playModeStateChanged += OnPlayModeStateChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisable()
|
||||||
|
{
|
||||||
|
DiagnositcsContext.OnContainerBuilt -= Reload;
|
||||||
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGUI()
|
||||||
|
{
|
||||||
|
RenderHeadPanel();
|
||||||
|
|
||||||
|
SplitterGUILayout.BeginVerticalSplit(verticalSplitterState, Array.Empty<GUILayoutOption>());
|
||||||
|
{
|
||||||
|
SplitterGUILayout.BeginHorizontalSplit(horizontalSplitterState);
|
||||||
|
{
|
||||||
|
RenderBuildPanel();
|
||||||
|
RenderInstancePanel();
|
||||||
|
}
|
||||||
|
SplitterGUILayout.EndHorizontalSplit();
|
||||||
|
|
||||||
|
RenderStackTracePanel();
|
||||||
|
}
|
||||||
|
SplitterGUILayout.EndVerticalSplit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderHeadPanel()
|
||||||
|
{
|
||||||
|
using (new EditorGUILayout.VerticalScope())
|
||||||
|
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||||
|
{
|
||||||
|
var flattenOn = GUILayout.Toggle(treeView.Flatten, FlattenHeadContent, EditorStyles.toolbarButton);
|
||||||
|
if (flattenOn != treeView.Flatten)
|
||||||
|
{
|
||||||
|
treeView.Flatten = flattenOn;
|
||||||
|
treeView.ReloadAndSort();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
treeView.searchString = searchField.OnToolbarGUI(treeView.searchString);
|
||||||
|
|
||||||
|
if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton))
|
||||||
|
{
|
||||||
|
treeView.ReloadAndSort();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderBuildPanel()
|
||||||
|
{
|
||||||
|
using (new EditorGUILayout.VerticalScope(TableListStyle))
|
||||||
|
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(tableScrollPosition,
|
||||||
|
// true,
|
||||||
|
// true,
|
||||||
|
GUILayout.ExpandWidth(true),
|
||||||
|
GUILayout.MaxWidth(2000f)))
|
||||||
|
{
|
||||||
|
tableScrollPosition = scrollViewScope.scrollPosition;
|
||||||
|
|
||||||
|
var controlRect = EditorGUILayout.GetControlRect(
|
||||||
|
GUILayout.ExpandHeight(true),
|
||||||
|
GUILayout.ExpandWidth(true));
|
||||||
|
treeView?.OnGUI(controlRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderInstancePanel()
|
||||||
|
{
|
||||||
|
if (!VContainerSettings.DiagnosticsEnabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedItem = treeView.GetSelectedItem();
|
||||||
|
if (selectedItem?.DiagnosticsInfo.ResolveInfo is ResolveInfo resolveInfo)
|
||||||
|
{
|
||||||
|
if (resolveInfo.Instances.Count > 0)
|
||||||
|
{
|
||||||
|
instanceTreeView.CurrentDiagnosticsInfo = selectedItem.DiagnosticsInfo;
|
||||||
|
instanceTreeView.Reload();
|
||||||
|
|
||||||
|
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(instanceScrollPosition, GUILayout.ExpandHeight(true)))
|
||||||
|
{
|
||||||
|
instanceScrollPosition = scrollViewScope.scrollPosition;
|
||||||
|
var controlRect = EditorGUILayout.GetControlRect(
|
||||||
|
GUILayout.ExpandHeight(true),
|
||||||
|
GUILayout.ExpandWidth(true));
|
||||||
|
instanceTreeView?.OnGUI(controlRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.SelectableLabel("No instance reference");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderStackTracePanel()
|
||||||
|
{
|
||||||
|
var message = "";
|
||||||
|
if (VContainerSettings.DiagnosticsEnabled)
|
||||||
|
{
|
||||||
|
var selectedItem = treeView.GetSelectedItem();
|
||||||
|
if (selectedItem?.DiagnosticsInfo?.RegisterInfo is RegisterInfo registerInfo)
|
||||||
|
{
|
||||||
|
message = $"<a href=\"{registerInfo.GetScriptAssetPath()}\" line=\"{registerInfo.GetFileLineNumber()}\">Register at {registerInfo.GetHeadline()}</a>" +
|
||||||
|
Environment.NewLine +
|
||||||
|
Environment.NewLine +
|
||||||
|
selectedItem.DiagnosticsInfo.RegisterInfo.StackTrace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = "VContainer Diagnostics collector is disabled. To enable, please check VContainerSettings.";
|
||||||
|
}
|
||||||
|
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(detailsScrollPosition))
|
||||||
|
{
|
||||||
|
detailsScrollPosition = scrollViewScope.scrollPosition;
|
||||||
|
var vector = DetailsStyle.CalcSize(new GUIContent(message));
|
||||||
|
EditorGUILayout.SelectableLabel(message, DetailsStyle,
|
||||||
|
GUILayout.ExpandHeight(true),
|
||||||
|
GUILayout.ExpandWidth(true),
|
||||||
|
GUILayout.MinWidth(vector.x),
|
||||||
|
GUILayout.MinHeight(vector.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e3e4c1066bed4185a96639d5b1017922
|
||||||
|
timeCreated: 1632057811
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using UnityEngine;
|
||||||
|
using VContainer.Diagnostics;
|
||||||
|
|
||||||
|
namespace VContainer.Editor.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class VContainerInstanceTreeView : TreeView
|
||||||
|
{
|
||||||
|
static int idSeed;
|
||||||
|
static int NextId() => ++idSeed;
|
||||||
|
|
||||||
|
static string Stringify(object instance)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(instance, null))
|
||||||
|
return "null";
|
||||||
|
if (instance is UnityEngine.Object obj && obj == null)
|
||||||
|
return "null or destroyed";
|
||||||
|
return instance.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticsInfo CurrentDiagnosticsInfo { get; set; }
|
||||||
|
|
||||||
|
public VContainerInstanceTreeView() : base(new TreeViewState())
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TreeViewItem BuildRoot()
|
||||||
|
{
|
||||||
|
var root = new TreeViewItem(NextId(), -1, "Root");
|
||||||
|
var children = new List<TreeViewItem>();
|
||||||
|
if (CurrentDiagnosticsInfo is DiagnosticsInfo info)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < info.ResolveInfo.Instances.Count; i++)
|
||||||
|
{
|
||||||
|
var instance = info.ResolveInfo.Instances[i];
|
||||||
|
var item = new TreeViewItem(NextId(), 0,
|
||||||
|
$"({TypeNameHelper.GetTypeAlias(instance.GetType())}) {Stringify(instance)}");
|
||||||
|
AddProperties(instance, item);
|
||||||
|
children.Add(item);
|
||||||
|
SetExpanded(item.id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.children = children;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddProperties(object instance, TreeViewItem parent)
|
||||||
|
{
|
||||||
|
if (instance == null) return;
|
||||||
|
|
||||||
|
var type = instance.GetType();
|
||||||
|
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
if (prop.PropertyType.IsSubclassOf(typeof(UnityEngine.Object)) &&
|
||||||
|
prop.IsDefined(typeof(ObsoleteAttribute), true))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (prop.CanRead)
|
||||||
|
{
|
||||||
|
var value = prop.GetValue(instance);
|
||||||
|
var displayName = $"{prop.Name} = ({TypeNameHelper.GetTypeAlias(prop.PropertyType)}) {Stringify(value)}";
|
||||||
|
parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var displayName = $"{prop.Name} = (write-only {TypeNameHelper.GetTypeAlias(prop.PropertyType)})";
|
||||||
|
parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MissingReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
if ((field.FieldType.IsSubclassOf(typeof(UnityEngine.Object)) &&
|
||||||
|
field.IsDefined(typeof(ObsoleteAttribute), true)) ||
|
||||||
|
field.IsDefined(typeof(CompilerGeneratedAttribute), true))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = field.GetValue(instance);
|
||||||
|
var displayName = $"{field.Name} = ({TypeNameHelper.GetTypeAlias(field.FieldType)}) {Stringify(value)}";
|
||||||
|
parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName));
|
||||||
|
}
|
||||||
|
catch (MissingReferenceException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 538613b38d4a4facb28765eb06374863
|
||||||
|
timeCreated: 1633569303
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using VContainer.Unity;
|
||||||
|
|
||||||
|
namespace VContainer.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(ParentReference))]
|
||||||
|
public sealed class ParentReferencePropertyDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
static string[] GetAllTypeNames()
|
||||||
|
{
|
||||||
|
return new List<string> { "None" }
|
||||||
|
.Concat(TypeCache.GetTypesDerivedFrom<LifetimeScope>()
|
||||||
|
.Where(x => !x.IsAbstract)
|
||||||
|
.Select(type => type.FullName))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetLabel(Type type) => $"{type.Namespace}/{type.Name}";
|
||||||
|
|
||||||
|
string[] names;
|
||||||
|
|
||||||
|
public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label)
|
||||||
|
{
|
||||||
|
if (names == null)
|
||||||
|
{
|
||||||
|
names = GetAllTypeNames();
|
||||||
|
if (prop.serializedObject.targetObject is LifetimeScope lifetimeScope)
|
||||||
|
{
|
||||||
|
var lifetimeScopeName = lifetimeScope.GetType().FullName;
|
||||||
|
names = names.Where(name => name != lifetimeScopeName).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeNameProp = prop.FindPropertyRelative("TypeName");
|
||||||
|
|
||||||
|
using (new EditorGUI.PropertyScope(rect, label, prop))
|
||||||
|
{
|
||||||
|
var labelRect = new Rect(rect.x, rect.y, rect.width, 18f);
|
||||||
|
var popupRect = new Rect(rect.x, rect.y + labelRect.height, rect.width , 18f);
|
||||||
|
|
||||||
|
var index = Array.IndexOf(names, typeNameProp.stringValue);
|
||||||
|
if (index < 0) index = 0;
|
||||||
|
|
||||||
|
EditorGUI.LabelField(labelRect, "Parent");
|
||||||
|
using (var check = new EditorGUI.ChangeCheckScope())
|
||||||
|
{
|
||||||
|
index = EditorGUI.Popup(popupRect, index, names);
|
||||||
|
if (check.changed)
|
||||||
|
{
|
||||||
|
typeNameProp.stringValue = names[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
return 18f + 18f + 4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 51521f95bbd442b08bc2b53efcdeff29
|
||||||
|
timeCreated: 1594556689
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using VContainer.Unity;
|
||||||
|
|
||||||
|
#if UNITY_2020_2_OR_NEWER
|
||||||
|
using UnityEditor.Compilation;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VContainer.Editor
|
||||||
|
{
|
||||||
|
public sealed class ScriptTemplateProcessor : UnityEditor.AssetModificationProcessor
|
||||||
|
{
|
||||||
|
#if UNITY_2020_2_OR_NEWER
|
||||||
|
const string RootNamespaceBeginTag = "#ROOTNAMESPACEBEGIN#";
|
||||||
|
const string RootNamespaceEndTag = "#ROOTNAMESPACEEND#";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const string MonoInstallerTemplate =
|
||||||
|
"using VContainer;\n" +
|
||||||
|
"using VContainer.Unity;\n" +
|
||||||
|
"\n" +
|
||||||
|
#if UNITY_2020_2_OR_NEWER
|
||||||
|
RootNamespaceBeginTag + "\n" +
|
||||||
|
#endif
|
||||||
|
"public class #SCRIPTNAME# : LifetimeScope\n" +
|
||||||
|
"{\n" +
|
||||||
|
" protected override void Configure(IContainerBuilder builder)\n" +
|
||||||
|
" {\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n" +
|
||||||
|
#if UNITY_2020_2_OR_NEWER
|
||||||
|
RootNamespaceEndTag + "\n" +
|
||||||
|
#endif
|
||||||
|
"";
|
||||||
|
|
||||||
|
public static void OnWillCreateAsset(string metaPath)
|
||||||
|
{
|
||||||
|
if (VContainerSettings.Instance != null && VContainerSettings.Instance.DisableScriptModifier)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var suffixIndex = metaPath.LastIndexOf(".meta");
|
||||||
|
if (suffixIndex < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scriptPath = metaPath.Substring(0, suffixIndex);
|
||||||
|
var basename = Path.GetFileNameWithoutExtension(scriptPath);
|
||||||
|
var extname = Path.GetExtension(scriptPath);
|
||||||
|
if (extname != ".cs")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scriptPath.EndsWith("LifetimeScope.cs"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = MonoInstallerTemplate.Replace("#SCRIPTNAME#", basename);
|
||||||
|
|
||||||
|
#if UNITY_2020_2_OR_NEWER
|
||||||
|
{
|
||||||
|
var rootNamespace = CompilationPipeline.GetAssemblyRootNamespaceFromScriptPath(scriptPath);
|
||||||
|
content = RemoveOrInsertNamespaceSimple(content, rootNamespace);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (scriptPath.StartsWith("Assets/"))
|
||||||
|
{
|
||||||
|
scriptPath = scriptPath.Substring("Assets/".Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullPath = Path.Combine(Application.dataPath, scriptPath);
|
||||||
|
File.WriteAllText(fullPath, content);
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2020_2_OR_NEWER
|
||||||
|
// https://github.com/Unity-Technologies/UnityCsReference/blob/2020.2/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs#L495-L550
|
||||||
|
static string RemoveOrInsertNamespaceSimple(string content, string rootNamespace)
|
||||||
|
{
|
||||||
|
const char eol = '\n';
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(rootNamespace))
|
||||||
|
{
|
||||||
|
return content
|
||||||
|
.Replace(RootNamespaceBeginTag + eol, "")
|
||||||
|
.Replace(RootNamespaceEndTag + eol, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = content.Split(eol);
|
||||||
|
|
||||||
|
var startAt = ArrayUtility.IndexOf(lines, RootNamespaceBeginTag);
|
||||||
|
var endAt = ArrayUtility.IndexOf(lines, RootNamespaceEndTag);
|
||||||
|
|
||||||
|
lines[startAt] = $"namespace {rootNamespace}\n{{";
|
||||||
|
{
|
||||||
|
for (var i = startAt + 1; i < endAt; ++i)
|
||||||
|
{
|
||||||
|
lines[i] = $" {lines[i]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines[endAt] = "}";
|
||||||
|
|
||||||
|
return string.Join(eol.ToString(), lines);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e4beca40a4df74e7787b464769f15620
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "VContainer.Editor",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"VContainer"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": true,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7bce08a00b4d54cd694096abadbb897e
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 652e91fc271c04624b08f658dc62397e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1eb05967f0cc40c59ebf679dcc359905
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2024 left (https://github.com/left-/)
|
||||||
|
// Copyright (c) 2020 hadashiA
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if UNITY_2021_3_OR_NEWER
|
||||||
|
using System;
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
using UnityEngine;
|
||||||
|
#else
|
||||||
|
using Awaitable = System.Threading.Tasks.Task;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
internal static class AwaitableExtensions
|
||||||
|
{
|
||||||
|
public static async Awaitable Forget(this Awaitable awaitable,
|
||||||
|
EntryPointExceptionHandler exceptionHandler = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await awaitable;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (exceptionHandler != null)
|
||||||
|
exceptionHandler.Publish(ex);
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9cbf97ce69c64b73a70d6514c63fba00
|
||||||
|
timeCreated: 1717651770
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Threading;
|
||||||
|
#if VCONTAINER_UNITASK_INTEGRATION
|
||||||
|
using Awaitable = Cysharp.Threading.Tasks.UniTask;
|
||||||
|
#elif UNITY_2023_1_OR_NEWER
|
||||||
|
using UnityEngine;
|
||||||
|
#else
|
||||||
|
using Awaitable = System.Threading.Tasks.Task;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IAsyncStartable
|
||||||
|
{
|
||||||
|
Awaitable StartAsync(CancellationToken cancellation = default);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cf8c4b2a3efd465ba893956bd3e16207
|
||||||
|
timeCreated: 1612009971
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IFixedTickable
|
||||||
|
{
|
||||||
|
void FixedTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e83f6a64504c4d1da11c1f6d046a0ecc
|
||||||
|
timeCreated: 1612009716
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IInitializable
|
||||||
|
{
|
||||||
|
void Initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7e3cb2445077468db349bf4dadae84be
|
||||||
|
timeCreated: 1612009493
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface ILateTickable
|
||||||
|
{
|
||||||
|
void LateTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9b82ce93c2104d6c91ce9868c02dafc7
|
||||||
|
timeCreated: 1612009914
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IPostFixedTickable
|
||||||
|
{
|
||||||
|
void PostFixedTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2bf16fe4c16a465b8a8f1d68a8685e11
|
||||||
|
timeCreated: 1612009787
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IPostInitializable
|
||||||
|
{
|
||||||
|
void PostInitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ff6e6d963c0544649d8fedd94d25b8da
|
||||||
|
timeCreated: 1612009543
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IPostLateTickable
|
||||||
|
{
|
||||||
|
void PostLateTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dcd967d55bd245408860956770845c3d
|
||||||
|
timeCreated: 1612009947
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IPostStartable
|
||||||
|
{
|
||||||
|
void PostStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 90378302abd9404eb23d078553c60031
|
||||||
|
timeCreated: 1612009663
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IPostTickable
|
||||||
|
{
|
||||||
|
void PostTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 968f5691c67f4f64b8deed98e6a62cc2
|
||||||
|
timeCreated: 1612009893
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface IStartable
|
||||||
|
{
|
||||||
|
void Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 29296d5987f24dce8ee347cc39fdf1e0
|
||||||
|
timeCreated: 1612009633
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer.Unity
|
||||||
|
{
|
||||||
|
public interface ITickable
|
||||||
|
{
|
||||||
|
void Tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e6460471a1864836afaf0617f182a721
|
||||||
|
timeCreated: 1612009837
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public class PreserveAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2018_4_OR_NEWER
|
||||||
|
[JetBrains.Annotations.MeansImplicitUse(
|
||||||
|
JetBrains.Annotations.ImplicitUseKindFlags.Access |
|
||||||
|
JetBrains.Annotations.ImplicitUseKindFlags.Assign |
|
||||||
|
JetBrains.Annotations.ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
|
||||||
|
#endif
|
||||||
|
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class InjectAttribute : PreserveAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class InjectIgnoreAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d9445a99bad3a60af9f20ca441c14bca
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class KeyAttribute : PreserveAttribute
|
||||||
|
{
|
||||||
|
public object Key { get; }
|
||||||
|
|
||||||
|
public KeyAttribute(object key = null)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 53840c03444a04ed09ef84747bf8d2af
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("VContainer.Tests")]
|
||||||
|
[assembly: InternalsVisibleTo("VContainer.StandaloneTests")]
|
||||||
|
[assembly: InternalsVisibleTo("VContainer.Editor")]
|
||||||
|
[assembly: InternalsVisibleTo("Unity.VContainer.CodeGen")]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6158fbf642b64910aac0b04e9a4d62ea
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using VContainer.Diagnostics;
|
||||||
|
using VContainer.Internal;
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public interface IObjectResolver : IDisposable
|
||||||
|
{
|
||||||
|
object ApplicationOrigin { get; }
|
||||||
|
DiagnosticsCollector Diagnostics { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolve from type with or without key
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This version of resolve looks for all of scopes
|
||||||
|
/// </remarks>
|
||||||
|
object Resolve(Type type, object key = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try resolve from type with or without key
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This version of resolve looks for all of scopes
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns>Successfully resolved</returns>
|
||||||
|
bool TryResolve(Type type, out object resolved, object key = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolve from meta with registration
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This version of resolve will look for instances from only the registration information already founds.
|
||||||
|
/// </remarks>
|
||||||
|
object Resolve(Registration registration);
|
||||||
|
|
||||||
|
IScopedObjectResolver CreateScope(Action<IContainerBuilder> installation = null);
|
||||||
|
|
||||||
|
void Inject(object instance);
|
||||||
|
bool TryGetRegistration(Type type, out Registration registration, object key = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IScopedObjectResolver : IObjectResolver
|
||||||
|
{
|
||||||
|
IObjectResolver Root { get; }
|
||||||
|
IScopedObjectResolver Parent { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Lifetime
|
||||||
|
{
|
||||||
|
Transient,
|
||||||
|
Singleton,
|
||||||
|
Scoped
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ScopedContainer : IScopedObjectResolver
|
||||||
|
{
|
||||||
|
public IObjectResolver Root { get; }
|
||||||
|
public IScopedObjectResolver Parent { get; }
|
||||||
|
public object ApplicationOrigin { get; }
|
||||||
|
public DiagnosticsCollector Diagnostics { get; set; }
|
||||||
|
|
||||||
|
readonly Registry registry;
|
||||||
|
readonly ConcurrentDictionary<Registration, Lazy<object>> sharedInstances = new ConcurrentDictionary<Registration, Lazy<object>>();
|
||||||
|
readonly CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
readonly Func<Registration, Lazy<object>> createInstance;
|
||||||
|
|
||||||
|
internal ScopedContainer(
|
||||||
|
Registry registry,
|
||||||
|
IObjectResolver root,
|
||||||
|
IScopedObjectResolver parent = null,
|
||||||
|
object applicationOrigin = null)
|
||||||
|
{
|
||||||
|
Root = root;
|
||||||
|
Parent = parent;
|
||||||
|
ApplicationOrigin = applicationOrigin;
|
||||||
|
this.registry = registry;
|
||||||
|
createInstance = registration =>
|
||||||
|
{
|
||||||
|
return new Lazy<object>(() => registration.SpawnInstance(this));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public object Resolve(Type type, object key = null)
|
||||||
|
{
|
||||||
|
if (TryFindRegistration(type, key, out var registration))
|
||||||
|
{
|
||||||
|
return Resolve(registration);
|
||||||
|
}
|
||||||
|
throw new VContainerException(type, $"No such registration of type: {type} {(key == null ? string.Empty : $"with Key: {key}")}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryResolve(Type type, out object resolved, object key = null)
|
||||||
|
{
|
||||||
|
if (TryFindRegistration(type, key, out var registration))
|
||||||
|
{
|
||||||
|
resolved = Resolve(registration);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public object Resolve(Registration registration)
|
||||||
|
{
|
||||||
|
if (Diagnostics != null)
|
||||||
|
{
|
||||||
|
return Diagnostics.TraceResolve(registration, ResolveCore);
|
||||||
|
}
|
||||||
|
return ResolveCore(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public IScopedObjectResolver CreateScope(Action<IContainerBuilder> installation = null)
|
||||||
|
{
|
||||||
|
var containerBuilder = new ScopedContainerBuilder(Root, this)
|
||||||
|
{
|
||||||
|
ApplicationOrigin = ApplicationOrigin
|
||||||
|
};
|
||||||
|
installation?.Invoke(containerBuilder);
|
||||||
|
return containerBuilder.BuildScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Inject(object instance)
|
||||||
|
{
|
||||||
|
var injector = InjectorCache.GetOrBuild(instance.GetType());
|
||||||
|
injector.Inject(instance, this, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryGetRegistration(Type type, out Registration registration, object key = null)
|
||||||
|
=> registry.TryGet(type, key, out registration);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Diagnostics != null)
|
||||||
|
{
|
||||||
|
Diagnostics.Clear();
|
||||||
|
}
|
||||||
|
disposables.Dispose();
|
||||||
|
sharedInstances.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
object ResolveCore(Registration registration)
|
||||||
|
{
|
||||||
|
switch (registration.Lifetime)
|
||||||
|
{
|
||||||
|
case Lifetime.Singleton:
|
||||||
|
if (Parent is null)
|
||||||
|
return Root.Resolve(registration);
|
||||||
|
|
||||||
|
if (!registry.Exists(registration.ImplementationType, registration.Key))
|
||||||
|
return Parent.Resolve(registration);
|
||||||
|
|
||||||
|
return CreateTrackedInstance(registration);
|
||||||
|
|
||||||
|
case Lifetime.Scoped:
|
||||||
|
return CreateTrackedInstance(registration);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return registration.SpawnInstance(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
object CreateTrackedInstance(Registration registration)
|
||||||
|
{
|
||||||
|
var lazy = sharedInstances.GetOrAdd(registration, createInstance);
|
||||||
|
var created = lazy.IsValueCreated;
|
||||||
|
var instance = lazy.Value;
|
||||||
|
if (!created && instance is IDisposable disposable && !(registration.Provider is ExistingInstanceProvider))
|
||||||
|
{
|
||||||
|
disposables.Add(disposable);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal bool TryFindRegistration(Type type, object key, out Registration registration)
|
||||||
|
{
|
||||||
|
IScopedObjectResolver scope = this;
|
||||||
|
while (scope != null)
|
||||||
|
{
|
||||||
|
if (scope.TryGetRegistration(type, out registration, key))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
scope = scope.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
registration = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Container : IObjectResolver
|
||||||
|
{
|
||||||
|
public object ApplicationOrigin { get; }
|
||||||
|
public DiagnosticsCollector Diagnostics { get; set; }
|
||||||
|
|
||||||
|
readonly Registry registry;
|
||||||
|
readonly IScopedObjectResolver rootScope;
|
||||||
|
readonly ConcurrentDictionary<Registration, Lazy<object>> sharedInstances = new ConcurrentDictionary<Registration, Lazy<object>>();
|
||||||
|
readonly CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
readonly Func<Registration, Lazy<object>> createInstance;
|
||||||
|
|
||||||
|
internal Container(Registry registry, object applicationOrigin = null)
|
||||||
|
{
|
||||||
|
this.registry = registry;
|
||||||
|
rootScope = new ScopedContainer(registry, this, applicationOrigin: applicationOrigin);
|
||||||
|
|
||||||
|
createInstance = registration =>
|
||||||
|
{
|
||||||
|
return new Lazy<object>(() => registration.SpawnInstance(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
ApplicationOrigin = applicationOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public object Resolve(Type type, object key = null)
|
||||||
|
{
|
||||||
|
if (TryGetRegistration(type, out var registration, key))
|
||||||
|
{
|
||||||
|
return Resolve(registration);
|
||||||
|
}
|
||||||
|
throw new VContainerException(type, $"No such registration of type: {type} with Key: {key}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryResolve(Type type, out object resolved, object key = null)
|
||||||
|
{
|
||||||
|
if (TryGetRegistration(type, out var registration, key))
|
||||||
|
{
|
||||||
|
resolved = Resolve(registration);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public object Resolve(Registration registration)
|
||||||
|
{
|
||||||
|
if (Diagnostics != null)
|
||||||
|
{
|
||||||
|
return Diagnostics.TraceResolve(registration, ResolveCore);
|
||||||
|
}
|
||||||
|
return ResolveCore(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public IScopedObjectResolver CreateScope(Action<IContainerBuilder> installation = null)
|
||||||
|
=> rootScope.CreateScope(installation);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Inject(object instance)
|
||||||
|
{
|
||||||
|
var injector = InjectorCache.GetOrBuild(instance.GetType());
|
||||||
|
injector.Inject(instance, this, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryGetRegistration(Type type, out Registration registration, object key = null)
|
||||||
|
=> registry.TryGet(type, key, out registration);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Diagnostics != null)
|
||||||
|
{
|
||||||
|
Diagnostics.Clear();
|
||||||
|
}
|
||||||
|
rootScope.Dispose();
|
||||||
|
disposables.Dispose();
|
||||||
|
sharedInstances.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
object ResolveCore(Registration registration)
|
||||||
|
{
|
||||||
|
switch (registration.Lifetime)
|
||||||
|
{
|
||||||
|
case Lifetime.Singleton:
|
||||||
|
var singleton = sharedInstances.GetOrAdd(registration, createInstance);
|
||||||
|
if (!singleton.IsValueCreated && singleton.Value is IDisposable disposable && !(registration.Provider is ExistingInstanceProvider))
|
||||||
|
{
|
||||||
|
disposables.Add(disposable);
|
||||||
|
}
|
||||||
|
return singleton.Value;
|
||||||
|
|
||||||
|
case Lifetime.Scoped:
|
||||||
|
return rootScope.Resolve(registration);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return registration.SpawnInstance(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb5df50ba586a47e5a93a5045e69bca7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using VContainer.Diagnostics;
|
||||||
|
using VContainer.Internal;
|
||||||
|
#if VCONTAINER_PARALLEL_CONTAINER_BUILD
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public interface IContainerBuilder
|
||||||
|
{
|
||||||
|
object ApplicationOrigin { get; set; }
|
||||||
|
DiagnosticsCollector Diagnostics { get; set; }
|
||||||
|
int Count { get; }
|
||||||
|
RegistrationBuilder this[int index] { get; set; }
|
||||||
|
|
||||||
|
T Register<T>(T registrationBuilder) where T : RegistrationBuilder;
|
||||||
|
void RegisterBuildCallback(Action<IObjectResolver> container);
|
||||||
|
bool Exists(Type type, bool includeInterfaceTypes = false, bool findParentScopes = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ScopedContainerBuilder : ContainerBuilder
|
||||||
|
{
|
||||||
|
readonly IObjectResolver root;
|
||||||
|
readonly IScopedObjectResolver parent;
|
||||||
|
|
||||||
|
internal ScopedContainerBuilder(IObjectResolver root, IScopedObjectResolver parent)
|
||||||
|
{
|
||||||
|
this.root = root;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public IScopedObjectResolver BuildScope()
|
||||||
|
{
|
||||||
|
var registry = BuildRegistry();
|
||||||
|
var container = new ScopedContainer(registry, root, parent, ApplicationOrigin);
|
||||||
|
container.Diagnostics = Diagnostics;
|
||||||
|
EmitCallbacks(container);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override IObjectResolver Build() => BuildScope();
|
||||||
|
|
||||||
|
public override bool Exists(Type type, bool includeInterfaceTypes = false, bool findParentScopes = false)
|
||||||
|
{
|
||||||
|
if (base.Exists(type, includeInterfaceTypes, findParentScopes))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findParentScopes)
|
||||||
|
{
|
||||||
|
var next = parent;
|
||||||
|
while (next != null)
|
||||||
|
{
|
||||||
|
if (next.TryGetRegistration(type, out var registration))
|
||||||
|
{
|
||||||
|
if (includeInterfaceTypes || registration.ImplementationType == type)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next = next.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContainerBuilder : IContainerBuilder
|
||||||
|
{
|
||||||
|
public object ApplicationOrigin { get; set; }
|
||||||
|
|
||||||
|
public int Count => registrationBuilders.Count;
|
||||||
|
|
||||||
|
public RegistrationBuilder this[int index]
|
||||||
|
{
|
||||||
|
get => registrationBuilders[index];
|
||||||
|
set => registrationBuilders[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticsCollector Diagnostics
|
||||||
|
{
|
||||||
|
get => diagnostics;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
diagnostics = value;
|
||||||
|
diagnostics?.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly List<RegistrationBuilder> registrationBuilders = new List<RegistrationBuilder>();
|
||||||
|
Action<IObjectResolver> buildCallback;
|
||||||
|
DiagnosticsCollector diagnostics;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public T Register<T>(T registrationBuilder) where T : RegistrationBuilder
|
||||||
|
{
|
||||||
|
registrationBuilders.Add(registrationBuilder);
|
||||||
|
Diagnostics?.TraceRegister(new RegisterInfo(registrationBuilder));
|
||||||
|
return registrationBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void RegisterBuildCallback(Action<IObjectResolver> callback)
|
||||||
|
{
|
||||||
|
buildCallback += callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public virtual bool Exists(Type type, bool includeInterfaceTypes = false, bool findParentScopes = false)
|
||||||
|
{
|
||||||
|
foreach (var registrationBuilder in registrationBuilders)
|
||||||
|
{
|
||||||
|
if (registrationBuilder.ImplementationType == type ||
|
||||||
|
includeInterfaceTypes && registrationBuilder.InterfaceTypes?.Contains(type) == true)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public virtual IObjectResolver Build()
|
||||||
|
{
|
||||||
|
var registry = BuildRegistry();
|
||||||
|
var container = new Container(registry, ApplicationOrigin);
|
||||||
|
container.Diagnostics = Diagnostics;
|
||||||
|
EmitCallbacks(container);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected Registry BuildRegistry()
|
||||||
|
{
|
||||||
|
var registrations = new Registration[registrationBuilders.Count + 1];
|
||||||
|
|
||||||
|
#if VCONTAINER_PARALLEL_CONTAINER_BUILD
|
||||||
|
Parallel.For(0, registrationBuilders.Count, i =>
|
||||||
|
{
|
||||||
|
var registrationBuilder = registrationBuilders[i];
|
||||||
|
var registration = registrationBuilder.Build();
|
||||||
|
Diagnostics?.TraceBuild(registrationBuilder, registration);
|
||||||
|
registrations[i] = registration;
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
for (var i = 0; i < registrationBuilders.Count; i++)
|
||||||
|
{
|
||||||
|
var registrationBuilder = registrationBuilders[i];
|
||||||
|
var registration = registrationBuilder.Build();
|
||||||
|
Diagnostics?.TraceBuild(registrationBuilder, registration);
|
||||||
|
registrations[i] = registration;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
registrations[registrations.Length - 1] = new Registration(
|
||||||
|
typeof(IObjectResolver),
|
||||||
|
Lifetime.Transient,
|
||||||
|
null,
|
||||||
|
ContainerInstanceProvider.Default);
|
||||||
|
|
||||||
|
var registry = Registry.Build(registrations);
|
||||||
|
TypeAnalyzer.CheckCircularDependency(registrations, registry);
|
||||||
|
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected void EmitCallbacks(IObjectResolver container)
|
||||||
|
{
|
||||||
|
buildCallback?.Invoke(container);
|
||||||
|
Diagnostics?.NotifyContainerBuilt(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cc776071549c945db9e79411839a1bb6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using VContainer.Internal;
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public static class ContainerBuilderExtensions
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder Register(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Type type,
|
||||||
|
Lifetime lifetime) =>
|
||||||
|
builder.Register(type.IsGenericType && type.IsGenericTypeDefinition
|
||||||
|
? new OpenGenericRegistrationBuilder(type, lifetime)
|
||||||
|
: new RegistrationBuilder(type, lifetime));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder Register(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Type interfaceType,
|
||||||
|
Type implementationType,
|
||||||
|
Lifetime lifetime) =>
|
||||||
|
builder.Register(implementationType, lifetime).As(interfaceType);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder Register<T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Lifetime lifetime) =>
|
||||||
|
builder.Register(typeof(T), lifetime);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder Register<TInterface, TImplement>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Lifetime lifetime)
|
||||||
|
where TImplement : TInterface =>
|
||||||
|
builder.Register<TImplement>(lifetime).As<TInterface>();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder Register<TInterface1, TInterface2, TImplement>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Lifetime lifetime)
|
||||||
|
where TImplement : TInterface1, TInterface2 =>
|
||||||
|
builder.Register<TImplement>(lifetime).As(typeof(TInterface1), typeof(TInterface2));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder Register<TInterface1, TInterface2, TInterface3, TImplement>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Lifetime lifetime)
|
||||||
|
where TImplement : TInterface1, TInterface2, TInterface3
|
||||||
|
=> builder.Register<TImplement>(lifetime).As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder Register<TInterface>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<IObjectResolver, TInterface> implementationConfiguration,
|
||||||
|
Lifetime lifetime)
|
||||||
|
=> builder.Register(new FuncRegistrationBuilder(container => implementationConfiguration(container), typeof(TInterface), lifetime));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterInstance(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
object instance,
|
||||||
|
Type implementationType)
|
||||||
|
=> builder.Register(new InstanceRegistrationBuilder(instance)).As(implementationType);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterInstance<TInterface>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
TInterface instance)
|
||||||
|
=> builder.Register(new InstanceRegistrationBuilder(instance)).As(typeof(TInterface));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterInstance<TInterface1, TInterface2>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
TInterface1 instance)
|
||||||
|
=> builder.RegisterInstance(instance).As(typeof(TInterface1), typeof(TInterface2));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterInstance<TInterface1, TInterface2, TInterface3>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
TInterface1 instance)
|
||||||
|
=> builder.RegisterInstance(instance).As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<T> factory)
|
||||||
|
=> builder.RegisterInstance(factory);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<TParam1, T> factory)
|
||||||
|
=> builder.RegisterInstance(factory);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, TParam2, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<TParam1, TParam2, T> factory)
|
||||||
|
=> builder.RegisterInstance(factory);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, TParam2, TParam3, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<TParam1, TParam2, TParam3, T> factory)
|
||||||
|
=> builder.RegisterInstance(factory);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, TParam2, TParam3, TParam4, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<TParam1, TParam2, TParam3, TParam4, T> factory)
|
||||||
|
=> builder.RegisterInstance(factory);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<IObjectResolver, Func<T>> factoryFactory,
|
||||||
|
Lifetime lifetime)
|
||||||
|
=> builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func<T>), lifetime));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<IObjectResolver, Func<TParam1, T>> factoryFactory,
|
||||||
|
Lifetime lifetime)
|
||||||
|
=> builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func<TParam1, T>), lifetime));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, TParam2, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<IObjectResolver, Func<TParam1, TParam2, T>> factoryFactory,
|
||||||
|
Lifetime lifetime)
|
||||||
|
=> builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func<TParam1, TParam2, T>), lifetime));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, TParam2, TParam3, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<IObjectResolver, Func<TParam1, TParam2, TParam3, T>> factoryFactory,
|
||||||
|
Lifetime lifetime)
|
||||||
|
=> builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func<TParam1, TParam2, TParam3, T>), lifetime));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static RegistrationBuilder RegisterFactory<TParam1, TParam2, TParam3, TParam4, T>(
|
||||||
|
this IContainerBuilder builder,
|
||||||
|
Func<IObjectResolver, Func<TParam1, TParam2, TParam3, TParam4, T>> factoryFactory,
|
||||||
|
Lifetime lifetime)
|
||||||
|
=> builder.Register(new FuncRegistrationBuilder(factoryFactory, typeof(Func<TParam1, TParam2, TParam3, TParam4, T>), lifetime));
|
||||||
|
|
||||||
|
public static void RegisterDisposeCallback(this IContainerBuilder builder, Action<IObjectResolver> callback)
|
||||||
|
{
|
||||||
|
if (!builder.Exists(typeof(BuilderCallbackDisposable)))
|
||||||
|
{
|
||||||
|
builder.Register<BuilderCallbackDisposable>(Lifetime.Singleton);
|
||||||
|
}
|
||||||
|
builder.RegisterBuildCallback(container =>
|
||||||
|
{
|
||||||
|
var disposable = container.Resolve<BuilderCallbackDisposable>();
|
||||||
|
disposable.Disposing += callback;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("IObjectResolver is registered by default. This method does nothing.")]
|
||||||
|
public static void RegisterContainer(this IContainerBuilder builder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 76ede31a370f45ffa838fe2da30f58a9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f6de57dc253147c896d75dc9c2cfeab5
|
||||||
|
timeCreated: 1632059553
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using VContainer.Internal;
|
||||||
|
|
||||||
|
namespace VContainer.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class DiagnosticsCollector
|
||||||
|
{
|
||||||
|
public string ScopeName { get; }
|
||||||
|
|
||||||
|
readonly List<DiagnosticsInfo> diagnosticsInfos = new List<DiagnosticsInfo>();
|
||||||
|
readonly ThreadLocal<Stack<DiagnosticsInfo>> resolveCallStack
|
||||||
|
= new ThreadLocal<Stack<DiagnosticsInfo>>(() => new Stack<DiagnosticsInfo>());
|
||||||
|
|
||||||
|
public DiagnosticsCollector(string scopeName)
|
||||||
|
{
|
||||||
|
ScopeName = scopeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DiagnosticsInfo> GetDiagnosticsInfos()
|
||||||
|
{
|
||||||
|
return diagnosticsInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (diagnosticsInfos)
|
||||||
|
{
|
||||||
|
diagnosticsInfos.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TraceRegister(RegisterInfo registerInfo)
|
||||||
|
{
|
||||||
|
lock (diagnosticsInfos)
|
||||||
|
{
|
||||||
|
diagnosticsInfos.Add(new DiagnosticsInfo(ScopeName, registerInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TraceBuild(RegistrationBuilder registrationBuilder, Registration registration)
|
||||||
|
{
|
||||||
|
lock (diagnosticsInfos)
|
||||||
|
{
|
||||||
|
foreach (var x in diagnosticsInfos)
|
||||||
|
{
|
||||||
|
if (x.RegisterInfo.RegistrationBuilder == registrationBuilder)
|
||||||
|
{
|
||||||
|
x.ResolveInfo = new ResolveInfo(registration);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object TraceResolve(Registration registration, Func<Registration, object> resolving)
|
||||||
|
{
|
||||||
|
var current = DiagnositcsContext.FindByRegistration(registration);
|
||||||
|
var owner = resolveCallStack.Value.Count > 0 ? resolveCallStack.Value.Peek() : null;
|
||||||
|
|
||||||
|
if (!(registration.Provider is CollectionInstanceProvider) && current != null && current != owner)
|
||||||
|
{
|
||||||
|
current.ResolveInfo.RefCount += 1;
|
||||||
|
current.ResolveInfo.MaxDepth = current.ResolveInfo.MaxDepth < 0
|
||||||
|
? resolveCallStack.Value.Count
|
||||||
|
: Math.Max(current.ResolveInfo.MaxDepth, resolveCallStack.Value.Count);
|
||||||
|
|
||||||
|
owner?.Dependencies.Add(current);
|
||||||
|
|
||||||
|
resolveCallStack.Value.Push(current);
|
||||||
|
var watch = Stopwatch.StartNew();
|
||||||
|
var instance = resolving(registration);
|
||||||
|
watch.Stop();
|
||||||
|
resolveCallStack.Value.Pop();
|
||||||
|
|
||||||
|
SetResolveTime(current, watch.ElapsedMilliseconds);
|
||||||
|
|
||||||
|
if (!current.ResolveInfo.Instances.Contains(instance))
|
||||||
|
{
|
||||||
|
current.ResolveInfo.Instances.Add(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
return resolving(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetResolveTime(DiagnosticsInfo current, long elapsedMilliseconds)
|
||||||
|
{
|
||||||
|
var resolves = current.ResolveInfo.RefCount;
|
||||||
|
var resolveTime = current.ResolveInfo.ResolveTime;
|
||||||
|
|
||||||
|
switch (current.ResolveInfo.Registration.Lifetime)
|
||||||
|
{
|
||||||
|
case Lifetime.Transient:
|
||||||
|
resolveTime = (resolveTime * (resolves - 1) + elapsedMilliseconds) / resolves;
|
||||||
|
break;
|
||||||
|
case Lifetime.Scoped:
|
||||||
|
case Lifetime.Singleton:
|
||||||
|
if (elapsedMilliseconds > resolveTime)
|
||||||
|
resolveTime = elapsedMilliseconds;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
current.ResolveInfo.ResolveTime = resolveTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyContainerBuilt(IObjectResolver container)
|
||||||
|
=> DiagnositcsContext.NotifyContainerBuilt(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 689fb82515a24c2887f2693a3a11010b
|
||||||
|
timeCreated: 1632059581
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace VContainer.Diagnostics
|
||||||
|
{
|
||||||
|
public static class DiagnositcsContext
|
||||||
|
{
|
||||||
|
static readonly Dictionary<string, DiagnosticsCollector> collectors
|
||||||
|
= new Dictionary<string, DiagnosticsCollector>();
|
||||||
|
|
||||||
|
public static event Action<IObjectResolver> OnContainerBuilt;
|
||||||
|
|
||||||
|
public static DiagnosticsCollector GetCollector(string name)
|
||||||
|
{
|
||||||
|
lock (collectors)
|
||||||
|
{
|
||||||
|
if (!collectors.TryGetValue(name, out var collector))
|
||||||
|
{
|
||||||
|
collector = new DiagnosticsCollector(name);
|
||||||
|
collectors.Add(name, collector);
|
||||||
|
}
|
||||||
|
return collector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ILookup<string, DiagnosticsInfo> GetGroupedDiagnosticsInfos()
|
||||||
|
{
|
||||||
|
lock (collectors)
|
||||||
|
{
|
||||||
|
return collectors
|
||||||
|
.SelectMany(x => x.Value.GetDiagnosticsInfos())
|
||||||
|
.Where(x => x.ResolveInfo.MaxDepth <= 1)
|
||||||
|
.ToLookup(x => x.ScopeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<DiagnosticsInfo> GetDiagnosticsInfos()
|
||||||
|
{
|
||||||
|
lock (collectors)
|
||||||
|
{
|
||||||
|
return collectors.SelectMany(x => x.Value.GetDiagnosticsInfos());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void NotifyContainerBuilt(IObjectResolver container)
|
||||||
|
{
|
||||||
|
OnContainerBuilt?.Invoke(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static DiagnosticsInfo FindByRegistration(Registration registration)
|
||||||
|
{
|
||||||
|
return GetDiagnosticsInfos().FirstOrDefault(x => x.ResolveInfo.Registration == registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveCollector(string name)
|
||||||
|
{
|
||||||
|
lock (collectors)
|
||||||
|
{
|
||||||
|
collectors.Remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a87e62523cc246f2917f3ba78f7c6e99
|
||||||
|
timeCreated: 1633441412
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VContainer.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class DiagnosticsInfo
|
||||||
|
{
|
||||||
|
public string ScopeName { get; }
|
||||||
|
public RegisterInfo RegisterInfo { get; }
|
||||||
|
public ResolveInfo ResolveInfo { get; set; }
|
||||||
|
public List<DiagnosticsInfo> Dependencies { get; } = new List<DiagnosticsInfo>();
|
||||||
|
|
||||||
|
public DiagnosticsInfo(string scopeName, RegisterInfo registerInfo)
|
||||||
|
{
|
||||||
|
ScopeName = scopeName;
|
||||||
|
RegisterInfo = registerInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af9e78dd14ec4031998bbf9c34cc93ae
|
||||||
|
timeCreated: 1633441316
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace VContainer.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class RegisterInfo
|
||||||
|
{
|
||||||
|
static bool displayFileNames = true;
|
||||||
|
static int idSeed;
|
||||||
|
|
||||||
|
public int Id { get; }
|
||||||
|
public RegistrationBuilder RegistrationBuilder { get; }
|
||||||
|
public StackTrace StackTrace { get; }
|
||||||
|
|
||||||
|
StackFrame headLineStackFrame;
|
||||||
|
|
||||||
|
internal string formattedStackTrace = default; // cache field for internal use(Unity Editor, etc...)
|
||||||
|
|
||||||
|
public RegisterInfo(RegistrationBuilder registrationBuilder)
|
||||||
|
{
|
||||||
|
Id = Interlocked.Increment(ref idSeed);
|
||||||
|
RegistrationBuilder = registrationBuilder;
|
||||||
|
StackTrace = new StackTrace(true);
|
||||||
|
headLineStackFrame = GetHeadlineFrame(StackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFilename()
|
||||||
|
{
|
||||||
|
if (headLineStackFrame != null && displayFileNames && headLineStackFrame.GetILOffset() != -1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return headLineStackFrame.GetFileName();
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
displayFileNames = false;
|
||||||
|
}
|
||||||
|
catch (SecurityException)
|
||||||
|
{
|
||||||
|
displayFileNames = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetFileLineNumber()
|
||||||
|
{
|
||||||
|
if (headLineStackFrame != null && displayFileNames && headLineStackFrame.GetILOffset() != -1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return headLineStackFrame.GetFileLineNumber();
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
displayFileNames = false;
|
||||||
|
}
|
||||||
|
catch (SecurityException)
|
||||||
|
{
|
||||||
|
displayFileNames = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetScriptAssetPath()
|
||||||
|
{
|
||||||
|
var filename = GetFilename();
|
||||||
|
if (filename == null)
|
||||||
|
return "";
|
||||||
|
var prefixIndex = filename.LastIndexOf("Assets/");
|
||||||
|
return prefixIndex > 0 ? filename.Substring(prefixIndex) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetHeadline()
|
||||||
|
{
|
||||||
|
if (headLineStackFrame == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var method = headLineStackFrame.GetMethod();
|
||||||
|
var filename = GetFilename();
|
||||||
|
if (filename != null)
|
||||||
|
{
|
||||||
|
var lineNumber = GetFileLineNumber();
|
||||||
|
return $"{method.DeclaringType?.FullName}.{method.Name} (at {Path.GetFileName(filename)}:{lineNumber})";
|
||||||
|
}
|
||||||
|
|
||||||
|
var ilOffset = headLineStackFrame.GetILOffset();
|
||||||
|
if (ilOffset != -1)
|
||||||
|
{
|
||||||
|
return $"{method.DeclaringType?.FullName}.{method.Name}(offset: {ilOffset})";
|
||||||
|
}
|
||||||
|
return $"{method.DeclaringType?.FullName}.{method.Name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
StackFrame GetHeadlineFrame(StackTrace stackTrace)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < stackTrace.FrameCount; i++)
|
||||||
|
{
|
||||||
|
var sf = stackTrace.GetFrame(i);
|
||||||
|
if (sf == null) continue;
|
||||||
|
|
||||||
|
var m = sf.GetMethod();
|
||||||
|
if (m == null) continue;
|
||||||
|
|
||||||
|
if (m.DeclaringType == null) continue;
|
||||||
|
if (m.DeclaringType.Namespace == null || !m.DeclaringType.Namespace.StartsWith("VContainer"))
|
||||||
|
{
|
||||||
|
return sf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stackTrace.FrameCount > 0 ? stackTrace.GetFrame(0) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4359f1f8bb3b4540835998a60eb93049
|
||||||
|
timeCreated: 1633272541
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VContainer.Diagnostics
|
||||||
|
{
|
||||||
|
public sealed class ResolveInfo
|
||||||
|
{
|
||||||
|
public Registration Registration { get; }
|
||||||
|
public List<object> Instances { get; } = new List<object>();
|
||||||
|
public int MaxDepth { get; set; } = -1;
|
||||||
|
public int RefCount { get; set; }
|
||||||
|
public long ResolveTime { get; set; }
|
||||||
|
|
||||||
|
public ResolveInfo(Registration registration)
|
||||||
|
{
|
||||||
|
Registration = registration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3f53a6c226cf4a04ae556c00dc477f4b
|
||||||
|
timeCreated: 1633102576
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public interface IInjectParameter
|
||||||
|
{
|
||||||
|
bool Match(Type parameterType, string parameterName);
|
||||||
|
object GetValue(IObjectResolver resolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 890f74c4a6fb42db8340eb8f1a505330
|
||||||
|
timeCreated: 1595654174
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public interface IInjector
|
||||||
|
{
|
||||||
|
void Inject(object instance, IObjectResolver resolver, IReadOnlyList<IInjectParameter> parameters);
|
||||||
|
object CreateInstance(IObjectResolver resolver, IReadOnlyList<IInjectParameter> parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ddd3eeb838dd449228703d913f0e9108
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public interface IInstanceProvider
|
||||||
|
{
|
||||||
|
object SpawnInstance(IObjectResolver resolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0474f1261ed04e1e91e41b96a52297e9
|
||||||
|
timeCreated: 1637894248
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace VContainer
|
||||||
|
{
|
||||||
|
public static class IObjectResolverExtensions
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Resolve<T>(this IObjectResolver resolver, object key = null) => (T)resolver.Resolve(typeof(T), key);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool TryResolve<T>(this IObjectResolver resolver, out T resolved, object key = null)
|
||||||
|
{
|
||||||
|
if (resolver.TryResolve(typeof(T), out var r, key))
|
||||||
|
{
|
||||||
|
resolved = (T)r;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T ResolveOrDefault<T>(this IObjectResolver resolver, T defaultValue = default, object key = null)
|
||||||
|
{
|
||||||
|
if (resolver.TryResolve(typeof(T), out var value, key))
|
||||||
|
{
|
||||||
|
return (T)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using from CodeGen
|
||||||
|
[Preserve]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static object ResolveNonGeneric(this IObjectResolver resolve, Type type, object key = null) => resolve.Resolve(type, key);
|
||||||
|
|
||||||
|
public static object ResolveOrParameter(
|
||||||
|
this IObjectResolver resolver,
|
||||||
|
Type parameterType,
|
||||||
|
string parameterName,
|
||||||
|
IReadOnlyList<IInjectParameter> parameters,
|
||||||
|
object key = null)
|
||||||
|
{
|
||||||
|
if (parameters == null)
|
||||||
|
{
|
||||||
|
return resolver.Resolve(parameterType, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once ForCanBeConvertedToForeach
|
||||||
|
for (var i = 0; i < parameters.Count; i++)
|
||||||
|
{
|
||||||
|
var parameter = parameters[i];
|
||||||
|
if (parameter.Match(parameterType, parameterName))
|
||||||
|
{
|
||||||
|
return parameter.GetValue(resolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key != null ?
|
||||||
|
resolver.Resolve(parameterType, key) :
|
||||||
|
resolver.Resolve(parameterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4b7dc27e3935451ba5ca0f67efba7e6f
|
||||||
|
timeCreated: 1595619880
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 853495921eac94a55bba66a67ce6feb7
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
class BuilderCallbackDisposable : IDisposable
|
||||||
|
{
|
||||||
|
public event Action<IObjectResolver> Disposing;
|
||||||
|
readonly IObjectResolver container;
|
||||||
|
|
||||||
|
public BuilderCallbackDisposable(IObjectResolver container)
|
||||||
|
{
|
||||||
|
this.container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Disposing != null) Disposing.Invoke(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 04c6203c157b4898b815f392b47d02ba
|
||||||
|
timeCreated: 1703318719
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
sealed class CappedArrayPool<T>
|
||||||
|
{
|
||||||
|
internal const int InitialBucketSize = 4;
|
||||||
|
|
||||||
|
public static readonly CappedArrayPool<T> Shared8Limit = new CappedArrayPool<T>(8);
|
||||||
|
|
||||||
|
readonly T[][][] buckets;
|
||||||
|
readonly object syncRoot = new object();
|
||||||
|
readonly int[] tails;
|
||||||
|
|
||||||
|
internal CappedArrayPool(int maxLength)
|
||||||
|
{
|
||||||
|
buckets = new T[maxLength][][];
|
||||||
|
tails = new int[maxLength];
|
||||||
|
for (var i = 0; i < maxLength; i++)
|
||||||
|
{
|
||||||
|
var arrayLength = i + 1;
|
||||||
|
buckets[i] = new T[InitialBucketSize][];
|
||||||
|
for (var j = 0; j < InitialBucketSize; j++)
|
||||||
|
{
|
||||||
|
buckets[i][j] = new T[arrayLength];
|
||||||
|
}
|
||||||
|
tails[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] Rent(int length)
|
||||||
|
{
|
||||||
|
if (length <= 0)
|
||||||
|
return Array.Empty<T>();
|
||||||
|
|
||||||
|
if (length > buckets.Length)
|
||||||
|
return new T[length]; // Not supported
|
||||||
|
|
||||||
|
var i = length - 1;
|
||||||
|
|
||||||
|
lock (syncRoot)
|
||||||
|
{
|
||||||
|
var bucket = buckets[i];
|
||||||
|
var tail = tails[i];
|
||||||
|
if (tail >= bucket.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref bucket, bucket.Length * 2);
|
||||||
|
buckets[i] = bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bucket[tail] == null)
|
||||||
|
{
|
||||||
|
bucket[tail] = new T[length];
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = bucket[tail];
|
||||||
|
tails[i] += 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(T[] array)
|
||||||
|
{
|
||||||
|
if (array.Length <= 0 || array.Length > buckets.Length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var i = array.Length - 1;
|
||||||
|
lock (syncRoot)
|
||||||
|
{
|
||||||
|
Array.Clear(array, 0, array.Length);
|
||||||
|
if (tails[i] > 0)
|
||||||
|
tails[i] -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 798af9f34fc7941f6a89a12f634db404
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
sealed class CompositeDisposable : IDisposable
|
||||||
|
{
|
||||||
|
readonly Stack<IDisposable> disposables = new Stack<IDisposable>();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
IDisposable disposable;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
lock (disposables)
|
||||||
|
{
|
||||||
|
disposable = disposables.Count > 0
|
||||||
|
? disposables.Pop()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
disposable?.Dispose();
|
||||||
|
} while (disposable != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Add(IDisposable disposable)
|
||||||
|
{
|
||||||
|
lock (disposables)
|
||||||
|
{
|
||||||
|
disposables.Push(disposable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4308506e1771d4ec69df54d5a890d67b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
public sealed class ContainerLocal<T>
|
||||||
|
{
|
||||||
|
public readonly T Value;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
public ContainerLocal(T value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7868bf6fa13e45b39973d776a3fad130
|
||||||
|
timeCreated: 1637458343
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
sealed class FixedTypeObjectKeyHashtable<TValue>
|
||||||
|
{
|
||||||
|
readonly struct HashEntry
|
||||||
|
{
|
||||||
|
public readonly Type Type;
|
||||||
|
public readonly object Key;
|
||||||
|
public readonly TValue Value;
|
||||||
|
|
||||||
|
public HashEntry(Type type, object key, TValue value)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly HashEntry[][] table;
|
||||||
|
readonly int indexFor;
|
||||||
|
|
||||||
|
public FixedTypeObjectKeyHashtable(KeyValuePair<(Type, object), TValue>[] values, float loadFactor = 0.75f)
|
||||||
|
{
|
||||||
|
var initialCapacity = (int)(values.Length / loadFactor);
|
||||||
|
|
||||||
|
// make power of 2
|
||||||
|
var capacity = 1;
|
||||||
|
while (capacity < initialCapacity)
|
||||||
|
{
|
||||||
|
capacity <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
table = new HashEntry[capacity][];
|
||||||
|
indexFor = table.Length - 1;
|
||||||
|
|
||||||
|
foreach (var item in values)
|
||||||
|
{
|
||||||
|
var hash = GetHashCode(item.Key.Item1, item.Key.Item2);
|
||||||
|
var array = table[hash & indexFor];
|
||||||
|
if (array == null)
|
||||||
|
{
|
||||||
|
array = new HashEntry[1];
|
||||||
|
array[0] = new HashEntry(item.Key.Item1, item.Key.Item2, item.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var newArray = new HashEntry[array.Length + 1];
|
||||||
|
Array.Copy(array, newArray, array.Length);
|
||||||
|
array = newArray;
|
||||||
|
array[array.Length - 1] = new HashEntry(item.Key.Item1, item.Key.Item2, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
table[hash & indexFor] = array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetHashCode(Type type, object key = null)
|
||||||
|
{
|
||||||
|
var typeHash = RuntimeHelpers.GetHashCode(type);
|
||||||
|
|
||||||
|
if(key == null)
|
||||||
|
return typeHash;
|
||||||
|
|
||||||
|
// Combine the hash codes of Type and Key
|
||||||
|
var keyHash = key.GetHashCode();
|
||||||
|
return (typeHash * 397) ^ keyHash; // FNV-style combination
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGet(Type type, object key, out TValue value)
|
||||||
|
{
|
||||||
|
var hashCode = GetHashCode(type, key);
|
||||||
|
var buckets = table[hashCode & indexFor];
|
||||||
|
|
||||||
|
if (buckets == null) goto END;
|
||||||
|
|
||||||
|
if (buckets[0].Type == type)
|
||||||
|
{
|
||||||
|
if (key == null || Equals(buckets[0].Key, key))
|
||||||
|
{
|
||||||
|
value = buckets[0].Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i < buckets.Length; i++)
|
||||||
|
{
|
||||||
|
if (buckets[i].Type != type)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == null || Equals(buckets[i].Key, key))
|
||||||
|
{
|
||||||
|
value = buckets[i].Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
END:
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 04f08cefc67d04906946d27eef22afb6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#if UNITY_2021_3_OR_NEWER
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
class FreeList<T> where T : class
|
||||||
|
{
|
||||||
|
public bool IsDisposed => lastIndex == -2;
|
||||||
|
public int Length => lastIndex + 1;
|
||||||
|
|
||||||
|
readonly object gate = new object();
|
||||||
|
T[] values;
|
||||||
|
int lastIndex = -1;
|
||||||
|
|
||||||
|
public FreeList(int initialCapacity)
|
||||||
|
{
|
||||||
|
values = new T[initialCapacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NETSTANDARD2_1
|
||||||
|
public ReadOnlySpan<T> AsSpan()
|
||||||
|
{
|
||||||
|
if (lastIndex < 0)
|
||||||
|
{
|
||||||
|
return ReadOnlySpan<T>.Empty;
|
||||||
|
}
|
||||||
|
return values.AsSpan(0, lastIndex + 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public T this[int index] => values[index];
|
||||||
|
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
CheckDispose();
|
||||||
|
|
||||||
|
// try find blank
|
||||||
|
var index = FindNullIndex(values);
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
// full, 1, 4, 6,...resize(x1.5)
|
||||||
|
var len = values.Length;
|
||||||
|
var newValues = new T[len + len / 2];
|
||||||
|
Array.Copy(values, newValues, len);
|
||||||
|
values = newValues;
|
||||||
|
index = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
values[index] = item;
|
||||||
|
if (lastIndex < index)
|
||||||
|
{
|
||||||
|
lastIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
if (index < values.Length)
|
||||||
|
{
|
||||||
|
ref var v = ref values[index];
|
||||||
|
if (v == null) throw new KeyNotFoundException($"key index {index} is not found.");
|
||||||
|
|
||||||
|
v = null;
|
||||||
|
if (index == lastIndex)
|
||||||
|
{
|
||||||
|
lastIndex = FindLastNonNullIndex(values, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(T value)
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
if (lastIndex < 0) return false;
|
||||||
|
|
||||||
|
var index = -1;
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (values[i] == value)
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
RemoveAt(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
if (lastIndex > 0)
|
||||||
|
{
|
||||||
|
Array.Clear(values, 0, lastIndex + 1);
|
||||||
|
lastIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
lastIndex = -2; // -2 is disposed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckDispose()
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(GetType().FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2021_3_OR_NEWER
|
||||||
|
static unsafe int FindNullIndex(T[] target)
|
||||||
|
{
|
||||||
|
ref var head = ref UnsafeUtility.As<T, IntPtr>(ref MemoryMarshal.GetReference(target.AsSpan()));
|
||||||
|
fixed (void* p = &head)
|
||||||
|
{
|
||||||
|
var span = new ReadOnlySpan<IntPtr>(p, target.Length);
|
||||||
|
|
||||||
|
#if NETSTANDARD2_1
|
||||||
|
return span.IndexOf(IntPtr.Zero);
|
||||||
|
#else
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
if (span[i] == IntPtr.Zero) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsafe int FindLastNonNullIndex(T[] target, int lastIndex)
|
||||||
|
{
|
||||||
|
ref var head = ref UnsafeUtility.As<T, IntPtr>(ref MemoryMarshal.GetReference(target.AsSpan()));
|
||||||
|
fixed (void* p = &head)
|
||||||
|
{
|
||||||
|
var span = new ReadOnlySpan<IntPtr>(p, lastIndex); // without lastIndexed value.
|
||||||
|
|
||||||
|
for (var i = span.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (span[i] != IntPtr.Zero) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int FindNullIndex(T[] target)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < target.Length; i++)
|
||||||
|
{
|
||||||
|
if (target[i] == null) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FindLastNonNullIndex(T[] target, int lastIndex)
|
||||||
|
{
|
||||||
|
for (var i = lastIndex; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (target[i] != null) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ab65b7c977d342df8c7b3d914e896b30
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
sealed class FuncRegistrationBuilder : RegistrationBuilder
|
||||||
|
{
|
||||||
|
readonly Func<IObjectResolver, object> implementationProvider;
|
||||||
|
|
||||||
|
public FuncRegistrationBuilder(
|
||||||
|
Func<IObjectResolver, object> implementationProvider,
|
||||||
|
Type implementationType,
|
||||||
|
Lifetime lifetime) : base(implementationType, lifetime)
|
||||||
|
{
|
||||||
|
this.implementationProvider = implementationProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Registration Build()
|
||||||
|
{
|
||||||
|
var spawner = new FuncInstanceProvider(implementationProvider);
|
||||||
|
return new Registration(ImplementationType, Lifetime, InterfaceTypes, spawner, Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 959eabb8a5cb4f198c7e96512ea177b8
|
||||||
|
timeCreated: 1619866780
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
sealed class TypedParameter : IInjectParameter
|
||||||
|
{
|
||||||
|
public readonly Type Type;
|
||||||
|
public readonly object Value;
|
||||||
|
|
||||||
|
public TypedParameter(Type type, object value)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(Type parameterType, string _) => parameterType == Type;
|
||||||
|
|
||||||
|
public object GetValue(IObjectResolver _)
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class FuncTypedParameter : IInjectParameter
|
||||||
|
{
|
||||||
|
public readonly Type Type;
|
||||||
|
public readonly Func<IObjectResolver, object> Func;
|
||||||
|
|
||||||
|
public FuncTypedParameter(Type type, Func<IObjectResolver, object> func)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(Type parameterType, string _) => parameterType == Type;
|
||||||
|
|
||||||
|
public object GetValue(IObjectResolver resolver)
|
||||||
|
{
|
||||||
|
return Func(resolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class NamedParameter : IInjectParameter
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly object Value;
|
||||||
|
|
||||||
|
public NamedParameter(string name, object value)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(Type _, string parameterName) => parameterName == Name;
|
||||||
|
|
||||||
|
public object GetValue(IObjectResolver _)
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class FuncNamedParameter : IInjectParameter
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly Func<IObjectResolver, object> Func;
|
||||||
|
|
||||||
|
public FuncNamedParameter(string name, Func<IObjectResolver, object> func)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(Type _, string parameterName) => parameterName == Name;
|
||||||
|
|
||||||
|
public object GetValue(IObjectResolver resolver)
|
||||||
|
{
|
||||||
|
return Func(resolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dbed645399934dceb7271819378c67fe
|
||||||
|
timeCreated: 1593003624
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
public static class InjectorCache
|
||||||
|
{
|
||||||
|
static readonly ConcurrentDictionary<Type, IInjector> Injectors = new ConcurrentDictionary<Type, IInjector>();
|
||||||
|
|
||||||
|
public static IInjector GetOrBuild(Type type)
|
||||||
|
{
|
||||||
|
return Injectors.GetOrAdd(type, key =>
|
||||||
|
{
|
||||||
|
// SourceGenerator
|
||||||
|
var generatedType = key.Assembly.GetType($"{key.FullName}GeneratedInjector", false);
|
||||||
|
if (generatedType != null)
|
||||||
|
{
|
||||||
|
return (IInjector)Activator.CreateInstance(generatedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IL weaving (Deprecated)
|
||||||
|
var getter = key.GetMethod("__GetGeneratedInjector", BindingFlags.Static | BindingFlags.Public);
|
||||||
|
if (getter != null)
|
||||||
|
{
|
||||||
|
return (IInjector)getter.Invoke(null, null);
|
||||||
|
}
|
||||||
|
return ReflectionInjector.Build(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9868d07fc52841ac9aaef692536402e6
|
||||||
|
timeCreated: 1595675733
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 975a095af910460bb016e0cc5cf9eb07
|
||||||
|
timeCreated: 1637894696
|
||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
struct RegistrationElement
|
||||||
|
{
|
||||||
|
public Registration Registration;
|
||||||
|
public IObjectResolver RegisteredContainer;
|
||||||
|
|
||||||
|
public RegistrationElement(Registration registration, IObjectResolver registeredContainer)
|
||||||
|
{
|
||||||
|
Registration = registration;
|
||||||
|
RegisteredContainer = registeredContainer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class CollectionInstanceProvider : IInstanceProvider, IEnumerable<Registration>
|
||||||
|
{
|
||||||
|
public static bool Match(Type openGenericType) => openGenericType == typeof(IEnumerable<>) ||
|
||||||
|
openGenericType == typeof(IReadOnlyList<>);
|
||||||
|
|
||||||
|
public List<Registration>.Enumerator GetEnumerator() => registrations.GetEnumerator();
|
||||||
|
IEnumerator<Registration> IEnumerable<Registration>.GetEnumerator() => GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public Type ImplementationType { get; }
|
||||||
|
public IReadOnlyList<Type> InterfaceTypes => interfaceTypes;
|
||||||
|
public Lifetime Lifetime => Lifetime.Transient; // Collection reference is transient. So its members can have each lifetimes.
|
||||||
|
|
||||||
|
public Type ElementType { get; }
|
||||||
|
|
||||||
|
readonly List<Type> interfaceTypes;
|
||||||
|
readonly List<Registration> registrations = new List<Registration>();
|
||||||
|
|
||||||
|
public CollectionInstanceProvider(Type elementType)
|
||||||
|
{
|
||||||
|
ElementType = elementType;
|
||||||
|
ImplementationType = elementType.MakeArrayType();
|
||||||
|
interfaceTypes = new List<Type>
|
||||||
|
{
|
||||||
|
RuntimeTypeCache.EnumerableTypeOf(elementType),
|
||||||
|
RuntimeTypeCache.ReadOnlyListTypeOf(elementType),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var contractTypes = InterfaceTypes != null ? string.Join(", ", InterfaceTypes) : "";
|
||||||
|
return $"CollectionRegistration {ImplementationType} ContractTypes=[{contractTypes}] {Lifetime}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Registration registration)
|
||||||
|
{
|
||||||
|
foreach (var x in registrations)
|
||||||
|
{
|
||||||
|
if (x.Lifetime == Lifetime.Singleton && x.ImplementationType == registration.ImplementationType && x.Key == registration.Key)
|
||||||
|
{
|
||||||
|
throw new VContainerException(registration.ImplementationType, $"Conflict implementation type : {registration}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registrations.Add(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public object SpawnInstance(IObjectResolver resolver)
|
||||||
|
{
|
||||||
|
if (resolver is IScopedObjectResolver scope)
|
||||||
|
{
|
||||||
|
using (ListPool<RegistrationElement>.Get(out var entirelyRegistrations))
|
||||||
|
{
|
||||||
|
CollectFromParentScopes(scope, entirelyRegistrations);
|
||||||
|
return SpawnInstance(resolver, entirelyRegistrations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var array = Array.CreateInstance(ElementType, registrations.Count);
|
||||||
|
for (var i = 0; i < registrations.Count; i++)
|
||||||
|
{
|
||||||
|
array.SetValue(resolver.Resolve(registrations[i]), i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object SpawnInstance(IObjectResolver currentScope, IReadOnlyList<RegistrationElement> entirelyRegistrations)
|
||||||
|
{
|
||||||
|
var array = Array.CreateInstance(ElementType, entirelyRegistrations.Count);
|
||||||
|
for (var i = 0; i < entirelyRegistrations.Count; i++)
|
||||||
|
{
|
||||||
|
var x = entirelyRegistrations[i];
|
||||||
|
var resolver = x.Registration.Lifetime == Lifetime.Singleton
|
||||||
|
? x.RegisteredContainer
|
||||||
|
: currentScope;
|
||||||
|
array.SetValue(resolver.Resolve(x.Registration), i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CollectFromParentScopes(
|
||||||
|
IScopedObjectResolver scope,
|
||||||
|
List<RegistrationElement> registrationsBuffer,
|
||||||
|
bool localScopeOnly = false)
|
||||||
|
{
|
||||||
|
foreach (var registration in registrations)
|
||||||
|
{
|
||||||
|
registrationsBuffer.Add(new RegistrationElement(registration, scope));
|
||||||
|
}
|
||||||
|
|
||||||
|
var finderType = InterfaceTypes[0];
|
||||||
|
scope = scope.Parent;
|
||||||
|
|
||||||
|
while (scope != null)
|
||||||
|
{
|
||||||
|
if (scope.TryGetRegistration(finderType, out var registration) &&
|
||||||
|
registration.Provider is CollectionInstanceProvider parentCollection)
|
||||||
|
{
|
||||||
|
foreach (var x in parentCollection.registrations)
|
||||||
|
{
|
||||||
|
if (!localScopeOnly || x.Lifetime != Lifetime.Singleton)
|
||||||
|
{
|
||||||
|
registrationsBuffer.Add(new RegistrationElement(x, scope));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope = scope.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 24bcedcfe70849f4b609a3c695bee2da
|
||||||
|
timeCreated: 1637460174
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
sealed class ContainerInstanceProvider : IInstanceProvider
|
||||||
|
{
|
||||||
|
public static readonly ContainerInstanceProvider Default = new ContainerInstanceProvider();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public object SpawnInstance(IObjectResolver resolver) => resolver;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5fee000f6274ce19a7ee65eb42604e7
|
||||||
|
timeCreated: 1637894705
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VContainer.Internal
|
||||||
|
{
|
||||||
|
sealed class ContainerLocalInstanceProvider : IInstanceProvider
|
||||||
|
{
|
||||||
|
readonly Type wrappedType;
|
||||||
|
readonly Registration valueRegistration;
|
||||||
|
|
||||||
|
public ContainerLocalInstanceProvider(Type wrappedType, Registration valueRegistration)
|
||||||
|
{
|
||||||
|
this.wrappedType = wrappedType;
|
||||||
|
this.valueRegistration = valueRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SpawnInstance(IObjectResolver resolver)
|
||||||
|
{
|
||||||
|
object value;
|
||||||
|
|
||||||
|
if (resolver is ScopedContainer scope &&
|
||||||
|
valueRegistration.Provider is CollectionInstanceProvider collectionProvider)
|
||||||
|
{
|
||||||
|
using (ListPool<RegistrationElement>.Get(out var entirelyRegistrations))
|
||||||
|
{
|
||||||
|
collectionProvider.CollectFromParentScopes(scope, entirelyRegistrations, localScopeOnly: true);
|
||||||
|
value = collectionProvider.SpawnInstance(scope, entirelyRegistrations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = resolver.Resolve(valueRegistration);
|
||||||
|
}
|
||||||
|
var parameterValues = CappedArrayPool<object>.Shared8Limit.Rent(1);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
parameterValues[0] = value;
|
||||||
|
return Activator.CreateInstance(wrappedType, parameterValues);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CappedArrayPool<object>.Shared8Limit.Return(parameterValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user