ResolutionScope
using Stashbox.Exceptions;
using Stashbox.Expressions;
using Stashbox.Resolution;
using Stashbox.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
namespace Stashbox
{
internal sealed class ResolutionScope : IResolutionScope, IDisposable, IDependencyResolver
{
private class DelegateCache
{
public ImmutableTree<object, Func<IResolutionScope, object>> ServiceDelegates = ImmutableTree<object, Func<IResolutionScope, object>>.Empty;
public ImmutableTree<object, Func<IResolutionScope, Delegate>> FactoryDelegates = ImmutableTree<object, Func<IResolutionScope, Delegate>>.Empty;
}
private class DisposableItem
{
public IDisposable Item;
public DisposableItem Next;
public static readonly DisposableItem Empty = new DisposableItem();
}
private class FinalizableItem
{
public object Item;
public Action<object> Finalizer;
public FinalizableItem Next;
public static readonly FinalizableItem Empty = new FinalizableItem();
}
private readonly ResolutionStrategy resolutionStrategy;
private readonly ExpressionFactory expressionFactory;
private readonly IContainerContext containerContext;
private int disposed;
private DisposableItem rootItem = DisposableItem.Empty;
private FinalizableItem rootFinalizableItem = FinalizableItem.Empty;
private ImmutableTree<object> scopedItems = ImmutableTree<object>.Empty;
private ImmutableTree<Type, ImmutableTree<object, object>> scopedInstances = ImmutableTree<Type, ImmutableTree<object, object>>.Empty;
private ImmutableTree<ThreadLocal<bool>> circularDependencyBarrier = ImmutableTree<ThreadLocal<bool>>.Empty;
private DelegateCache delegateCache;
public object Name { get; }
public IResolutionScope ParentScope { get; }
private ResolutionScope(ResolutionStrategy resolutionStrategy, ExpressionFactory expressionBuilder, IContainerContext containerContext, DelegateCache delegateCache, object name)
{
this.resolutionStrategy = resolutionStrategy;
expressionFactory = expressionBuilder;
this.containerContext = containerContext;
Name = name;
this.delegateCache = delegateCache;
}
internal ResolutionScope(ResolutionStrategy resolutionStrategy, ExpressionFactory expressionBuilder, IContainerContext containerContext)
: this(resolutionStrategy, expressionBuilder, containerContext, new DelegateCache(), null)
{
}
private ResolutionScope(ResolutionStrategy resolutionStrategy, ExpressionFactory expressionBuilder, IContainerContext containerContext, IResolutionScope parent, DelegateCache delegateCache, object name = null)
: this(resolutionStrategy, expressionBuilder, containerContext, delegateCache, name)
{
ParentScope = parent;
}
public object Resolve(Type typeFrom, bool nullResultAllowed = false, object[] dependencyOverrides = null)
{
if (dependencyOverrides != null)
return Activate(new ResolutionContext(GetActiveScopeNames(), containerContext, resolutionStrategy, this == containerContext.RootScope, nullResultAllowed, ProcessDependencyOverrides(dependencyOverrides)), typeFrom, null);
Func<IResolutionScope, object> orDefault = delegateCache.ServiceDelegates.GetOrDefault(typeFrom);
if (orDefault == null)
return Activate(new ResolutionContext(GetActiveScopeNames(), containerContext, resolutionStrategy, this == containerContext.RootScope, nullResultAllowed, ProcessDependencyOverrides(dependencyOverrides)), typeFrom, null);
return orDefault(this);
}
public object Resolve(Type typeFrom, object name, bool nullResultAllowed = false, object[] dependencyOverrides = null)
{
if (dependencyOverrides != null)
return Activate(new ResolutionContext(GetActiveScopeNames(), containerContext, resolutionStrategy, this == containerContext.RootScope, nullResultAllowed, ProcessDependencyOverrides(dependencyOverrides)), typeFrom, null);
Func<IResolutionScope, object> orDefault = delegateCache.ServiceDelegates.GetOrDefault(name);
if (orDefault == null)
return Activate(new ResolutionContext(GetActiveScopeNames(), containerContext, resolutionStrategy, this == containerContext.RootScope, nullResultAllowed, ProcessDependencyOverrides(dependencyOverrides)), typeFrom, name);
return orDefault(this);
}
public IEnumerable<TKey> ResolveAll<TKey>(object[] dependencyOverrides = null)
{
return (IEnumerable<TKey>)Resolve(typeof(IEnumerable<TKey>), false, dependencyOverrides);
}
public IEnumerable<object> ResolveAll(Type typeFrom, object[] dependencyOverrides = null)
{
return (IEnumerable<object>)Resolve(typeof(IEnumerable<>).MakeGenericType(typeFrom), false, dependencyOverrides);
}
public Delegate ResolveFactory(Type typeFrom, object name = null, bool nullResultAllowed = false, params Type[] parameterTypes)
{
object key = name ?? typeFrom;
Func<IResolutionScope, Delegate> orDefault = delegateCache.FactoryDelegates.GetOrDefault(key);
if (orDefault == null)
return ActivateFactoryDelegate(typeFrom, parameterTypes, name, nullResultAllowed);
return orDefault(this);
}
public IDependencyResolver BeginScope(object name = null, bool attachToParent = false)
{
ResolutionScope resolutionScope = new ResolutionScope(resolutionStrategy, expressionFactory, containerContext, this, delegateCache, name);
if (!attachToParent)
return resolutionScope;
return AddDisposableTracking(resolutionScope);
}
public IDependencyResolver PutInstanceInScope(Type typeFrom, object instance, bool withoutDisposalTracking = false, object name = null)
{
Shield.EnsureNotNull(typeFrom, "typeFrom");
Shield.EnsureNotNull(instance, "instance");
object obj = name ?? typeFrom;
ImmutableTree<object, object> t5 = ImmutableTree<object, object>.Empty.AddOrUpdate(obj, instance, null);
Swap.SwapValue(ref scopedInstances, (Type t1, object t2, ImmutableTree<object, object> t3, object t4, ImmutableTree<Type, ImmutableTree<object, object>> instances) => instances.AddOrUpdate(t1, t3, (ImmutableTree<object, object> old, ImmutableTree<object, object> new) => old.AddOrUpdate(t4, t2, true)), typeFrom, instance, t5, obj);
if (!withoutDisposalTracking) {
IDisposable disposable = instance as IDisposable;
if (disposable != null)
AddDisposableTracking(disposable);
}
delegateCache = new DelegateCache();
return this;
}
public TTo BuildUp<TTo>(TTo instance)
{
ResolutionContext resolutionContext = new ResolutionContext(GetActiveScopeNames(), containerContext, resolutionStrategy, this == containerContext.RootScope, false, null);
return (TTo)expressionFactory.ConstructBuildUpExpression(resolutionContext, instance.AsConstant(), typeof(TTo)).CompileDelegate(resolutionContext, containerContext.ContainerConfiguration)(this);
}
public object Activate(Type type, params object[] arguments)
{
if (!type.IsResolvableType())
throw new ArgumentException("The given type (" + type.FullName + ") could not be activated on the fly by the container.");
ResolutionContext resolutionContext = new ResolutionContext(GetActiveScopeNames(), containerContext, resolutionStrategy, this == containerContext.RootScope, false, ProcessDependencyOverrides(arguments));
return expressionFactory.ConstructExpression(resolutionContext, type).CompileDelegate(resolutionContext, containerContext.ContainerConfiguration)(this);
}
public TDisposable AddDisposableTracking<TDisposable>(TDisposable disposable) where TDisposable : IDisposable
{
Swap.SwapValue<TDisposable, byte, byte, byte, DisposableItem>(ref rootItem, (TDisposable t1, byte t2, byte t3, byte t4, DisposableItem root) => new DisposableItem {
Item = (object)t1,
Next = root
}, disposable, 0, 0, 0);
return disposable;
}
public TService AddWithFinalizer<TService>(TService finalizable, Action<TService> finalizer)
{
Swap.SwapValue<TService, Action<TService>, byte, byte, FinalizableItem>(ref rootFinalizableItem, (TService t1, Action<TService> t2, byte t3, byte t4, FinalizableItem root) => new FinalizableItem {
Item = (object)t1,
Finalizer = (Action<object>)delegate(object f) {
t2((TService)f);
},
Next = root
}, finalizable, finalizer, 0, 0);
return finalizable;
}
public object GetOrAddScopedObject(int key, object sync, Func<IResolutionScope, object> factory)
{
object orDefault = scopedItems.GetOrDefault(key);
if (orDefault != null)
return orDefault;
lock (sync) {
orDefault = scopedItems.GetOrDefault(key);
if (orDefault == null) {
orDefault = factory(this);
Swap.SwapValue<int, object, byte, byte, ImmutableTree<object>>(ref scopedItems, (int t1, object t2, byte t3, byte t4, ImmutableTree<object> items) => items.AddOrUpdate(t1, t2, null), key, orDefault, 0, 0);
return orDefault;
}
return orDefault;
}
}
public void InvalidateDelegateCache()
{
delegateCache.ServiceDelegates = ImmutableTree<object, Func<IResolutionScope, object>>.Empty;
delegateCache.FactoryDelegates = ImmutableTree<object, Func<IResolutionScope, Delegate>>.Empty;
}
public IEnumerable<object> GetActiveScopeNames()
{
for (IResolutionScope current = this; current != null; current = current.ParentScope) {
if (current.Name != null)
yield return current.Name;
}
}
public void CheckRuntimeCircularDependencyBarrier(int key, Type type)
{
ThreadLocal<bool> orDefault = circularDependencyBarrier.GetOrDefault(key);
if (orDefault != null && orDefault.Value)
throw new CircularDependencyException(type, null);
Swap.SwapValue<int, byte, byte, byte, ImmutableTree<ThreadLocal<bool>>>(ref circularDependencyBarrier, (int t1, byte t2, byte t3, byte t4, ImmutableTree<ThreadLocal<bool>> barrier) => barrier.AddOrUpdate(t1, new ThreadLocal<bool>(), delegate(ThreadLocal<bool> old, ThreadLocal<bool> new) {
old.Value = true;
return old;
}), key, 0, 0, 0);
}
public void ResetRuntimeCircularDependencyBarrier(int key)
{
ThreadLocal<bool> orDefault = circularDependencyBarrier.GetOrDefault(key);
if (orDefault != null)
orDefault.Value = false;
}
public void Dispose()
{
if (Interlocked.CompareExchange(ref disposed, 1, 0) == 0) {
for (FinalizableItem next = rootFinalizableItem; next != FinalizableItem.Empty; next = next.Next) {
next.Finalizer(next.Item);
}
for (DisposableItem next2 = rootItem; next2 != DisposableItem.Empty; next2 = next2.Next) {
next2.Item.Dispose();
}
if (!circularDependencyBarrier.IsEmpty) {
foreach (KeyValuePair<int, ThreadLocal<bool>> item in circularDependencyBarrier.Walk()) {
item.Value.Dispose();
}
}
}
}
private object Activate(ResolutionContext resolutionContext, Type type, object name = null)
{
Expression expression = resolutionStrategy.BuildExpressionForTopLevelRequest(type, name, resolutionContext);
if (expression == null) {
if (resolutionContext.NullResultAllowed)
return null;
throw new ResolutionFailedException(type, "Service is not registered or unresolvable type requested.", null);
}
Func<IResolutionScope, object> func = expression.CompileDelegate(resolutionContext, containerContext.ContainerConfiguration);
if (resolutionContext.FactoryDelegateCacheEnabled)
Swap.SwapValue<object, Func<IResolutionScope, object>, byte, byte, ImmutableTree<object, Func<IResolutionScope, object>>>(ref delegateCache.ServiceDelegates, (object t1, Func<IResolutionScope, object> t2, byte t3, byte t4, ImmutableTree<object, Func<IResolutionScope, object>> c) => c.AddOrUpdate(t1, t2, null), name ?? type, func, 0, 0);
return func(this);
}
private Delegate ActivateFactoryDelegate(Type type, Type[] parameterTypes, object name, bool nullResultAllowed)
{
ResolutionContext resolutionContext = new ResolutionContext(GetActiveScopeNames(), containerContext, resolutionStrategy, this == containerContext.RootScope, nullResultAllowed, null).BeginContextWithFunctionParameters(from p in parameterTypes
select p.AsParameter(null));
Expression expression = resolutionStrategy.BuildExpressionForTopLevelRequest(type, name, resolutionContext);
if (expression == null) {
if (resolutionContext.NullResultAllowed)
return null;
throw new ResolutionFailedException(type, "Service is not registered or unresolvable type requested.", null);
}
Func<IResolutionScope, Delegate> func = expression.AsLambda(resolutionContext.ParameterExpressions.SelectMany((IEnumerable<Pair<bool, ParameterExpression>> x) => from i in x
select i.I2)).CompileDynamicDelegate(resolutionContext, containerContext.ContainerConfiguration);
Swap.SwapValue<object, Func<IResolutionScope, Delegate>, byte, byte, ImmutableTree<object, Func<IResolutionScope, Delegate>>>(ref delegateCache.FactoryDelegates, (object t1, Func<IResolutionScope, Delegate> t2, byte t3, byte t4, ImmutableTree<object, Func<IResolutionScope, Delegate>> c) => c.AddOrUpdate(t1, t2, null), name ?? type, func, 0, 0);
return func(this);
}
private HashTree<Type, HashTree<object, Expression>> ProcessDependencyOverrides(object[] dependencyOverrides)
{
if (dependencyOverrides == null && scopedInstances.IsEmpty)
return null;
HashTree<Type, HashTree<object, Expression>> hashTree = new HashTree<Type, HashTree<object, Expression>>();
if (!scopedInstances.IsEmpty) {
foreach (KeyValuePair<Type, ImmutableTree<object, object>> item in scopedInstances.Walk()) {
HashTree<object, Expression> hashTree2 = new HashTree<object, Expression>();
foreach (KeyValuePair<object, object> item2 in item.Value.Walk()) {
hashTree2.Add(item2.Key, item2.Value.AsConstant(), false);
}
hashTree.Add(item.Key, hashTree2, true);
}
}
if (dependencyOverrides == null)
return hashTree;
foreach (object obj in dependencyOverrides) {
Type type = obj.GetType();
ConstantExpression value = obj.AsConstant();
HashTree<object, Expression> orDefault = hashTree.GetOrDefault(type, true);
if (orDefault == null)
hashTree.Add(type, new HashTree<object, Expression>(type, value), true);
else
orDefault.Add(type, value, false);
foreach (Type item3 in type.GetRegisterableInterfaceTypes().Concat(type.GetRegisterableBaseTypes())) {
orDefault = hashTree.GetOrDefault(item3, true);
if (orDefault == null)
hashTree.Add(item3, new HashTree<object, Expression>(item3, value), true);
else
orDefault.Add(item3, value, false);
}
}
return hashTree;
}
}
}