Initial commit: Unity WordConnect project
This commit is contained in:
@ -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,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
// http://neue.cc/2017/07/11_555.html
|
||||
sealed class FixedTypeKeyHashtable<TValue>
|
||||
{
|
||||
readonly struct HashEntry
|
||||
{
|
||||
public readonly Type Type;
|
||||
public readonly TValue Value;
|
||||
|
||||
public HashEntry(Type key, TValue value)
|
||||
{
|
||||
Type = key;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
readonly HashEntry[][] table;
|
||||
readonly int indexFor;
|
||||
|
||||
public FixedTypeKeyHashtable(KeyValuePair<Type, TValue>[] values, float loadFactor = 0.75f)
|
||||
{
|
||||
var initialCapacity = (int)((float)values.Length / loadFactor);
|
||||
|
||||
// make power of 2(and use mask)
|
||||
// see: Hashing https://en.wikipedia.org/wiki/Hash_table
|
||||
var capacity = 1;
|
||||
while (capacity < initialCapacity)
|
||||
{
|
||||
capacity <<= 1;
|
||||
}
|
||||
|
||||
table = new HashEntry[(int)capacity][];
|
||||
indexFor = table.Length - 1;
|
||||
|
||||
foreach (var item in values)
|
||||
{
|
||||
var hash = RuntimeHelpers.GetHashCode(item.Key);
|
||||
var array = table[hash & indexFor];
|
||||
if (array == null)
|
||||
{
|
||||
array = new HashEntry[1];
|
||||
array[0] = new HashEntry(item.Key, 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, item.Value);
|
||||
}
|
||||
|
||||
table[hash & indexFor] = array;
|
||||
}
|
||||
}
|
||||
|
||||
public TValue Get(Type type)
|
||||
{
|
||||
var hashCode = RuntimeHelpers.GetHashCode(type);
|
||||
var buckets = table[hashCode & indexFor];
|
||||
|
||||
if (buckets == null) goto ERROR;
|
||||
|
||||
if (buckets[0].Type == type)
|
||||
{
|
||||
return buckets[0].Value;
|
||||
}
|
||||
|
||||
for (int i = 1; i < buckets.Length; i++)
|
||||
{
|
||||
if (buckets[i].Type == type)
|
||||
{
|
||||
return buckets[i].Value;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR:
|
||||
throw new KeyNotFoundException("Type was not found, Type: " + type.FullName);
|
||||
}
|
||||
|
||||
public bool TryGet(Type type, out TValue value)
|
||||
{
|
||||
var hashCode = RuntimeHelpers.GetHashCode(type);
|
||||
var buckets = table[hashCode & indexFor];
|
||||
|
||||
if (buckets == null) goto END;
|
||||
|
||||
if (buckets[0].Type == type)
|
||||
{
|
||||
value = buckets[0].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 1; i < buckets.Length; i++)
|
||||
{
|
||||
if (buckets[i].Type == type)
|
||||
{
|
||||
value = buckets[i].Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
END:
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d640b33996034830abfbef71b7f8e440
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24bcedcfe70849f4b609a3c695bee2da
|
||||
timeCreated: 1637460174
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5fee000f6274ce19a7ee65eb42604e7
|
||||
timeCreated: 1637894705
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67ec193044734d97aa59126a9cb83398
|
||||
timeCreated: 1637461669
|
||||
@ -0,0 +1,17 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
sealed class ExistingInstanceProvider : IInstanceProvider
|
||||
{
|
||||
readonly object implementationInstance;
|
||||
|
||||
public ExistingInstanceProvider(object implementationInstance)
|
||||
{
|
||||
this.implementationInstance = implementationInstance;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public object SpawnInstance(IObjectResolver resolver) => implementationInstance;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fcef74c11fc4018bbc88bc395ef8657
|
||||
timeCreated: 1619863673
|
||||
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
sealed class FuncInstanceProvider : IInstanceProvider
|
||||
{
|
||||
readonly Func<IObjectResolver, object> implementationProvider;
|
||||
|
||||
public FuncInstanceProvider(Func<IObjectResolver, object> implementationProvider)
|
||||
{
|
||||
this.implementationProvider = implementationProvider;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public object SpawnInstance(IObjectResolver resolver) => implementationProvider(resolver);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4fb0cd9294e424bb69b28711f95ccc0
|
||||
timeCreated: 1619867067
|
||||
@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
sealed class InstanceProvider : IInstanceProvider
|
||||
{
|
||||
readonly IInjector injector;
|
||||
readonly IReadOnlyList<IInjectParameter> customParameters;
|
||||
|
||||
public InstanceProvider(
|
||||
IInjector injector,
|
||||
IReadOnlyList<IInjectParameter> customParameters = null)
|
||||
{
|
||||
this.injector = injector;
|
||||
this.customParameters = customParameters;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public object SpawnInstance(IObjectResolver resolver)
|
||||
=> injector.CreateInstance(resolver, customParameters);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7f72f5485924ec19d7761e215e23a16
|
||||
timeCreated: 1637903141
|
||||
@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
public class OpenGenericInstanceProvider : IInstanceProvider
|
||||
{
|
||||
class TypeParametersEqualityComparer : IEqualityComparer<Type[]>
|
||||
{
|
||||
public bool Equals(Type[] x, Type[] y)
|
||||
{
|
||||
if (x == null || y == null) return x == y;
|
||||
if (x.Length != y.Length) return false;
|
||||
|
||||
for (var i = 0; i < x.Length; i++)
|
||||
{
|
||||
if (x[i] != y[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetHashCode(Type[] typeParameters)
|
||||
{
|
||||
var hash = 5381;
|
||||
foreach (var typeParameter in typeParameters)
|
||||
{
|
||||
hash = ((hash << 5) + hash) ^ typeParameter.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
readonly Lifetime lifetime;
|
||||
readonly Type implementationType;
|
||||
readonly IReadOnlyList<IInjectParameter> customParameters;
|
||||
|
||||
readonly ConcurrentDictionary<Type[], Registration> constructedRegistrations = new ConcurrentDictionary<Type[], Registration>(new TypeParametersEqualityComparer());
|
||||
readonly Func<Type[], Registration> createRegistrationFunc;
|
||||
|
||||
public OpenGenericInstanceProvider(Type implementationType, Lifetime lifetime, List<IInjectParameter> injectParameters)
|
||||
{
|
||||
this.implementationType = implementationType;
|
||||
this.lifetime = lifetime;
|
||||
customParameters = injectParameters;
|
||||
createRegistrationFunc = CreateRegistration;
|
||||
}
|
||||
|
||||
public Registration GetClosedRegistration(Type closedInterfaceType, Type[] typeParameters)
|
||||
{
|
||||
return constructedRegistrations.GetOrAdd(typeParameters, createRegistrationFunc);
|
||||
}
|
||||
|
||||
Registration CreateRegistration(Type[] typeParameters)
|
||||
{
|
||||
var newType = implementationType.MakeGenericType(typeParameters);
|
||||
var injector = InjectorCache.GetOrBuild(newType);
|
||||
var spawner = new InstanceProvider(injector, customParameters);
|
||||
return new Registration(newType, lifetime, new List<Type>(1) { newType }, spawner);
|
||||
}
|
||||
|
||||
public object SpawnInstance(IObjectResolver resolver)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 927f2cb4e816642cba170d66ffc1fcb9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,22 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
sealed class InstanceRegistrationBuilder : RegistrationBuilder
|
||||
{
|
||||
readonly object implementationInstance;
|
||||
|
||||
public InstanceRegistrationBuilder(object implementationInstance)
|
||||
: base(implementationInstance.GetType(), Lifetime.Singleton)
|
||||
{
|
||||
this.implementationInstance = implementationInstance;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override Registration Build()
|
||||
{
|
||||
var spawner = new ExistingInstanceProvider(implementationInstance);
|
||||
return new Registration(ImplementationType, Lifetime, InterfaceTypes, spawner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 097b02c25ccf47df97386afb6a4f0c21
|
||||
timeCreated: 1619863604
|
||||
@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
internal static class ListPool<T>
|
||||
{
|
||||
const int DefaultCapacity = 32;
|
||||
|
||||
private static readonly Stack<List<T>> _pool = new Stack<List<T>>(4);
|
||||
|
||||
/// <summary>
|
||||
/// BufferScope supports releasing a buffer with using clause.
|
||||
/// </summary>
|
||||
internal readonly struct BufferScope : IDisposable
|
||||
{
|
||||
private readonly List<T> _buffer;
|
||||
|
||||
public BufferScope(List<T> buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Release(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a buffer from the pool.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static List<T> Get()
|
||||
{
|
||||
lock (_pool)
|
||||
{
|
||||
if (_pool.Count == 0)
|
||||
{
|
||||
return new List<T>(DefaultCapacity);
|
||||
}
|
||||
|
||||
return _pool.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a buffer from the pool. Returning a disposable struct to support recycling via using clause.
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
internal static BufferScope Get(out List<T> buffer)
|
||||
{
|
||||
buffer = Get();
|
||||
return new BufferScope(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare a buffer won't be used anymore and put it back to the pool.
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
internal static void Release(List<T> buffer)
|
||||
{
|
||||
buffer.Clear();
|
||||
lock (_pool)
|
||||
{
|
||||
_pool.Push(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da1a74df3eab442e81879a0b2e6fac0e
|
||||
timeCreated: 1606668004
|
||||
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
public class OpenGenericRegistrationBuilder : RegistrationBuilder
|
||||
{
|
||||
public OpenGenericRegistrationBuilder(Type implementationType, Lifetime lifetime)
|
||||
: base(implementationType, lifetime)
|
||||
{
|
||||
if (!implementationType.IsGenericType || implementationType.IsConstructedGenericType)
|
||||
throw new VContainerException(implementationType, "Type is not open generic type.");
|
||||
}
|
||||
|
||||
public override Registration Build()
|
||||
{
|
||||
var provider = new OpenGenericInstanceProvider(ImplementationType, Lifetime, Parameters);
|
||||
return new Registration(ImplementationType, Lifetime, InterfaceTypes, provider);
|
||||
}
|
||||
|
||||
public override RegistrationBuilder AsImplementedInterfaces()
|
||||
{
|
||||
InterfaceTypes = InterfaceTypes ?? new List<Type>();
|
||||
foreach (var i in ImplementationType.GetInterfaces())
|
||||
{
|
||||
if (!i.IsGenericType)
|
||||
continue;
|
||||
|
||||
InterfaceTypes.Add(i.GetGenericTypeDefinition());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected override void AddInterfaceType(Type interfaceType)
|
||||
{
|
||||
if (interfaceType.IsConstructedGenericType)
|
||||
throw new VContainerException(interfaceType, "Type is not open generic type.");
|
||||
|
||||
foreach (var i in ImplementationType.GetInterfaces())
|
||||
{
|
||||
if (!i.IsGenericType || i.GetGenericTypeDefinition() != interfaceType)
|
||||
continue;
|
||||
|
||||
if (InterfaceTypes is null)
|
||||
{
|
||||
InterfaceTypes = new List<Type>();
|
||||
}
|
||||
|
||||
if (!InterfaceTypes.Contains(interfaceType))
|
||||
InterfaceTypes.Add(interfaceType);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
base.AddInterfaceType(interfaceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41ece9cea6ed8424db8850c90169cb5e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
sealed class ReflectionInjector : IInjector
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReflectionInjector Build(Type type)
|
||||
{
|
||||
var injectTypeInfo = TypeAnalyzer.AnalyzeWithCache(type);
|
||||
return new ReflectionInjector(injectTypeInfo);
|
||||
}
|
||||
|
||||
readonly InjectTypeInfo injectTypeInfo;
|
||||
|
||||
ReflectionInjector(InjectTypeInfo injectTypeInfo)
|
||||
{
|
||||
this.injectTypeInfo = injectTypeInfo;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Inject(object instance, IObjectResolver resolver, IReadOnlyList<IInjectParameter> parameters)
|
||||
{
|
||||
InjectFields(instance, resolver, parameters);
|
||||
InjectProperties(instance, resolver, parameters);
|
||||
InjectMethods(instance, resolver, parameters);
|
||||
}
|
||||
|
||||
public object CreateInstance(IObjectResolver resolver, IReadOnlyList<IInjectParameter> parameters)
|
||||
{
|
||||
var parameterInfos = injectTypeInfo.InjectConstructor.ParameterInfos;
|
||||
var parameterValues = CappedArrayPool<object>.Shared8Limit.Rent(parameterInfos.Length);
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < parameterInfos.Length; i++)
|
||||
{
|
||||
var parameterInfo = parameterInfos[i];
|
||||
parameterValues[i] = resolver.ResolveOrParameter(
|
||||
parameterInfo.ParameterType,
|
||||
parameterInfo.Name,
|
||||
parameters);
|
||||
}
|
||||
var instance = injectTypeInfo.InjectConstructor.ConstructorInfo.Invoke(parameterValues);
|
||||
Inject(instance, resolver, parameters);
|
||||
return instance;
|
||||
}
|
||||
catch (VContainerException ex)
|
||||
{
|
||||
throw new VContainerException(ex.InvalidType, $"Failed to resolve {injectTypeInfo.Type} : {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
CappedArrayPool<object>.Shared8Limit.Return(parameterValues);
|
||||
}
|
||||
}
|
||||
|
||||
void InjectFields(object obj, IObjectResolver resolver, IReadOnlyList<IInjectParameter> parameters)
|
||||
{
|
||||
if (injectTypeInfo.InjectFields == null)
|
||||
return;
|
||||
|
||||
foreach (var x in injectTypeInfo.InjectFields)
|
||||
{
|
||||
var fieldValue = resolver.ResolveOrParameter(x.FieldType, x.Name, parameters);
|
||||
x.SetValue(obj, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
void InjectProperties(object obj, IObjectResolver resolver, IReadOnlyList<IInjectParameter> parameters)
|
||||
{
|
||||
if (injectTypeInfo.InjectProperties == null)
|
||||
return;
|
||||
|
||||
foreach (var x in injectTypeInfo.InjectProperties)
|
||||
{
|
||||
var propValue = resolver.ResolveOrParameter(x.PropertyType, x.Name, parameters);
|
||||
x.SetValue(obj, propValue);
|
||||
}
|
||||
}
|
||||
|
||||
void InjectMethods(object obj, IObjectResolver resolver, IReadOnlyList<IInjectParameter> parameters)
|
||||
{
|
||||
if (injectTypeInfo.InjectMethods == null)
|
||||
return;
|
||||
|
||||
foreach (var method in injectTypeInfo.InjectMethods)
|
||||
{
|
||||
var parameterInfos = method.ParameterInfos;
|
||||
var parameterValues = CappedArrayPool<object>.Shared8Limit.Rent(parameterInfos.Length);
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < parameterInfos.Length; i++)
|
||||
{
|
||||
var parameterInfo = parameterInfos[i];
|
||||
parameterValues[i] = resolver.ResolveOrParameter(
|
||||
parameterInfo.ParameterType,
|
||||
parameterInfo.Name,
|
||||
parameters);
|
||||
}
|
||||
method.MethodInfo.Invoke(obj, parameterValues);
|
||||
}
|
||||
catch (VContainerException ex)
|
||||
{
|
||||
throw new VContainerException(ex.InvalidType, $"Failed to resolve {injectTypeInfo.Type} : {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
CappedArrayPool<object>.Shared8Limit.Return(parameterValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49d591b36bd7b4319a86f326ba19fed0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
static class RuntimeTypeCache
|
||||
{
|
||||
static readonly ConcurrentDictionary<Type, Type> OpenGenericTypes = new ConcurrentDictionary<Type, Type>();
|
||||
static readonly ConcurrentDictionary<Type, Type[]> GenericTypeParameters = new ConcurrentDictionary<Type, Type[]>();
|
||||
static readonly ConcurrentDictionary<Type, Type> ArrayTypes = new ConcurrentDictionary<Type, Type>();
|
||||
static readonly ConcurrentDictionary<Type, Type> EnumerableTypes = new ConcurrentDictionary<Type, Type>();
|
||||
static readonly ConcurrentDictionary<Type, Type> ReadOnlyListTypes = new ConcurrentDictionary<Type, Type>();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Type OpenGenericTypeOf(Type closedGenericType)
|
||||
=> OpenGenericTypes.GetOrAdd(closedGenericType, key => key.GetGenericTypeDefinition());
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Type[] GenericTypeParametersOf(Type closedGenericType)
|
||||
=> GenericTypeParameters.GetOrAdd(closedGenericType, key => key.GetGenericArguments());
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Type ArrayTypeOf(Type elementType)
|
||||
=> ArrayTypes.GetOrAdd(elementType, key => key.MakeArrayType());
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Type EnumerableTypeOf(Type elementType)
|
||||
=> EnumerableTypes.GetOrAdd(elementType, key => typeof(IEnumerable<>).MakeGenericType(key));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Type ReadOnlyListTypeOf(Type elementType)
|
||||
=> ReadOnlyListTypes.GetOrAdd(elementType, key => typeof(IReadOnlyList<>).MakeGenericType(key));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9be1b596e081421dbb84a84fb2ec17fe
|
||||
timeCreated: 1637461795
|
||||
@ -0,0 +1,408 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
sealed class InjectConstructorInfo
|
||||
{
|
||||
public readonly ConstructorInfo ConstructorInfo;
|
||||
public readonly ParameterInfo[] ParameterInfos;
|
||||
|
||||
public InjectConstructorInfo(ConstructorInfo constructorInfo)
|
||||
{
|
||||
ConstructorInfo = constructorInfo;
|
||||
ParameterInfos = constructorInfo.GetParameters();
|
||||
}
|
||||
|
||||
public InjectConstructorInfo(ConstructorInfo constructorInfo, ParameterInfo[] parameterInfos)
|
||||
{
|
||||
ConstructorInfo = constructorInfo;
|
||||
ParameterInfos = parameterInfos;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class InjectMethodInfo
|
||||
{
|
||||
public readonly MethodInfo MethodInfo;
|
||||
public readonly ParameterInfo[] ParameterInfos;
|
||||
|
||||
public InjectMethodInfo(MethodInfo methodInfo)
|
||||
{
|
||||
MethodInfo = methodInfo;
|
||||
ParameterInfos = methodInfo.GetParameters();
|
||||
}
|
||||
}
|
||||
|
||||
// sealed class InjectFieldInfo
|
||||
// {
|
||||
// public readonly Type FieldType;
|
||||
// public readonly Action<object, object> Setter;
|
||||
//
|
||||
// public InjectFieldInfo(FieldInfo fieldInfo)
|
||||
// {
|
||||
// FieldType = fieldInfo.FieldType;
|
||||
// Setter = fieldInfo.SetValue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sealed class InjectPropertyInfo
|
||||
// {
|
||||
// public readonly Type PropertyType;
|
||||
// public readonly Action<object, object> Setter;
|
||||
//
|
||||
// public InjectPropertyInfo(PropertyInfo propertyInfo)
|
||||
// {
|
||||
// PropertyType = propertyInfo.PropertyType;
|
||||
// Setter = propertyInfo.SetValue;
|
||||
// }
|
||||
// }
|
||||
|
||||
sealed class InjectTypeInfo
|
||||
{
|
||||
public readonly Type Type;
|
||||
public readonly InjectConstructorInfo InjectConstructor;
|
||||
public readonly IReadOnlyList<InjectMethodInfo> InjectMethods;
|
||||
public readonly IReadOnlyList<FieldInfo> InjectFields;
|
||||
public readonly IReadOnlyList<PropertyInfo> InjectProperties;
|
||||
|
||||
public InjectTypeInfo(
|
||||
Type type,
|
||||
InjectConstructorInfo injectConstructor,
|
||||
IReadOnlyList<InjectMethodInfo> injectMethods,
|
||||
IReadOnlyList<FieldInfo> injectFields,
|
||||
IReadOnlyList<PropertyInfo> injectProperties)
|
||||
{
|
||||
Type = type;
|
||||
InjectConstructor = injectConstructor;
|
||||
InjectFields = injectFields;
|
||||
InjectProperties = injectProperties;
|
||||
InjectMethods = injectMethods;
|
||||
}
|
||||
}
|
||||
|
||||
readonly struct DependencyInfo
|
||||
{
|
||||
public Type ImplementationType => Dependency.ImplementationType;
|
||||
public IInstanceProvider Provider => Dependency.Provider;
|
||||
|
||||
public readonly Registration Dependency;
|
||||
readonly Registration owner;
|
||||
readonly object method; // ctor or method
|
||||
readonly ParameterInfo param; // param or field or prop
|
||||
|
||||
public DependencyInfo(Registration dependency)
|
||||
{
|
||||
Dependency = dependency;
|
||||
owner = null;
|
||||
method = null;
|
||||
param = null;
|
||||
}
|
||||
|
||||
public DependencyInfo(Registration dependency, Registration owner, ConstructorInfo ctor, ParameterInfo param)
|
||||
{
|
||||
Dependency = dependency;
|
||||
this.owner = owner;
|
||||
method = ctor;
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public DependencyInfo(Registration dependency, Registration owner, MethodInfo method, ParameterInfo param)
|
||||
{
|
||||
Dependency = dependency;
|
||||
this.owner = owner;
|
||||
this.method = method;
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public DependencyInfo(Registration dependency, Registration owner, FieldInfo field)
|
||||
{
|
||||
Dependency = dependency;
|
||||
this.owner = owner;
|
||||
method = field;
|
||||
param = null;
|
||||
}
|
||||
|
||||
public DependencyInfo(Registration dependency, Registration owner, PropertyInfo prop)
|
||||
{
|
||||
Dependency = dependency;
|
||||
this.owner = owner;
|
||||
method = prop;
|
||||
param = null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case ConstructorInfo _:
|
||||
return $"{owner.ImplementationType}..ctor({param.Name})";
|
||||
case MethodInfo methodInfo:
|
||||
return $"{owner.ImplementationType.FullName}.{methodInfo.Name}({param.Name})";
|
||||
case FieldInfo field:
|
||||
return $"{owner.ImplementationType.FullName}.{field.Name}";
|
||||
case PropertyInfo prop:
|
||||
return $"{owner.ImplementationType.FullName}.{prop.Name}";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TypeAnalyzer
|
||||
{
|
||||
public static InjectTypeInfo AnalyzeWithCache(Type type) => Cache.GetOrAdd(type, AnalyzeFunc);
|
||||
|
||||
static readonly ConcurrentDictionary<Type, InjectTypeInfo> Cache = new ConcurrentDictionary<Type, InjectTypeInfo>();
|
||||
|
||||
[ThreadStatic]
|
||||
static Stack<DependencyInfo> circularDependencyChecker;
|
||||
|
||||
static readonly Func<Type, InjectTypeInfo> AnalyzeFunc = Analyze;
|
||||
|
||||
public static InjectTypeInfo Analyze(Type type)
|
||||
{
|
||||
var injectConstructor = default(InjectConstructorInfo);
|
||||
var analyzedType = type;
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
|
||||
// Constructor, single [Inject] constructor -> single most parameters constructor
|
||||
var annotatedConstructorCount = 0;
|
||||
var maxParameters = -1;
|
||||
foreach (var constructorInfo in typeInfo.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
if (constructorInfo.IsDefined(typeof(InjectAttribute), false))
|
||||
{
|
||||
if (++annotatedConstructorCount > 1)
|
||||
{
|
||||
throw new VContainerException(type, $"Type found multiple [Inject] marked constructors, type: {type.Name}");
|
||||
}
|
||||
injectConstructor = new InjectConstructorInfo(constructorInfo);
|
||||
}
|
||||
else if (annotatedConstructorCount <= 0)
|
||||
{
|
||||
var parameterInfos = constructorInfo.GetParameters();
|
||||
if (parameterInfos.Length > maxParameters)
|
||||
{
|
||||
injectConstructor = new InjectConstructorInfo(constructorInfo, parameterInfos);
|
||||
maxParameters = parameterInfos.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (injectConstructor == null)
|
||||
{
|
||||
var allowNoConstructor = type.IsEnum;
|
||||
#if UNITY_2018_4_OR_NEWER
|
||||
// It seems that Unity sometimes strips the constructor of Component at build time.
|
||||
// In that case, allow null.
|
||||
allowNoConstructor |= type.IsSubclassOf(typeof(UnityEngine.Component));
|
||||
#endif
|
||||
if (!allowNoConstructor)
|
||||
throw new VContainerException(type, $"Type does not found injectable constructor, type: {type.Name}");
|
||||
}
|
||||
|
||||
var injectMethods = default(List<InjectMethodInfo>);
|
||||
var injectFields = default(List<FieldInfo>);
|
||||
var injectProperties = default(List<PropertyInfo>);
|
||||
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
|
||||
while (type != null && type != typeof(object))
|
||||
{
|
||||
// Methods, [Inject] Only
|
||||
var methods = type.GetMethods(bindingFlags);
|
||||
foreach (var methodInfo in methods)
|
||||
{
|
||||
if (methodInfo.IsDefined(typeof(InjectAttribute), false))
|
||||
{
|
||||
if (injectMethods == null)
|
||||
{
|
||||
injectMethods = new List<InjectMethodInfo>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip if already exists
|
||||
foreach (var x in injectMethods)
|
||||
{
|
||||
if (x.MethodInfo.GetBaseDefinition() == methodInfo.GetBaseDefinition())
|
||||
goto EndMethod;
|
||||
}
|
||||
}
|
||||
|
||||
injectMethods.Add(new InjectMethodInfo(methodInfo));
|
||||
}
|
||||
}
|
||||
EndMethod:
|
||||
|
||||
// Fields, [Inject] Only
|
||||
var fields = type.GetFields(bindingFlags);
|
||||
foreach (var fieldInfo in fields)
|
||||
{
|
||||
if (fieldInfo.IsDefined(typeof(InjectAttribute), false))
|
||||
{
|
||||
if (injectFields == null)
|
||||
{
|
||||
injectFields = new List<FieldInfo>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Contains(injectFields, fieldInfo))
|
||||
{
|
||||
var message = $"Duplicate injection found for field: {fieldInfo}";
|
||||
throw new VContainerException(type, message);
|
||||
}
|
||||
|
||||
if (injectFields.Any(x => x.Name == fieldInfo.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
injectFields.Add(fieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Properties, [Inject] only
|
||||
var props = type.GetProperties(bindingFlags);
|
||||
foreach (var propertyInfo in props)
|
||||
{
|
||||
if (propertyInfo.IsDefined(typeof(InjectAttribute), false))
|
||||
{
|
||||
if (injectProperties == null)
|
||||
{
|
||||
injectProperties = new List<PropertyInfo>();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var x in injectProperties)
|
||||
{
|
||||
if (x.Name == propertyInfo.Name)
|
||||
goto EndProperty;
|
||||
}
|
||||
}
|
||||
injectProperties.Add(propertyInfo);
|
||||
}
|
||||
}
|
||||
EndProperty:
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return new InjectTypeInfo(
|
||||
analyzedType,
|
||||
injectConstructor,
|
||||
injectMethods,
|
||||
injectFields,
|
||||
injectProperties);
|
||||
}
|
||||
|
||||
private static bool Contains(List<FieldInfo> fields, FieldInfo field)
|
||||
{
|
||||
for (var i = 0; i < fields.Count; i++)
|
||||
{
|
||||
var x = fields[i];
|
||||
if (x.Name == field.Name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void CheckCircularDependency(IReadOnlyList<Registration> registrations, Registry registry)
|
||||
{
|
||||
// ThreadStatic
|
||||
if (circularDependencyChecker == null)
|
||||
circularDependencyChecker = new Stack<DependencyInfo>();
|
||||
|
||||
for (var i = 0; i < registrations.Count; i++)
|
||||
{
|
||||
circularDependencyChecker.Clear();
|
||||
CheckCircularDependencyRecursive(new DependencyInfo(registrations[i]), registry, circularDependencyChecker);
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckCircularDependencyRecursive(DependencyInfo current, Registry registry, Stack<DependencyInfo> stack)
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var dependency in stack)
|
||||
{
|
||||
if (current.ImplementationType == dependency.ImplementationType)
|
||||
{
|
||||
// When instantiated by Func, the abstract type cycle is user-avoidable.
|
||||
if (current.Dependency.Provider is FuncInstanceProvider)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stack.Push(current);
|
||||
|
||||
var path = string.Join("\n",
|
||||
stack.Take(i + 1)
|
||||
.Reverse()
|
||||
.Select((item, itemIndex) => $" [{itemIndex + 1}] {item} --> {item.ImplementationType.FullName}"));
|
||||
throw new VContainerException(current.Dependency.ImplementationType,
|
||||
$"Circular dependency detected!\n{path}");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
stack.Push(current);
|
||||
|
||||
if (Cache.TryGetValue(current.ImplementationType, out var injectTypeInfo))
|
||||
{
|
||||
if (injectTypeInfo.InjectConstructor != null)
|
||||
{
|
||||
foreach (var x in injectTypeInfo.InjectConstructor.ParameterInfos)
|
||||
{
|
||||
if (registry.TryGet(x.ParameterType, out var parameterRegistration))
|
||||
{
|
||||
CheckCircularDependencyRecursive(new DependencyInfo(parameterRegistration, current.Dependency, injectTypeInfo.InjectConstructor.ConstructorInfo, x), registry, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (injectTypeInfo.InjectMethods != null)
|
||||
{
|
||||
foreach (var methodInfo in injectTypeInfo.InjectMethods)
|
||||
{
|
||||
foreach (var x in methodInfo.ParameterInfos)
|
||||
{
|
||||
if (registry.TryGet(x.ParameterType, out var parameterRegistration))
|
||||
{
|
||||
CheckCircularDependencyRecursive(new DependencyInfo(parameterRegistration, current.Dependency, methodInfo.MethodInfo, x), registry, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (injectTypeInfo.InjectFields != null)
|
||||
{
|
||||
foreach (var x in injectTypeInfo.InjectFields)
|
||||
{
|
||||
if (registry.TryGet(x.FieldType, out var fieldRegistration))
|
||||
{
|
||||
CheckCircularDependencyRecursive(new DependencyInfo(fieldRegistration, current.Dependency, x), registry, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (injectTypeInfo.InjectProperties != null)
|
||||
{
|
||||
foreach (var x in injectTypeInfo.InjectProperties)
|
||||
{
|
||||
if (registry.TryGet(x.PropertyType, out var propertyRegistration))
|
||||
{
|
||||
CheckCircularDependencyRecursive(new DependencyInfo(propertyRegistration, current.Dependency, x), registry, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stack.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1039990f285746d39d3d6656ccdaac3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VContainer.Internal
|
||||
{
|
||||
sealed class TypeKeyHashTable2<TValue>
|
||||
{
|
||||
struct Bucket
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint DistAndFingerPrintFromHash(int hash)
|
||||
{
|
||||
return DistOne | ((uint)hash & FingerPrintMask);
|
||||
}
|
||||
|
||||
public const uint DistOne = 0x00000100;
|
||||
public const uint FingerPrintMask = 0x000000FF;
|
||||
|
||||
/// <summary>
|
||||
/// upper 3 bytes: dist (distance of , also known PSL (probe sequence length))
|
||||
/// lower 1 bytes: fingerprint (lower 1 byte of hash code)
|
||||
/// </summary>
|
||||
public uint DistAndFingerPrint;
|
||||
|
||||
/// <summary>
|
||||
/// The index that point to the location where value is actually stored.
|
||||
/// </summary>
|
||||
public int EntryIndex;
|
||||
}
|
||||
|
||||
readonly Bucket[] buckets;
|
||||
readonly KeyValuePair<Type, TValue>[] entries;
|
||||
readonly int indexFor;
|
||||
|
||||
int insertedEntryLength;
|
||||
|
||||
public TypeKeyHashTable2(KeyValuePair<Type, TValue>[] values, float loadFactor = 0.75f)
|
||||
{
|
||||
var initialCapacity = (int)(values.Length / loadFactor);
|
||||
|
||||
// make power of 2(and use mask)
|
||||
// see: Hashing https://en.wikipedia.org/wiki/Hash_table
|
||||
var capacity = 1;
|
||||
while (capacity < initialCapacity)
|
||||
{
|
||||
capacity <<= 1;
|
||||
}
|
||||
|
||||
buckets = new Bucket[capacity];
|
||||
entries = new KeyValuePair<Type, TValue>[values.Length];
|
||||
indexFor = buckets.Length - 1;
|
||||
|
||||
var entryIndex = 0;
|
||||
foreach (var x in values)
|
||||
{
|
||||
Insert(x, entryIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGet(Type key, out TValue value)
|
||||
{
|
||||
var hash = RuntimeHelpers.GetHashCode(key);
|
||||
var distAndFingerPrint = Bucket.DistAndFingerPrintFromHash(hash);
|
||||
var bucketIndex = hash & indexFor;
|
||||
var bucket = buckets[bucketIndex];
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (distAndFingerPrint == bucket.DistAndFingerPrint)
|
||||
{
|
||||
// compare key
|
||||
var entry = entries[bucket.EntryIndex];
|
||||
if (key == entry.Key)
|
||||
{
|
||||
value = entry.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (distAndFingerPrint > bucket.DistAndFingerPrint)
|
||||
{
|
||||
// not found
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
distAndFingerPrint += Bucket.DistOne;
|
||||
bucketIndex = NextBucketIndex(bucketIndex);
|
||||
bucket = buckets[bucketIndex];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Insert(KeyValuePair<Type, TValue> entry, int entryIndex)
|
||||
{
|
||||
var hash = RuntimeHelpers.GetHashCode(entry.Key);
|
||||
var distAndFingerPrint = Bucket.DistAndFingerPrintFromHash(hash);
|
||||
var bucketIndex = hash & indexFor;
|
||||
|
||||
// robin food hashing
|
||||
while (distAndFingerPrint <= buckets[bucketIndex].DistAndFingerPrint)
|
||||
{
|
||||
// key already exists
|
||||
if (distAndFingerPrint == buckets[bucketIndex].DistAndFingerPrint &&
|
||||
entry.Key == entries[buckets[bucketIndex].EntryIndex].Key)
|
||||
{
|
||||
throw new InvalidOperationException($"The key already exists: {entry.Key}");
|
||||
}
|
||||
|
||||
//
|
||||
bucketIndex = NextBucketIndex(bucketIndex);
|
||||
distAndFingerPrint += Bucket.DistOne; //
|
||||
}
|
||||
|
||||
entries[entryIndex] = entry;
|
||||
SetBucketAt(bucketIndex, new Bucket
|
||||
{
|
||||
DistAndFingerPrint = distAndFingerPrint,
|
||||
EntryIndex = entryIndex
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="bucket"></param>
|
||||
/// <param name="i"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void SetBucketAt(int i, Bucket bucket)
|
||||
{
|
||||
while (buckets[i].DistAndFingerPrint != 0)
|
||||
{
|
||||
// swap
|
||||
(buckets[i], bucket) = (bucket, buckets[i]);
|
||||
bucket.DistAndFingerPrint += Bucket.DistOne;
|
||||
i = NextBucketIndex(i);
|
||||
}
|
||||
buckets[i] = bucket;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
int NextBucketIndex(int i)
|
||||
{
|
||||
return i + 1 >= buckets.Length ? 0 : i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c311a30613d4728b2b891e770db92f7
|
||||
timeCreated: 1695016383
|
||||
Reference in New Issue
Block a user