diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/BattleFieldSimulator.Utilities.csproj b/BattleFieldSimulator/BattleFieldSimulator.Utilities/BattleFieldSimulator.Utilities.csproj deleted file mode 100644 index 6091e1b..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/BattleFieldSimulator.Utilities.csproj +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - Debug - AnyCPU - {F65ECA33-08FF-4797-8E53-B086320214F4} - Library - Properties - BattleFieldSimulator.Utilities - BattleFieldSimulator.Utilities - v4.7.2 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - - - - - \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/BootStrapper.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/BootStrapper.cs deleted file mode 100644 index 0d5eeaf..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/BootStrapper.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using DryIoc; - -namespace BattleFieldSimulator -{ - public class BootStrapper - { - private static readonly string NoBootstrapperMessage = $"Called {nameof(BootStrapper)} before it existed"; - - private static BootStrapper _bootStrapper; - private static IContainer _container; - - public static BootStrapper Instance => - _bootStrapper ?? throw new InvalidOperationException(NoBootstrapperMessage); - - private BootStrapper(params IModule[] modules) - { - _container = new Container(); - foreach (var module in modules) - { - module.Register(_container); - } - foreach (var module in modules) - { - module.Resolve(_container); - } - } - - public static BootStrapper BootstrapSystem(params IModule[] modules) => - _bootStrapper = new BootStrapper(modules); - - public T Resolve() - { - return _container.Resolve(); - } - } -} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/Class1.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/Class1.cs deleted file mode 100644 index 33b2880..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BattleFieldSimulator.Utilities -{ - public class Class1 - { - } -} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/Container.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/Container.cs deleted file mode 100644 index 6f30561..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/Container.cs +++ /dev/null @@ -1,14042 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2013-2020 Maksim Volkau - -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 !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_FAST_EXPRESSION_COMPILER -#endif -#if !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_ISERVICE_PROVIDER -#endif -#if !PCL && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 -#define SUPPORTS_SERIALIZABLE -#define SUPPORTS_STACK_TRACE -#define SUPPORTS_MANAGED_THREAD_ID -#endif -#if !PCL && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_5 && !NET35 && !NET40 && !NET403 && !NET45 && !NET451 && !NET452 -#define SUPPORTS_ASYNC_LOCAL -#endif -#if !PCL && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NET35 && !NET40 && !NET403 -#define SUPPORTS_VARIANCE -#endif -#if !PCL && !NET35 && !NET40 && !NET403 && !NET45 && !NET451 && !NET452 && !NET46 && !NET461 && !NET462 && !NET47 && !NET471 && !NET472 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 -#define SUPPORTS_EXPRESSION_COMPILE_WITH_PREFER_INTERPRETATION_PARAM -#endif -#if !PCL && !NET35 && !NET40 && !NET403 -#define SUPPORTS_DELEGATE_METHOD -#endif - -namespace DryIoc -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Text; - using System.Threading; - using System.Diagnostics.CodeAnalysis; // for SuppressMessage - using System.Diagnostics; // for StackTrace - using System.Runtime.CompilerServices; // for MethodImplAttribute - - using ImTools; - using static ImTools.ArrayTools; - using static System.Environment; - - using ExprType = System.Linq.Expressions.ExpressionType; - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - using FastExpressionCompiler.LightExpression; - using static FastExpressionCompiler.LightExpression.Expression; -#else - using System.Linq.Expressions; - using static System.Linq.Expressions.Expression; -#endif - - /// Inversion of control container - public sealed partial class Container : IContainer - { - /// Creates new container with default rules . - public Container() : this(Rules.Default, Ref.Of(Registry.Default), NewSingletonScope()) - { - SetInitialFactoryID(); - } - - /// Creates new container, optionally providing to modify default container behavior. - /// (optional) Rules to modify container default resolution behavior. - /// If not specified, then will be used. - /// (optional) Scope context to use for scoped reuse. - public Container(Rules rules = null, IScopeContext scopeContext = null) - : this(rules ?? Rules.Default, Ref.Of(Registry.Default), NewSingletonScope(), scopeContext) - { - SetInitialFactoryID(); - } - - /// Creates new container with configured rules. - /// Allows to modify rules. - /// (optional) Scope context to use for . - public Container(Func configure, IScopeContext scopeContext = null) - : this(configure.ThrowIfNull()(Rules.Default) ?? Rules.Default, scopeContext) - { } - - private sealed class SinlgetonScopeName - { - public static readonly SinlgetonScopeName Name = new SinlgetonScopeName(); - private SinlgetonScopeName() { } - public override string ToString() => nameof(SinlgetonScopeName); - } - - /// Helper to create singleton scope - public static IScope NewSingletonScope() => new Scope(name: SinlgetonScopeName.Name); - - /// Pretty prints the container info including the open scope details if any. - public override string ToString() - { - var s = _scopeContext == null ? "Container" : "Container with ambient ScopeContext " + _scopeContext; - - var scope = CurrentScope; - s += scope == null ? " without Scope" : " with Scope " + scope; - - if (Rules != Rules.Default) - s += NewLine + " with " + Rules; - - if (IsDisposed) - { - s += " has been DISPOSED!" + NewLine; - if (_disposeStackTrace != null) - s += " Dispose stack-trace " + _disposeStackTrace; - else - s += " You may include Dispose stack-trace into the message via:" + NewLine + - "container.With(rules => rules.WithCaptureContainerDisposeStackTrace())"; - } - - return s; - } - - /// Dispose either open scope, or container with singletons, if no scope opened. - public void Dispose() - { - // if already disposed - just leave - if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1) - return; - - // Nice to have disposal stack-trace, but we can live without it if something goes wrong - if (Rules.CaptureContainerDisposeStackTrace) - try { _disposeStackTrace = new StackTrace(); } - catch { } - - if (_parent != null) - { - if (_ownCurrentScope != null) - { - _ownCurrentScope.Dispose(); - } - else if (_scopeContext != null) - { - IScope currentScope = null; - _scopeContext.SetCurrent(s => - { - // save the current scope for the later, - // do dispose it AFTER its parent is actually set to be a new ambient current scope. - currentScope = s; - return s?.Parent; - }); - currentScope?.Dispose(); - } - } - else - { - _registry.Swap(Registry.Empty); - Rules = Rules.Default; - _singletonScope.Dispose(); // will also dispose any tracked scopes - _scopeContext?.Dispose(); - } - } - - #region Compile-time generated parts - former DryIocZero - - partial void GetLastGeneratedFactoryID(ref int lastFactoryID); - - partial void ResolveGenerated(ref object service, Type serviceType); - - partial void ResolveGenerated(ref object service, - Type serviceType, object serviceKey, Type requiredServiceType, Request preRequestParent, object[] args); - - partial void ResolveManyGenerated(ref IEnumerable services, Type serviceType); - - /// Identifies the service when resolving collection - public struct ResolveManyResult - { - /// Factory, the required part - public FactoryDelegate FactoryDelegate; - - /// Optional key - public object ServiceKey; - - /// Optional required service type, can be an open-generic type. - public Type RequiredServiceType; - - /// Constructs the struct. - public static ResolveManyResult Of(FactoryDelegate factoryDelegate, - object serviceKey = null, Type requiredServiceType = null) => - new ResolveManyResult - { - FactoryDelegate = factoryDelegate, - ServiceKey = serviceKey, - RequiredServiceType = requiredServiceType - }; - } - - /// Directly uses generated factories to resolve service. Or returns the default if service does not have generated factory. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ExpressionIsAlwaysNull", Justification = "Per design")] - public object ResolveCompileTimeGeneratedOrDefault(Type serviceType) - { - object service = null; - ResolveGenerated(ref service, serviceType); - return service; - } - - /// Directly uses generated factories to resolve service. Or returns the default if service does not have generated factory. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ExpressionIsAlwaysNull", Justification = "Per design")] - public object ResolveCompileTimeGeneratedOrDefault(Type serviceType, object serviceKey) - { - object service = null; - ResolveGenerated(ref service, serviceType, serviceKey, - requiredServiceType: null, preRequestParent: null, args: null); - return service; - } - - /// Resolves many generated only services. Ignores runtime registrations. - public IEnumerable ResolveManyCompileTimeGeneratedOrEmpty(Type serviceType) - { - IEnumerable manyGenerated = ArrayTools.Empty(); - ResolveManyGenerated(ref manyGenerated, serviceType); - return manyGenerated; - } - - #endregion - - #region IRegistrator - - /// Returns all registered service factories with their Type and optional Key. - /// Decorator and Wrapper types are not included. - public IEnumerable GetServiceRegistrations() => - _registry.Value.GetServiceRegistrations(); - - // todo: Make `serviceKey` and `factoryType` optional - /// Searches for registered factories by type, and key (if specified), - /// and by factory type (by default uses ). - /// May return empty, 1 or multiple factories. - public Factory[] GetRegisteredFactories(Type serviceType, object serviceKey, FactoryType factoryType) => - _registry.Value.GetRegisteredFactories(serviceType.ThrowIfNull(), serviceKey, factoryType); - - /// Stores factory into container using and as key - /// for later lookup. - /// Any subtypes of . - /// Type of service to resolve later. - /// (optional) Service key of any type with and - /// implemented. - /// (optional) Says how to handle existing registration with the same - /// and . - /// Confirms that service and implementation types are statically checked by compiler. - /// True if factory was added to registry, false otherwise. - /// False may be in case of setting and already existing factory. - public void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked) - { - ThrowIfContainerDisposed(); - - if (serviceKey == null) - serviceKey = Rules.DefaultRegistrationServiceKey; - - factory.ThrowIfNull().ValidateAndNormalizeRegistration(serviceType, serviceKey, isStaticallyChecked, Rules); - - if (!ifAlreadyRegistered.HasValue) - ifAlreadyRegistered = Rules.DefaultIfAlreadyRegistered; - - // Improves performance a bit by first attempting to swap the registry while it is still unchanged. - var r = _registry.Value; - if (!_registry.TrySwapIfStillCurrent(r, r.Register(factory, serviceType, ifAlreadyRegistered.Value, serviceKey))) - RegistrySwap(factory, serviceType, serviceKey, ifAlreadyRegistered); - } - - // hiding nested lambda in method to reduce allocations - private Registry RegistrySwap(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered) => - _registry.Swap(r => r.Register(factory, serviceType, ifAlreadyRegistered.Value, serviceKey)); - - /// - public bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, Func condition) - { - ThrowIfContainerDisposed(); - return _registry.Value.IsRegistered(serviceType, serviceKey, factoryType, condition); - } - - /// - public void Unregister(Type serviceType, object serviceKey, FactoryType factoryType, Func condition) - { - ThrowIfContainerDisposed(); - _registry.Swap(r => r.Unregister(factoryType, serviceType, serviceKey, condition)); - } - -#endregion - -#region IResolver - -#if SUPPORTS_ISERVICE_PROVIDER - /// Resolves service with policy, - /// enabling the fallback resolution for not registered services (default MS convention) - object IServiceProvider.GetService(Type serviceType) => - ((IResolver)this).Resolve(serviceType, IfUnresolved.ReturnDefaultIfNotRegistered); -#endif - - object IResolver.Resolve(Type serviceType, IfUnresolved ifUnresolved) - { - object service = null; - ResolveGenerated(ref service, serviceType); - if (service != null) - return service; - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - var cacheEntry = _registry.Value.GetCachedDefaultFactoryOrDefault(serviceTypeHash, serviceType); - if (cacheEntry != null) - { - ref var entry = ref cacheEntry.Value; - if (entry.Value is FactoryDelegate cachedDelegate) - return cachedDelegate(this); - - if (ResolverContext.TryGetUsedInstance(this, serviceType, out var usedInstance)) - { - entry.Value = null; // reset the cache - return usedInstance; - } - - var rules = Rules; - while (entry.Value is Expression expr) - { - if (rules.UseInterpretation && - Interpreter.TryInterpretAndUnwrapContainerException(this, expr, false, out var result)) - return result; - - // set to Compiling to notify other threads to use the interpretation until the service is compiled - if (Interlocked.CompareExchange(ref entry.Value, new Registry.Compiling(expr), expr) == expr) - { - var compiledFactory = expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation); - // todo: should we instead cache only after invoking the factory delegate - entry.Value = compiledFactory; - return compiledFactory(this); - } - } - - if (entry.Value is Registry.Compiling compiling) - { - if (Interpreter.TryInterpretAndUnwrapContainerException(this, compiling.Expression, false, out var result)) - return result; - return compiling.Expression.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation)(this); - } - } - - return ResolveAndCache(serviceTypeHash, serviceType, ifUnresolved); - } - - private object ResolveAndCache(int serviceTypeHash, Type serviceType, IfUnresolved ifUnresolved) - { - ThrowIfContainerDisposed(); - - if (ResolverContext.TryGetUsedInstance(this, serviceType, out var usedInstance)) - return usedInstance; - - var request = Request.Create(this, serviceType, ifUnresolved: ifUnresolved); - var factory = ((IContainer)this).ResolveFactory(request); // HACK: may mutate request, but it should be safe - - // Delegate to full blown Resolve aware of service key, open scope, etc. - var serviceKey = request.ServiceKey; - var scopeName = CurrentScope?.Name; - if (serviceKey != null || scopeName != null) - return ResolveAndCacheKeyed(serviceTypeHash, serviceType, serviceKey, ifUnresolved, scopeName, null, Request.Empty, null); - - if (factory == null) - return null; - - var rules = Rules; - FactoryDelegate factoryDelegate; - - // todo: [Obsolete] - in v5.0 there should be no check nor the InstanceFactory - if (factory is InstanceFactory || - !rules.UseInterpretationForTheFirstResolution) - { - factoryDelegate = factory.GetDelegateOrDefault(request); - if (factoryDelegate == null) - return null; - } - else - { - var expr = factory.GetExpressionOrDefault(request); - if (expr == null) - return null; - - if (expr is ConstantExpression constExpr) - { - var value = constExpr.Value; - if (factory.Caching != FactoryCaching.DoNotCache) - _registry.Value.TryCacheDefaultFactory(serviceTypeHash, serviceType, value.ToFactoryDelegate); - return value; - } - - // Important to cache expression first before tying to interpret, - // so that parallel resolutions may already use it and UseInstance may correctly evict the cache if needed. - if (factory.Caching != FactoryCaching.DoNotCache) - _registry.Value.TryCacheDefaultFactory(serviceTypeHash, serviceType, expr); - - // 1) First try to interpret - if (Interpreter.TryInterpretAndUnwrapContainerException(this, expr, rules.UseFastExpressionCompiler, out var instance)) - return instance; - // 2) Fallback to expression compilation - factoryDelegate = expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation); - } - - if (factory.Caching != FactoryCaching.DoNotCache) - _registry.Value.TryCacheDefaultFactory(serviceTypeHash, serviceType, factoryDelegate); - - return factoryDelegate(this); - } - - object IResolver.Resolve(Type serviceType, object serviceKey, - IfUnresolved ifUnresolved, Type requiredServiceType, Request preResolveParent, object[] args) - { - // fallback to simple Resolve and its default cache if no keys are passed - var scopeName = CurrentScope?.Name; - if (serviceKey == null && requiredServiceType == null && scopeName == null && - (preResolveParent == null || preResolveParent.IsEmpty) && args.IsNullOrEmpty()) - return ((IResolver)this).Resolve(serviceType, ifUnresolved); - - return ResolveAndCacheKeyed(RuntimeHelpers.GetHashCode(serviceType), serviceType, - serviceKey, ifUnresolved, scopeName, requiredServiceType, preResolveParent ?? Request.Empty, args); - } - - private object ResolveAndCacheKeyed(int serviceTypeHash, Type serviceType, - object serviceKey, IfUnresolved ifUnresolved, object scopeName, Type requiredServiceType, Request preResolveParent, - object[] args) - { - object service = null; - ResolveGenerated(ref service, serviceType, serviceKey, requiredServiceType, preResolveParent, args); - if (service != null) - return service; - - object cacheKey = null; - if (requiredServiceType == null && preResolveParent.IsEmpty && args.IsNullOrEmpty()) - { - cacheKey = scopeName == null ? serviceKey - : serviceKey == null ? scopeName - : KV.Of(scopeName, serviceKey); - - if (_registry.Value.GetCachedKeyedFactoryOrDefault(serviceTypeHash, serviceType, cacheKey, out var cacheEntry)) - { - if (cacheEntry.Factory is FactoryDelegate cachedDelegate) - return cachedDelegate(this); - if (TryInterpretOrCompileCachedExpression(this, cacheEntry, Rules, out var result)) - return result; - } - } - - // Cache is missed, so get the factory and put it into cache: - ThrowIfContainerDisposed(); - - var request = Request.Create(this, serviceType, serviceKey, ifUnresolved, requiredServiceType, preResolveParent, default, args); - var factory = ((IContainer)this).ResolveFactory(request); - if (factory == null) - return null; - - // Prevents caching if factory says Don't - if (factory.Caching == FactoryCaching.DoNotCache) - cacheKey = null; - - // Request service key may be changed when resolving the factory, - // so we need to look into Default cache again for the new key - if (cacheKey != null && serviceKey == null && request.ServiceKey != null) - { - cacheKey = scopeName == null ? request.ServiceKey : KV.Of(scopeName, request.ServiceKey); - if (_registry.Value.GetCachedKeyedFactoryOrDefault(serviceTypeHash, serviceType, cacheKey, out var cacheEntry)) - { - if (cacheEntry.Factory is FactoryDelegate cachedDelegate) - return cachedDelegate(this); - if (TryInterpretOrCompileCachedExpression(this, cacheEntry, Rules, out var result)) - return result; - } - } - - FactoryDelegate factoryDelegate; - if (factory is InstanceFactory || !Rules.UseInterpretationForTheFirstResolution) - { - factoryDelegate = factory.GetDelegateOrDefault(request); - if (factoryDelegate == null) - return null; - } - else - { - var expr = factory.GetExpressionOrDefault(request); - if (expr == null) - return null; - - if (expr is ConstantExpression constExpr) - { - var value = constExpr.Value; - if (cacheKey != null) - _registry.Value.TryCacheKeyedFactory(serviceTypeHash, serviceType, cacheKey, (FactoryDelegate)value.ToFactoryDelegate); - return value; - } - - // Important to cache expression first before tying to interpret, - // so that parallel resolutions may already use it and UseInstance may correctly evict the cache if needed - if (cacheKey != null) - _registry.Value.TryCacheKeyedFactory(serviceTypeHash, serviceType, cacheKey, expr); - - // 1) First try to interpret - var useFec = Rules.UseFastExpressionCompiler; - if (Interpreter.TryInterpretAndUnwrapContainerException(this, expr, useFec, out var instance)) - return instance; - - // 2) Fallback to expression compilation - factoryDelegate = expr.CompileToFactoryDelegate(useFec, Rules.UseInterpretation); - } - - // Cache factory only when we successfully called the factory delegate, to prevent failing delegates to be cached. - // Additionally disable caching when no services registered, not to cache an empty collection wrapper or alike. - if (cacheKey != null) - _registry.Value.TryCacheKeyedFactory(serviceTypeHash, serviceType, cacheKey, factoryDelegate); - - return factoryDelegate(this); - } - - private static bool TryInterpretOrCompileCachedExpression(IResolverContext r, - Registry.KeyedFactoryCacheEntry cacheEntry, Rules rules, out object result) - { - while (cacheEntry.Factory is Expression expr) - { - if (rules.UseInterpretation && - Interpreter.TryInterpretAndUnwrapContainerException(r, expr, false, out result)) - return true; - - // set to Compiling to notify other threads to use the interpretation until the service is compiled - if (Interlocked.CompareExchange(ref cacheEntry.Factory, new Registry.Compiling(expr), expr) == expr) - { - var factoryDelegate = expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation); - // todo: should we instead cache only after invoking the factory delegate - cacheEntry.Factory = factoryDelegate; - result = factoryDelegate(r); - return true; - } - } - - if (cacheEntry.Factory is Registry.Compiling compiling) - { - if (!Interpreter.TryInterpretAndUnwrapContainerException(r, compiling.Expression, false, out result)) - result = compiling.Expression.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation)(r); - return true; - } - - result = null; - return false; - } - - IEnumerable IResolver.ResolveMany(Type serviceType, object serviceKey, - Type requiredServiceType, Request preResolveParent, object[] args) - { - var requiredItemType = requiredServiceType ?? serviceType; - - // first return compile-time generated factories if any - var generatedFactories = Enumerable.Empty(); - ResolveManyGenerated(ref generatedFactories, serviceType); - if (serviceKey != null) - generatedFactories = generatedFactories.Where(x => serviceKey.Equals(x.ServiceKey)); - if (requiredServiceType != null) - generatedFactories = generatedFactories.Where(x => requiredServiceType == x.RequiredServiceType); - - foreach (var generated in generatedFactories) - yield return generated.FactoryDelegate(this); - - // Emulating the collection parent so that collection related rules and conditions were applied - // the same way as if resolving IEnumerable - if (preResolveParent == null || preResolveParent.IsEmpty) - preResolveParent = Request.Empty.Push( - typeof(IEnumerable), requiredItemType, serviceKey, IfUnresolved.Throw, - WrappersSupport.CollectionWrapperID, FactoryType.Wrapper, null, null, 0, 0); - - var container = (IContainer)this; - - var unwrappedType = container.GetWrappedType(requiredItemType, null); - if (unwrappedType != null && unwrappedType != typeof(void)) // accounting for the resolved action GH#114 - requiredItemType = unwrappedType; - - var items = container.GetAllServiceFactories(requiredItemType).ToArrayOrSelf() - .Where(x => x.Value != null) - .Select(f => new ServiceRegistrationInfo(f.Value, requiredServiceType, f.Key)); - - IEnumerable openGenericItems = null; - if (requiredItemType.IsClosedGeneric()) - { - var requiredItemOpenGenericType = requiredItemType.GetGenericDefinitionOrNull(); - openGenericItems = container.GetAllServiceFactories(requiredItemOpenGenericType) - .Where(x => x.Value != null) - .Select(x => new ServiceRegistrationInfo(x.Value, requiredServiceType, - new OpenGenericTypeKey(requiredItemOpenGenericType, x.Key))); - } - - // Append registered generic types with compatible variance, - // e.g. for IHandler - IHandler is compatible with IHandler if B : A. - IEnumerable variantGenericItems = null; - if (requiredItemType.IsGeneric() && container.Rules.VariantGenericTypesInResolvedCollection) - { - variantGenericItems = container.GetServiceRegistrations() - .Where(x => x.ServiceType.IsGeneric() - && x.ServiceType.GetGenericTypeDefinition() == requiredItemType.GetGenericTypeDefinition() - && x.ServiceType != requiredItemType - && x.ServiceType.IsAssignableTo(requiredItemType)); - } - - if (serviceKey != null) // include only single item matching key. - { - items = items.Where(it => serviceKey.Equals(it.OptionalServiceKey)); - if (openGenericItems != null) - openGenericItems = openGenericItems // extract the actual key from combined type and key - .Where(x => serviceKey.Equals(((OpenGenericTypeKey)x.OptionalServiceKey).ServiceKey)); - if (variantGenericItems != null) - variantGenericItems = variantGenericItems - .Where(it => serviceKey.Equals(it.OptionalServiceKey)); - } - - var metadataKey = preResolveParent.MetadataKey; - var metadata = preResolveParent.Metadata; - if (metadataKey != null || metadata != null) - { - items = items.Where(x => x.Factory.Setup.MatchesMetadata(metadataKey, metadata)); - if (openGenericItems != null) - openGenericItems = openGenericItems - .Where(x => x.Factory.Setup.MatchesMetadata(metadataKey, metadata)); - if (variantGenericItems != null) - variantGenericItems = variantGenericItems - .Where(x => x.Factory.Setup.MatchesMetadata(metadataKey, metadata)); - } - - // Exclude composite parent service from items, skip decorators - var parent = preResolveParent; - if (parent.FactoryType != FactoryType.Service) - parent = parent.FirstOrDefault(p => p.FactoryType == FactoryType.Service) ?? Request.Empty; - - if (!parent.IsEmpty && parent.GetActualServiceType() == requiredItemType) - { - items = items.Where(x => x.Factory.FactoryID != parent.FactoryID); - - if (openGenericItems != null) - openGenericItems = openGenericItems.Where(x => x - .Factory.FactoryGenerator?.GeneratedFactories.Enumerate() - .FindFirst(f => f.Value.FactoryID == parent.FactoryID) == null); - - if (variantGenericItems != null) - variantGenericItems = variantGenericItems - .Where(x => x.Factory.FactoryID != parent.FactoryID); - } - - var allItems = openGenericItems == null && variantGenericItems == null ? items - : variantGenericItems == null ? items.Concat(openGenericItems) - : openGenericItems == null ? items.Concat(variantGenericItems) - : items.Concat(openGenericItems).Concat(variantGenericItems); - - // Resolve in registration order - foreach (var item in allItems.OrderBy(x => x.FactoryRegistrationOrder)) - { - var service = container.Resolve(serviceType, item.OptionalServiceKey, - IfUnresolved.ReturnDefaultIfNotRegistered, item.ServiceType, preResolveParent, args); - if (service != null) // skip unresolved items - yield return service; - } - } - - private void ThrowIfContainerDisposed() - { - if (IsDisposed) - Throw.It(Error.ContainerIsDisposed, ToString()); - } - -#endregion - -#region IResolverContext - - /// - public IResolverContext Parent => _parent; - - /// - public IResolverContext Root - { - get - { - if (_parent == null) - return null; - var p = _parent; - while (p.Parent != null) - p = p.Parent; - return p; - } - } - - /// - public IScope SingletonScope => _singletonScope; - - /// - public IScopeContext ScopeContext => _scopeContext; - - /// - public IScope CurrentScope => - _scopeContext == null ? _ownCurrentScope : _scopeContext.GetCurrentOrDefault(); - - /// - [MethodImpl((MethodImplOptions)256)] - public IResolverContext WithCurrentScope(IScope scope) - { - ThrowIfContainerDisposed(); - return new Container(Rules, _registry, _singletonScope, _scopeContext, - scope, _disposed, _disposeStackTrace, parent: this); - } - - /// [Obsolete("Please use `RegisterInstance` or `Use` method instead")] - public void UseInstance(Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey) - { - ThrowIfContainerDisposed(); - - if (instance != null) - instance.ThrowIfNotInstanceOf(serviceType, Error.RegisteringInstanceNotAssignableToServiceType); - - if (weaklyReferenced) - instance = new WeakReference(instance); - else if (preventDisposal) - instance = new HiddenDisposable(instance); - - var scope = _ownCurrentScope ?? _singletonScope; - var reuse = scope == _singletonScope ? Reuse.Singleton : Reuse.Scoped; - var instanceType = instance?.GetType() ?? typeof(object); - - _registry.Swap(r => - { - var entry = r.Services.GetValueOrDefault(serviceType); - var oldEntry = entry; - - // no entries, first registration, usual/hot path - if (entry == null) - { - // add new entry with instance factory - var instanceFactory = new InstanceFactory(instance, instanceType, reuse, scope); - entry = serviceKey == null - ? (object)instanceFactory - : FactoriesEntry.Empty.With(instanceFactory, serviceKey); - } - else - { - // have some registrations of instance, find if we should replace, add, or throw - var singleDefaultFactory = entry as Factory; - if (singleDefaultFactory != null) - { - if (serviceKey != null) - { - // @ifAlreadyRegistered does not make sense for keyed, because there are no other keyed - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope), serviceKey); - } - else // for default instance - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.AppendNotKeyed: - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - case IfAlreadyRegistered.Throw: - Throw.It(Error.UnableToRegisterDuplicateDefault, serviceType, singleDefaultFactory); - break; - case IfAlreadyRegistered.Keep: - break; - case IfAlreadyRegistered.Replace: - var reusedFactory = singleDefaultFactory as InstanceFactory; - if (reusedFactory != null) - scope.SetOrAdd(reusedFactory.FactoryID, instance); - else if (reuse != Reuse.Scoped) // for non-instance single registration, just replace with non-scoped instance only - entry = new InstanceFactory(instance, instanceType, reuse, scope); - else - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - case IfAlreadyRegistered.AppendNewImplementation: // otherwise Keep the old one - if (singleDefaultFactory.CanAccessImplementationType && - singleDefaultFactory.ImplementationType != instanceType) - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - } - } - } - else // for multiple existing or single keyed factory - { - var singleKeyedOrManyDefaultFactories = (FactoriesEntry)entry; - if (serviceKey != null) - { - var singleKeyedFactory = singleKeyedOrManyDefaultFactories.Factories.GetValueOrDefault(serviceKey); - if (singleKeyedFactory == null) - { - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope), serviceKey); - } - else // when keyed instance is found - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.Replace: - var reusedFactory = singleKeyedFactory as InstanceFactory; - if (reusedFactory != null) - scope.SetOrAdd(reusedFactory.FactoryID, instance); - else - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope), serviceKey); - break; - case IfAlreadyRegistered.Keep: - break; - default: - Throw.It(Error.UnableToRegisterDuplicateKey, serviceType, serviceKey, singleKeyedFactory); - break; - } - } - } - else // for default instance - { - var defaultFactories = singleKeyedOrManyDefaultFactories.LastDefaultKey == null - ? Empty() - : singleKeyedOrManyDefaultFactories.Factories.Enumerate() - .Match(it => it.Key is DefaultKey, it => it.Value) - .ToArrayOrSelf(); - - if (defaultFactories.Length == 0) // no default factories among the multiple existing keyed factories - { - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - } - else // there are existing default factories - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.AppendNotKeyed: - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - case IfAlreadyRegistered.Throw: - Throw.It(Error.UnableToRegisterDuplicateDefault, serviceType, defaultFactories); - break; - case IfAlreadyRegistered.Keep: - break; // entry does not change - case IfAlreadyRegistered.Replace: - var instanceFactories = defaultFactories.Match(f => f is InstanceFactory); - if (instanceFactories.Length == 1) - { - scope.SetOrAdd(instanceFactories[0].FactoryID, instance); - } - else // multiple default or a keyed factory - { - // scoped instance may be appended only, and not replacing anything - if (reuse == Reuse.Scoped) - { - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - } - else // here is the replacement goes on - { - var keyedFactories = singleKeyedOrManyDefaultFactories.Factories.Enumerate() - .Match(it => !(it.Key is DefaultKey)).ToArrayOrSelf(); - - if (keyedFactories.Length == 0) // replaces all default factories? - entry = new InstanceFactory(instance, instanceType, reuse, scope); - else - { - var factoriesEntry = FactoriesEntry.Empty; - for (var i = 0; i < keyedFactories.Length; i++) - factoriesEntry = factoriesEntry - .With(keyedFactories[i].Value, keyedFactories[i].Key); - entry = factoriesEntry - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - } - } - } - break; - case IfAlreadyRegistered.AppendNewImplementation: // otherwise Keep the old one - var duplicateImplIndex = defaultFactories.IndexOf( - x => x.CanAccessImplementationType && x.ImplementationType == instanceType); - if (duplicateImplIndex == -1) // add new implementation - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - // otherwise do nothing - keep the old entry - break; - } - } - } - } - } - - var hash = RuntimeHelpers.GetHashCode(serviceType); - var registry = r.WithServices(r.Services.AddOrUpdate(hash, serviceType, entry)); - - // clearing the resolution cache for the updated factory if any - if (oldEntry != null && oldEntry != entry) - { - var oldFactory = oldEntry as Factory; - if (oldFactory != null) - registry.DropFactoryCache(oldFactory, hash, serviceType); - else - ((FactoriesEntry)oldEntry).Factories.Enumerate().ToArray() - .ForEach(x => registry.DropFactoryCache(x.Value, hash, serviceType, serviceKey)); - } - - return registry; - }); - } - - void IResolverContext.UseInstance(Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey) => - UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - void IRegistrator.UseInstance(Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey) => - UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - void IResolverContext.InjectPropertiesAndFields(object instance, string[] propertyAndFieldNames) - { - var instanceType = instance.ThrowIfNull().GetType(); - - PropertiesAndFieldsSelector propertiesAndFields = null; - if (!propertyAndFieldNames.IsNullOrEmpty()) - { - var matchedMembers = instanceType.GetTypeInfo().DeclaredMembers.Match( - m => (m is PropertyInfo || m is FieldInfo) && propertyAndFieldNames.IndexOf(m.Name) != -1, - PropertyOrFieldServiceInfo.Of); - // todo: Should we throw when no props are found? - propertiesAndFields = matchedMembers.ToFunc>; - } - - propertiesAndFields = propertiesAndFields ?? Rules.PropertiesAndFields ?? PropertiesAndFields.Auto; - - var request = Request.Create(this, instanceType) - .WithResolvedFactory(new RegisteredInstanceFactory(instance, Reuse.Transient), - skipRecursiveDependencyCheck: true, skipCaptiveDependencyCheck: true); - - foreach (var serviceInfo in propertiesAndFields(request)) - if (serviceInfo != null) - { - var details = serviceInfo.Details; - var value = ((IResolver)this).Resolve(serviceInfo.ServiceType, details.ServiceKey, - details.IfUnresolved, details.RequiredServiceType, request, args: null); - if (value != null) - serviceInfo.SetValue(instance, value); - } - } - - /// Adding the factory directly to scope for resolution - public void Use(Type serviceType, FactoryDelegate factory) - { - (CurrentScope ?? SingletonScope).SetUsedInstance(serviceType, factory); - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - var cacheEntry = _registry.Value.GetCachedDefaultFactoryOrDefault(serviceTypeHash, serviceType); - if (cacheEntry != null) - { - ref var entry = ref cacheEntry.Value; - entry.Value = null; // reset the cache if any - } - } - -#endregion - -#region IContainer - - /// The rules object defines policies per container for registration and resolution. - public Rules Rules { get; private set; } - - /// Represents scope bound to container itself, and not the ambient (context) thing. - public IScope OwnCurrentScope => _ownCurrentScope; - - /// Indicates that container is disposed. - public bool IsDisposed => _disposed == 1 || _singletonScope.IsDisposed; - - /// - public IContainer With(Rules rules, IScopeContext scopeContext, RegistrySharing registrySharing, IScope singletonScope) => - With(_parent, rules, scopeContext, registrySharing, singletonScope, _ownCurrentScope); - - /// - public IContainer With(IResolverContext parent, Rules rules, IScopeContext scopeContext, - RegistrySharing registrySharing, IScope singletonScope, IScope curentScope) - { - ThrowIfContainerDisposed(); - - var registry = - registrySharing == RegistrySharing.Share ? _registry : - registrySharing == RegistrySharing.CloneButKeepCache ? Ref.Of(_registry.Value) - : Ref.Of(_registry.Value.WithoutCache()); - - return new Container(rules ?? Rules, registry, singletonScope ?? NewSingletonScope(), scopeContext, - curentScope ?? _ownCurrentScope, _disposed, _disposeStackTrace, parent ?? _parent); - } - - /// Produces new container which prevents any further registrations. - /// (optional) Controls what to do with the next registration: ignore or throw exception. - /// Throws exception by default. - public IContainer WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow = false) => - new Container(Rules, - Ref.Of(_registry.Value.WithNoMoreRegistrationAllowed(ignoreInsteadOfThrow)), - _singletonScope, _scopeContext, _ownCurrentScope, - _disposed, _disposeStackTrace, _parent); - - /// - public bool ClearCache(Type serviceType, FactoryType? factoryType, object serviceKey) - { - var hash = RuntimeHelpers.GetHashCode(serviceType); - - if (factoryType != null) - return _registry.Value.ClearCache(hash, serviceType, serviceKey, factoryType.Value); - - var registry = _registry.Value; - - var clearedServices = registry.ClearCache(hash, serviceType, serviceKey, FactoryType.Service); - var clearedWrapper = registry.ClearCache(hash, serviceType, serviceKey, FactoryType.Wrapper); - var clearedDecorator = registry.ClearCache(hash, serviceType, serviceKey, FactoryType.Decorator); - - return clearedServices || clearedWrapper || clearedDecorator; - } - - [MethodImpl((MethodImplOptions)256)] - internal Expression GetCachedFactoryExpression(int factoryId, Request request, out ImMapEntry slot) => - _registry.Value.GetCachedFactoryExpression(factoryId, request, out slot); - - [MethodImpl((MethodImplOptions) 256)] - internal void CacheFactoryExpression(int factoryId, Request request, Expression expr, ImMapEntry slot) => - _registry.Value.CacheFactoryExpression(factoryId, request, expr, slot); - - Factory IContainer.ResolveFactory(Request request) - { - var factory = ((IContainer)this).GetServiceFactoryOrDefault(request); - if (factory == null) - { - factory = GetWrapperFactoryOrDefault(request); - if (factory != null) - return factory; - - var unknownServiceResolvers = Rules.UnknownServiceResolvers; - if (!unknownServiceResolvers.IsNullOrEmpty()) - for (var i = 0; factory == null && i < unknownServiceResolvers.Length; i++) - factory = unknownServiceResolvers[i](request)?.DoNotCache(); - } - - if (factory?.FactoryGenerator != null) - factory = factory.FactoryGenerator.GetGeneratedFactory(request); - - if (factory == null) - TryThrowUnableToResolve(request); - - return factory; - } - - internal static void TryThrowUnableToResolve(Request request) - { - if (request.IfUnresolved != IfUnresolved.Throw) - return; - - var str = new StringBuilder(); - str = request.Container - .GetAllServiceFactories(request.ServiceType, bothClosedAndOpenGenerics: true) - .Aggregate(str, (s, x) => s - .Append(x.Value.Reuse?.CanApply(request) ?? true ? " " : " without matching scope ") - .Print(x)); - - if (str.Length != 0) - Throw.It(Error.UnableToResolveFromRegisteredServices, request, str); - else - Throw.It(Error.UnableToResolveUnknownService, request, - request.Rules.DynamicRegistrationProviders.EmptyIfNull().Length, - request.Rules.UnknownServiceResolvers.EmptyIfNull().Length); - } - - private Factory GetServiceFactoryOrDefaultForNullServiceKey(Request request, ServiceDetails details) - { - var requiredServiceType = details.RequiredServiceType; - var serviceType = requiredServiceType != null && requiredServiceType.IsOpenGeneric() - ? requiredServiceType - : request.GetActualServiceType(); - - if (Rules.FactorySelector != null) - return GetRuleSelectedServiceFactoryOrDefault(request, serviceType); - - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - if (entry == null && serviceType.GetTypeInfo().IsGenericType) - entry = serviceFactories.GetValueOrDefault(serviceType.GetGenericTypeDefinition()); - - // Most common case when we have a single default factory and no dynamic rules in addition - if (entry is Factory singleDefaultFactory && - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - { - if (singleDefaultFactory.Setup.Condition?.Invoke(request) == false || - (details.MetadataKey != null || details.Metadata != null) && - !singleDefaultFactory.Setup.MatchesMetadata(details.MetadataKey, details.Metadata)) - return null; - - return singleDefaultFactory; - } - - var factories = entry == null ? null - : entry is Factory factory ? new KV(DefaultKey.Value, factory).One() - : entry.To().Factories - .Visit(new List>(2), (x, list) => list.Add(KV.Of(x.Key, x.Value))) - .ToArray(); // todo: optimize - we may not need ToArray here - - if (!Rules.DynamicRegistrationProviders.IsNullOrEmpty() && - (factories.IsNullOrEmpty() || !Rules.UseDynamicRegistrationsAsFallbackOnly)) - factories = CombineRegisteredWithDynamicFactories(factories, true, FactoryType.Service, serviceType, null); - - if (factories.IsNullOrEmpty()) - return null; - - // First, filter out non default normal and dynamic factories - var defaultFactories = factories.Match(f => f.Key is DefaultKey || f.Key is DefaultDynamicKey); - if (defaultFactories.Length == 0) - return null; - - // For multiple matched factories, if the single one has a condition, then use it - var matchedFactories = defaultFactories.Match(x => request.MatchFactoryConditionAndMetadata(x)); - - // Check the for matching scopes. Only for more than 1 factory, - // for the single factory the check will be down the road - // BitBucket issue: #175 - if (matchedFactories.Length > 1 && request.Rules.ImplicitCheckForReuseMatchingScope) - { - var reuseMatchedFactories = matchedFactories.Match(x => request.MatchFactoryReuse(x)); - if (reuseMatchedFactories.Length == 1) - matchedFactories = reuseMatchedFactories; - else if (reuseMatchedFactories.Length > 1) - matchedFactories = FindFactoryWithTheMinReuseLifespan(matchedFactories)?.One() ?? matchedFactories; - - if (matchedFactories.Length == 1) - { - // Issue: #382 - // Add asResolutionCall for the factory to prevent caching of in-lined expression in context with not matching condition - if (request.IsResolutionCall) - request.ChangeServiceKey(matchedFactories[0].Key); - else // for injected dependency - matchedFactories[0].Value.Setup = matchedFactories[0].Value.Setup.WithAsResolutionCall(); - } - } - - // Match open-generic implementation with closed service type. Performance is OK because the generated factories are cached - - // so there should not be repeating of the check, and not match of Performance decrease. - if (matchedFactories.Length > 1) - matchedFactories = matchedFactories.Match(x => request.MatchGeneratedFactory(x)); - - if (matchedFactories.Length > 1) - { - var conditionedFactories = matchedFactories.Match(f => f.Value.Setup.Condition != null); - if (conditionedFactories.Length == 1) - matchedFactories = conditionedFactories; - } - - if (matchedFactories.Length > 1) - { - var preferedFactories = matchedFactories.Match(f => f.Value.Setup.PreferInSingleServiceResolve); - if (preferedFactories.Length == 1) - matchedFactories = preferedFactories; - } - - // The result is a single matched factory - if (matchedFactories.Length == 1) - { - // Changes service key for resolution call to identify single factory in cache and prevent wrong hit - if (defaultFactories.Length > 1 && request.IsResolutionCall) - request.ChangeServiceKey(matchedFactories[0].Key); - return matchedFactories[0].Value; - } - - if (matchedFactories.Length > 1 && request.IfUnresolved == IfUnresolved.Throw) - Throw.It(Error.ExpectedSingleDefaultFactory, matchedFactories, request); - - return null; - } - - private Factory GetServiceFactoryOrDefaultForServiceKey(Request request, ServiceDetails details, object serviceKey) - { - Type serviceType; - var requiredServiceType = details.RequiredServiceType; - if (requiredServiceType != null && requiredServiceType.IsOpenGeneric()) - serviceType = requiredServiceType; - else - { - serviceType = request.GetActualServiceType(); - - // Special case when open-generic required service type is encoded in ServiceKey as array of { ReqOpenGenServiceType, ServiceKey } - // presumes that required service type is closed generic - if (serviceKey is OpenGenericTypeKey openGenericTypeKey && - serviceType.GetTypeInfo().IsGenericType && serviceType.GetGenericTypeDefinition() == openGenericTypeKey.RequiredServiceType) - { - serviceType = openGenericTypeKey.RequiredServiceType; - serviceKey = openGenericTypeKey.ServiceKey; - } - } - - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - - // If the entry is not found or the key in entry is not found go for the open-generic services - if ((entry == null || - entry is Factory && !serviceKey.Equals(DefaultKey.Value) || - entry is FactoriesEntry facEntry && facEntry.Factories.GetValueOrDefault(serviceKey) == null) && - serviceType.IsClosedGeneric()) - entry = serviceFactories.GetValueOrDefault(serviceType.GetGenericTypeDefinition()) ?? entry; - - // Most common case when we have a single default factory and no dynamic rules in addition - if (entry is Factory singleDefaultFactory && - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - { - if (serviceKey != DefaultKey.Value || - singleDefaultFactory.Setup.Condition?.Invoke(request) == false || - (details.MetadataKey != null || details.Metadata != null) && - !singleDefaultFactory.Setup.MatchesMetadata(details.MetadataKey, details.Metadata)) - return null; - - return singleDefaultFactory; - } - - var factories = entry == null ? null - : entry is Factory factory ? new KV(DefaultKey.Value, factory).One() - : entry.To().Factories - .Visit(new List>(2), (x, list) => list.Add(KV.Of(x.Key, x.Value))) - .ToArray(); // todo: optimize - we may not need ToArray here - - if (!Rules.DynamicRegistrationProviders.IsNullOrEmpty() && - (factories.IsNullOrEmpty() || !Rules.UseDynamicRegistrationsAsFallbackOnly)) - factories = CombineRegisteredWithDynamicFactories(factories, true, FactoryType.Service, serviceType, serviceKey); - - if (factories.IsNullOrEmpty()) - return null; - - // For requested keyed service (which may be a `DefaultKey` or `DefaultDynamicKey`) - // just lookup for the key and return whatever the result - foreach (var f in factories) - if (serviceKey.Equals(f.Key) && f.Value.CheckCondition(request)) - return f.Value; - return null; - } - - Factory IContainer.GetServiceFactoryOrDefault(Request request) - { - var details = request.ServiceDetails; - if (details.ServiceKey == null) - return GetServiceFactoryOrDefaultForNullServiceKey(request, details); - return GetServiceFactoryOrDefaultForServiceKey(request, details, details.ServiceKey); - } - - private Factory GetRuleSelectedServiceFactoryOrDefault(Request request, Type serviceType) - { - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - - KV[] factories; - if (entry is Factory singleDefaultFactory) - { - // optimize for the most usual case - a single factory and no dynamic rules - if (Rules.UseDynamicRegistrationsAsFallbackOnly || - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return request.MatchFactoryConditionAndMetadata(singleDefaultFactory) - ? Rules.FactorySelector(request, DefaultKey.Value.Pair(singleDefaultFactory).One()) - : null; - - factories = new[] {new KV(DefaultKey.Value, singleDefaultFactory)}; - } - else if (entry is FactoriesEntry e) - { - factories = e.Factories.Visit(new List>(), (x, l) => l.Add(KV.Of(x.Key, x.Value))).ToArray(); - } - else - { - object openGenericEntry; - factories = Empty>(); - if (serviceType.IsClosedGeneric()) - { - var openGenericServiceType = serviceType.GetGenericTypeDefinition(); - openGenericEntry = serviceFactories.GetValueOrDefault(openGenericServiceType); - if (openGenericEntry != null) - factories = GetRegistryEntryKeyFactoryPairs(openGenericEntry).ToArrayOrSelf(); - } - } - - if ((factories.Length == 0 || !Rules.UseDynamicRegistrationsAsFallbackOnly) && - !Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - factories = CombineRegisteredWithDynamicFactories(factories, true, FactoryType.Service, serviceType); - - if (factories.Length == 0) - return null; - - // optimize for the case with the single factory - if (factories.Length == 1) - return request.MatchFactoryConditionAndMetadata(factories[0]) - ? Rules.FactorySelector(request, factories[0].Key.Pair(factories[0].Value).One()) - : null; - - // Sort in registration order - if (factories.Length > 1) - Array.Sort(factories, _lastFactoryIDWinsComparer); - - var matchedFactories = factories.Match(request.MatchFactoryConditionAndMetadata); - if (matchedFactories.Length > 1 && request.Rules.ImplicitCheckForReuseMatchingScope) - { - // Check the for matching scopes. Only for more than 1 factory, - // for the single factory the check will be down the road - // BitBucket issue: #175 - matchedFactories = matchedFactories.Match(request.MatchFactoryReuse); - if (matchedFactories.Length == 1) - { - // Issue: #382 - // Add asResolutionCall for the factory to prevent caching of in-lined expression in context with not matching condition - if (request.IsResolutionCall) - request.ChangeServiceKey(matchedFactories[0].Key); - else // for injected dependency - matchedFactories[0].Value.Setup = matchedFactories[0].Value.Setup.WithAsResolutionCall(); - } - } - - // Match open-generic implementation with closed service type. Performance is OK because the generated factories are cached - - // so there should not be repeating of the check, and not match of Performance decrease. - if (matchedFactories.Length > 1) - matchedFactories = matchedFactories.Match(request.MatchGeneratedFactory); - - if (matchedFactories.Length == 0) - return null; - - var selectedFactory = Rules.FactorySelector(request, matchedFactories.Map(x => x.Key.Pair(x.Value))); - if (selectedFactory == null) - return null; - - // Issue: #508 - if (factories.Length > 1) - { - var i = 0; - while (i < matchedFactories.Length && matchedFactories[i].Value.FactoryID != selectedFactory.FactoryID) - ++i; - if (i < matchedFactories.Length) - request.ChangeServiceKey(matchedFactories[i].Key); - } - - return selectedFactory; - } - - private static KV FindFactoryWithTheMinReuseLifespan(KV[] factories) - { - var minLifespan = int.MaxValue; - var multipleFactories = false; - KV minLifespanFactory = null; - for (var i = 0; i < factories.Length; i++) - { - var factory = factories[i]; - var reuse = factory.Value.Reuse; - var lifespan = reuse == null || reuse == Reuse.Transient ? int.MaxValue : reuse.Lifespan; - if (lifespan < minLifespan) - { - minLifespan = lifespan; - minLifespanFactory = factory; - multipleFactories = false; - } - else if (lifespan == minLifespan) - { - multipleFactories = true; - } - } - - return !multipleFactories && minLifespanFactory != null ? minLifespanFactory : null; - } - - IEnumerable> IContainer.GetAllServiceFactories(Type serviceType, bool bothClosedAndOpenGenerics) - { - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - - var factories = GetRegistryEntryKeyFactoryPairs(entry).ToArrayOrSelf(); - - if (bothClosedAndOpenGenerics && serviceType.IsClosedGeneric()) - { - var openGenericServiceType = serviceType.GetGenericTypeDefinition(); - var openGenericEntry = serviceFactories.GetValueOrDefault(openGenericServiceType); - if (openGenericEntry != null) - factories = factories.Append(GetRegistryEntryKeyFactoryPairs(openGenericEntry).ToArrayOrSelf()); - } - - if (!factories.IsNullOrEmpty() && Rules.UseDynamicRegistrationsAsFallbackOnly || - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return factories; - - return CombineRegisteredWithDynamicFactories(factories, bothClosedAndOpenGenerics, FactoryType.Service, serviceType); - } - - private static IEnumerable> GetRegistryEntryKeyFactoryPairs(object entry) => - entry == null - ? Empty>() - : entry is Factory ? new[] { new KV(DefaultKey.Value, (Factory)entry) } - // todo: optimize - : entry.To().Factories.Visit(new List>(), (x, l) => l.Add(KV.Of(x.Key, x.Value))).ToArray(); - - Expression IContainer.GetDecoratorExpressionOrDefault(Request request) - { - // return early if no decorators registered - if (_registry.Value.Decorators.IsEmpty && request.Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return null; - - var serviceType = request.ServiceType; - var arrayElementType = serviceType.GetArrayElementTypeOrNull(); - if (arrayElementType != null) - { - request = request.WithChangedServiceInfo(x => - x.With(typeof(IEnumerable<>).MakeGenericType(arrayElementType))); - serviceType = request.ServiceType; - } - - var container = request.Container; - var decorators = container.GetDecoratorFactoriesOrDefault(serviceType); - - // Combine with required service type if different from service type - var requiredServiceType = request.GetActualServiceType(); - if (requiredServiceType != serviceType) - decorators = decorators.Append(container.GetDecoratorFactoriesOrDefault(requiredServiceType)); - - // Define the list of ids for the already applied decorators - int[] appliedDecoratorIDs = null; - if (!decorators.IsNullOrEmpty()) - { - appliedDecoratorIDs = GetAppliedDecoratorIDs(request); - if (!appliedDecoratorIDs.IsNullOrEmpty()) - decorators = decorators.Match(appliedDecoratorIDs, (ids, d) => ids.IndexOf(d.FactoryID) == -1); - } - - // Append open-generic decorators - var genericDecorators = Empty(); - Type openGenericServiceType = null; - if (serviceType.GetTypeInfo().IsGenericType) - genericDecorators = container.GetDecoratorFactoriesOrDefault(openGenericServiceType = serviceType.GetGenericTypeDefinition()); - - // Combine with open-generic required type if they are different from service type - if (requiredServiceType != serviceType) - { - var openGenericRequiredType = requiredServiceType.GetTypeInfo().IsGenericType ? requiredServiceType.GetGenericTypeDefinition() : null; - if (openGenericRequiredType != null && openGenericRequiredType != openGenericServiceType) - genericDecorators = genericDecorators.Append( - container.GetDecoratorFactoriesOrDefault(openGenericRequiredType)); - } - - // Append generic type argument decorators, registered as Object - // Note: the condition for type arguments should be checked before generating the closed generic version - var typeArgDecorators = container.GetDecoratorFactoriesOrDefault(typeof(object)); - if (!typeArgDecorators.IsNullOrEmpty()) - genericDecorators = genericDecorators.Append( - typeArgDecorators.Match(request, (r, d) => d.CheckCondition(r))); - - // Filter out already applied generic decorators - // And combine with rest of decorators - if (!genericDecorators.IsNullOrEmpty()) - { - appliedDecoratorIDs = appliedDecoratorIDs ?? GetAppliedDecoratorIDs(request); - if (!appliedDecoratorIDs.IsNullOrEmpty()) - { - genericDecorators = genericDecorators - .Match(appliedDecoratorIDs, - (appliedDecIds, d) => - { - var factoryGenerator = d.FactoryGenerator; - if (factoryGenerator == null) - return appliedDecIds.IndexOf(d.FactoryID) == -1; - - foreach (var entry in factoryGenerator.GeneratedFactories.Enumerate()) - if (appliedDecIds.IndexOf(entry.Value.FactoryID) != -1) - return false; - - return true; - }); - } - - // Generate closed-generic versions - if (!genericDecorators.IsNullOrEmpty()) - { - genericDecorators = genericDecorators - .Map(request, (r, d) => d.FactoryGenerator == null ? d : d.FactoryGenerator.GetGeneratedFactory(r, ifErrorReturnDefault: true)) - .Match(d => d != null); - decorators = decorators.Append(genericDecorators); - } - } - - // Filter out the recursive decorators by doing the same recursive check - // that Request.WithResolvedFactory does. Fixes: #267 - if (!decorators.IsNullOrEmpty()) - decorators = decorators.Match(request, (r, d) => !r.HasRecursiveParent(d.FactoryID)); - - // Return earlier if no decorators found, or we have filtered out everything - if (decorators.IsNullOrEmpty()) - return null; - - Factory decorator; - if (decorators.Length == 1) - { - decorator = decorators[0]; - if (!decorator.CheckCondition(request)) - return null; - } - else - { - // Within the remaining decorators find one with the maximum Order - // or if no Order for all decorators, then find the last registered - with the biggest FactoryID - decorator = decorators - .OrderByDescending(d => ((Setup.DecoratorSetup)d.Setup).Order) - .ThenByDescending(d => d.RegistrationOrder) - .FirstOrDefault(d => d.CheckCondition(request)); - } - - var decoratorExpr = decorator?.GetExpressionOrDefault(request); - if (decoratorExpr == null) - return null; - - // decorator of arrays should be converted back from IEnumerable to array. - if (arrayElementType != null) - decoratorExpr = Call(WrappersSupport.ToArrayMethod.MakeGenericMethod(arrayElementType), decoratorExpr); - - return decoratorExpr; - } - - private static int[] GetAppliedDecoratorIDs(Request request) - { - var appliedIDs = Empty(); - for (var p = request.DirectParent; !p.IsEmpty && p.FactoryType != FactoryType.Service; p = p.DirectParent) - { - if (p.FactoryType == FactoryType.Decorator && - p.DecoratedFactoryID == request.FactoryID) - appliedIDs = appliedIDs.AppendOrUpdate(p.FactoryID); - } - return appliedIDs; - } - - Factory IContainer.GetWrapperFactoryOrDefault(Type serviceType) - { - var wrappers = _registry.Value.Wrappers; - var wrapper = wrappers.GetValueOrDefault(serviceType); - if (wrapper == null && serviceType.GetTypeInfo().IsGenericType) - wrapper = wrappers.GetValueOrDefault(serviceType.GetGenericTypeDefinition()); - return wrapper as Factory; - } - - Factory[] IContainer.GetDecoratorFactoriesOrDefault(Type serviceType) - { - var allDecorators = _registry.Value.Decorators; - var decorators = allDecorators.IsEmpty ? null : (Factory[])allDecorators.GetValueOrDefault(serviceType); - if (decorators == null) - { - if (Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return Empty(); - return CombineRegisteredWithDynamicFactories(null, true, FactoryType.Decorator, serviceType, null).Map(x => x.Value); - } - - if (Rules.UseDynamicRegistrationsAsFallbackOnly || - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return decorators; - - var decoratorsWithDefaultKey = decorators.Map(d => new KV(DefaultKey.Value, d)); - return CombineRegisteredWithDynamicFactories( - decoratorsWithDefaultKey, true, FactoryType.Decorator, serviceType, null).Map(x => x.Value); - } - - Type IContainer.GetWrappedType(Type serviceType, Type requiredServiceType) - { - if (requiredServiceType != null && requiredServiceType.IsOpenGeneric()) - return ((IContainer)this).GetWrappedType(serviceType, null); - - serviceType = requiredServiceType ?? serviceType; - - var wrappedType = serviceType.GetArrayElementTypeOrNull(); - if (wrappedType == null) - { - var factory = ((IContainer)this).GetWrapperFactoryOrDefault(serviceType); - if (factory != null) - { - wrappedType = ((Setup.WrapperSetup)factory.Setup).GetWrappedTypeOrNullIfWrapsRequired(serviceType); - if (wrappedType == null) - return null; - } - } - - return wrappedType == null ? serviceType - : ((IContainer)this).GetWrappedType(wrappedType, null); - } - - /// Converts known item into literal expression or wraps it in a constant expression. - public Expression GetConstantExpression(object item, Type itemType = null, bool throwIfStateRequired = false) - { - // Check for UsedForExpressionGeneration, and if not set just short-circuit to Expression.Constant - if (!throwIfStateRequired && !Rules.ThrowIfRuntimeStateRequired && !Rules.UsedForExpressionGeneration) - return itemType == null ? Constant(item) : Constant(item, itemType); - - if (item == null) - return itemType == null || itemType == typeof(object) ? Constant(null) : Constant(null, itemType); - - var convertible = item as IConvertibleToExpression; - if (convertible != null) - return convertible.ToExpression(it => GetConstantExpression(it, null, throwIfStateRequired)); - - var actualItemType = item.GetType(); - if (actualItemType.GetGenericDefinitionOrNull() == typeof(KV<,>)) - { - var kvArgTypes = actualItemType.GetGenericParamsAndArgs(); - return Call(_kvOfMethod.MakeGenericMethod(kvArgTypes), - GetConstantExpression(actualItemType.GetTypeInfo().GetDeclaredField("Key").GetValue(item), kvArgTypes[0], throwIfStateRequired), - GetConstantExpression(actualItemType.GetTypeInfo().GetDeclaredField("Value").GetValue(item), kvArgTypes[1], throwIfStateRequired)); - } - - if (actualItemType.IsPrimitive() || - actualItemType.IsAssignableTo()) - return itemType == null ? Constant(item) : Constant(item, itemType); - - // don't try to recover the non primitive type of element, - // cause it is a too much work to find the base common element type in array - var arrayElemType = actualItemType.GetArrayElementTypeOrNull(); - if (arrayElemType != null && arrayElemType != typeof(object) && - (arrayElemType.IsPrimitive() || actualItemType.IsAssignableTo())) - return NewArrayInit(arrayElemType, - ((object[])item).Map(x => GetConstantExpression(x, arrayElemType, throwIfStateRequired))); - - var itemExpr = Rules.ItemToExpressionConverter?.Invoke(item, itemType); - if (itemExpr != null) - return itemExpr; - - Throw.If(throwIfStateRequired || Rules.ThrowIfRuntimeStateRequired, - Error.StateIsRequiredToUseItem, item); - - return itemType == null ? Constant(item) : Constant(item, itemType); - } - - private static readonly MethodInfo _kvOfMethod = - typeof(KV).GetTypeInfo().GetDeclaredMethod(nameof(KV.Of)); - -#endregion - -#region Factories Add/Get - - internal sealed class FactoriesEntry - { - public readonly DefaultKey LastDefaultKey; - public readonly ImHashMap Factories; - - // lastDefaultKey may be null - public FactoriesEntry(DefaultKey lastDefaultKey, ImHashMap factories) - { - LastDefaultKey = lastDefaultKey; - Factories = factories; - } - - public static readonly FactoriesEntry Empty = - new FactoriesEntry(null, ImHashMap.Empty); - - public FactoriesEntry With(Factory factory, object serviceKey = null) - { - var lastDefaultKey = serviceKey == null - ? LastDefaultKey == null ? DefaultKey.Value : LastDefaultKey.Next() - : LastDefaultKey; - - return new FactoriesEntry(lastDefaultKey, Factories.AddOrUpdate(serviceKey ?? lastDefaultKey, factory)); - } - } - - private KV[] CombineRegisteredWithDynamicFactories( - KV[] registeredFactories, bool bothClosedAndOpenGenerics, - FactoryType factoryType, Type serviceType, object serviceKey = null) - { - Rules.DynamicRegistrationProvider[] dynamicRegistrationProviders = Rules.DynamicRegistrationProviders; - var serviceGenericTypeDefinition = bothClosedAndOpenGenerics && serviceType.IsClosedGeneric() - ? serviceType.GetGenericTypeDefinition() - : null; - - // Assign unique continious keys across all of dynamic providers, - // to prevent duplicate keys and peeking the wrong factory by collection wrappers - // NOTE: Given that dynamic registration always return the same implementation types in the same order - // then the dynamic key will be assigned deterministically, so that even if `CombineRegisteredWithDynamicFactories` - // is called multiple times during the resolution (like for `ResolveMany`) it is possible to match the required factory by its order. - var dynamicKey = DefaultDynamicKey.Value; - - var resultFactories = registeredFactories; - for (var i = 0; i < dynamicRegistrationProviders.Length; i++) - { - var dynamicRegistrationProvider = dynamicRegistrationProviders[i]; - - var dynamicRegistrations = dynamicRegistrationProvider(serviceType, serviceKey).ToArrayOrSelf(); - if (serviceGenericTypeDefinition != null) - dynamicRegistrations = dynamicRegistrations.Append(dynamicRegistrationProvider(serviceGenericTypeDefinition, serviceKey).ToArrayOrSelf()); - if (dynamicRegistrations.IsNullOrEmpty()) - continue; - - if (resultFactories == null) - { - resultFactories = dynamicRegistrations.Match(x => - x.Factory.FactoryType == factoryType && - x.Factory.ValidateAndNormalizeRegistration(serviceType, serviceKey, false, Rules), - x => KV.Of(x.ServiceKey ?? (dynamicKey = dynamicKey.Next()), x.Factory)); - continue; - } - - var remainingDynamicFactories = dynamicRegistrations - .Match(x => - { - if (x.Factory.FactoryType != factoryType || - !x.Factory.ValidateAndNormalizeRegistration(serviceType, serviceKey, false, Rules)) - return false; - - if (x.ServiceKey == null) // for the default dynamic factory - { - switch (x.IfAlreadyRegistered) - { - // accept the default if result factories don't contain it already - case IfAlreadyRegistered.Keep: - case IfAlreadyRegistered.Throw: - return resultFactories.IndexOf(f => f.Key is DefaultKey || f.Key is DefaultDynamicKey) == -1; - - // remove the default from the result factories - case IfAlreadyRegistered.Replace: - resultFactories = resultFactories.Match(f => !(f.Key is DefaultKey || f.Key is DefaultDynamicKey)); - return true; - - case IfAlreadyRegistered.AppendNotKeyed: - return true; - - case IfAlreadyRegistered.AppendNewImplementation: - // if we cannot access to dynamic implementation type, assume that the type is new implementation - if (!x.Factory.CanAccessImplementationType) - return true; - - // keep dynamic factory if there is no result factory with the same implementation type - return resultFactories.IndexOf(x, (s, f) => - f.Value.CanAccessImplementationType && f.Value.ImplementationType == s.Factory.ImplementationType) == -1; - } - } - else // for the keyed dynamic factory - { - switch (x.IfAlreadyRegistered) - { - // remove the result factory with the same key - case IfAlreadyRegistered.Replace: - resultFactories = resultFactories.Match(x.ServiceKey, (key, f) => !f.Key.Equals(key)); - return true; - - // keep the dynamic factory with the new service key, otherwise skip it - default: - return resultFactories.IndexOf(x.ServiceKey, (key, f) => f.Key.Equals(key)) == -1; - } - } - - return true; - }, - x => KV.Of(x.ServiceKey ?? (dynamicKey = dynamicKey.Next()), x.Factory)); - - resultFactories = resultFactories.Append(remainingDynamicFactories); - } - - return resultFactories; - } - - private static readonly LastFactoryIDWinsComparer _lastFactoryIDWinsComparer = new LastFactoryIDWinsComparer(); - private struct LastFactoryIDWinsComparer : IComparer> - { - public int Compare(KV first, KV next) => - (first?.Value.FactoryID ?? 0) - (next?.Value.FactoryID ?? 0); - } - - private Factory GetWrapperFactoryOrDefault(Request request) - { - // wrapper ignores the service key and propagates the service key to wrapped service - var serviceType = request.GetActualServiceType(); - - var serviceTypeInfo = serviceType.GetTypeInfo(); - if (serviceTypeInfo.IsArray) - serviceType = typeof(IEnumerable<>).MakeGenericType(serviceTypeInfo.GetElementType()); - - var factory = ((IContainer)this).GetWrapperFactoryOrDefault(serviceType); - if (factory?.FactoryGenerator != null) - factory = factory.FactoryGenerator.GetGeneratedFactory(request); - - if (factory == null) - return null; - - var condition = factory.Setup.Condition; - if (condition != null && !condition(request)) - return null; - - return factory; - } - -#endregion - -#region Implementation - - private int _disposed; - private StackTrace _disposeStackTrace; - - internal readonly Ref _registry; - - private readonly IScope _singletonScope; - private readonly IScope _ownCurrentScope; - private readonly IScopeContext _scopeContext; - private readonly IResolverContext _parent; - - internal sealed class InstanceFactory : Factory - { - public override Type ImplementationType { get; } - public override bool HasRuntimeState => true; - - public InstanceFactory(object instance, Type instanceType, IReuse reuse, IScope scopeToAdd = null) : base(reuse) - { - ImplementationType = instanceType; - scopeToAdd?.SetOrAdd(FactoryID, instance); - } - - /// Switched off until I (or someone) will figure it out. - public override bool UseInterpretation(Request request) => false; - - /// Tries to return instance directly from scope or sigleton, and fallbacks to expression for decorator. - public override FactoryDelegate GetDelegateOrDefault(Request request) - { - if (request.IsResolutionRoot) - { - var decoratedExpr = request.Container.GetDecoratorExpressionOrDefault(request.WithResolvedFactory(this)); - if (decoratedExpr != null) - return decoratedExpr.CompileToFactoryDelegate(request.Rules.UseFastExpressionCompiler, request.Rules.UseInterpretation); - } - - return GetInstanceFromScopeChainOrSingletons; - } - - /// Called for Injection as dependency. - public override Expression GetExpressionOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - return request.Container.GetDecoratorExpressionOrDefault(request) - ?? CreateExpressionOrDefault(request); - } - - public override Expression CreateExpressionOrDefault(Request request) => - Resolver.CreateResolutionExpression(request); - -#region Implementation - - private object GetInstanceFromScopeChainOrSingletons(IResolverContext r) - { - for (var scope = r.CurrentScope; scope != null; scope = scope.Parent) - { - var result = GetAndUnwrapOrDefault(scope, FactoryID); - if (result != null) - return result; - } - - var instance = GetAndUnwrapOrDefault(r.SingletonScope, FactoryID); - return instance.ThrowIfNull(Error.UnableToFindSingletonInstance); - } - - private static object GetAndUnwrapOrDefault(IScope scope, int factoryId) - { - object value; - if (!scope.TryGet(out value, factoryId)) - return null; - return (value as WeakReference)?.Target.ThrowIfNull(Error.WeakRefReuseWrapperGCed) - ?? (value as HiddenDisposable)?.Value - ?? value; - } - -#endregion - } - - internal sealed class Registry - { - public static readonly Registry Empty = new Registry(); - public static readonly Registry Default = new Registry(WrappersSupport.Wrappers); - - // Factories: - public readonly ImMap> Services; - // todo: we may use Factory or Factory[] as a value for decorators - public readonly ImMap> Decorators; // value is Factory[] - public readonly ImMap> Wrappers; // value is Factory - - internal const int CACHE_SLOT_COUNT = 16; - internal const int CACHE_SLOT_COUNT_MASK = CACHE_SLOT_COUNT - 1; - - public sealed class Compiling - { - public readonly Expression Expression; - public Compiling(Expression expression) => Expression = expression; - } - - public ImMap>[] DefaultFactoryCache; - - [MethodImpl((MethodImplOptions)256)] - public ImMapEntry> GetCachedDefaultFactoryOrDefault(int serviceTypeHash, Type serviceType) - { - // copy to local `cache` will prevent NRE if cache is set to null from outside - var cache = DefaultFactoryCache; - return cache == null ? null : cache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]?.GetEntryOrDefault(serviceTypeHash, serviceType); - } - - public void TryCacheDefaultFactory(int serviceTypeHash, Type serviceType, T factory) - { - // Disable caching when no services registered, not to cache an empty collection wrapper or alike. - if (Services.IsEmpty) - return; - - if (DefaultFactoryCache == null) - Interlocked.CompareExchange(ref DefaultFactoryCache, new ImMap>[CACHE_SLOT_COUNT], null); - - ref var map = ref DefaultFactoryCache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]; - if (map == null) - Interlocked.CompareExchange(ref map, ImMap>.Empty, null); - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrUpdate(serviceTypeHash, serviceType, factory), m) != m) - Ref.Swap(ref map, serviceTypeHash, serviceType, factory, (x, h, t, f) => x.AddOrUpdate(h, t, f)); - } - - internal sealed class KeyedFactoryCacheEntry - { - public readonly KeyedFactoryCacheEntry Rest; - public readonly object Key; - public object Factory; - public KeyedFactoryCacheEntry(KeyedFactoryCacheEntry rest, object key, object factory) - { - Rest = rest; - Key = key; - Factory = factory; - } - } - - // Where key is `KV.Of(ServiceKey | ScopeName | RequiredServiceType | KV.Of(ServiceKey, ScopeName | RequiredServiceType) | ...)` - // and value is `KeyedFactoryCacheEntries` - public ImMap>[] KeyedFactoryCache; - - [MethodImpl((MethodImplOptions)256)] - public bool GetCachedKeyedFactoryOrDefault(int serviceTypeHash, Type serviceType, object key, out KeyedFactoryCacheEntry result) - { - result = null; - var cache = KeyedFactoryCache; - if (cache != null) - { - var entry = cache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]?.GetEntryOrDefault(serviceTypeHash, serviceType); - if (entry != null) - for (var x = (KeyedFactoryCacheEntry)entry.Value.Value; x != null && result == null; x = x.Rest) - if (x.Key.Equals(key)) - result = x; - } - - return result != null; - } - - public void TryCacheKeyedFactory(int serviceTypeHash, Type serviceType, object key, object factory) - { - // Disable caching when no services registered, not to cache an empty collection wrapper or alike. - if (Services.IsEmpty) - return; - - if (KeyedFactoryCache == null) - Interlocked.CompareExchange(ref KeyedFactoryCache, new ImMap>[CACHE_SLOT_COUNT], null); - - ref var map = ref KeyedFactoryCache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]; - if (map == null) - Interlocked.CompareExchange(ref map, ImMap>.Empty, null); - - var entry = map.GetEntryOrDefault(serviceTypeHash, serviceType); - if (entry == null) - { - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(serviceTypeHash, serviceType), m) != m) - Ref.Swap(ref map, serviceTypeHash, serviceType, (x, h, t) => x.AddOrKeep(h, t)); - entry = map.GetEntryOrDefault(serviceTypeHash, serviceType); - } - - var e = entry.Value.Value; - if (Interlocked.CompareExchange(ref entry.Value.Value, SetOrAddKeyedCacheFactory(e, key, factory), e) != e) - Ref.Swap(ref entry.Value.Value, key, factory, SetOrAddKeyedCacheFactory); - } - - private object SetOrAddKeyedCacheFactory(object x, object k, object f) - { - for (var entry = (KeyedFactoryCacheEntry)x; entry != null; entry = entry.Rest) - { - if (entry.Key.Equals(k)) - { - entry.Factory = f; - return x; - } - } - - return new KeyedFactoryCacheEntry((KeyedFactoryCacheEntry)x, k, f); - } - - internal struct ExpressionCacheSlot - { - public Expression Transient; - public Expression Scoped; - public KeyValuePair[] ScopedToName; - } - - /// The int key is the `FactoryID` - public ImMap[] FactoryExpressionCache; - - public Expression GetCachedFactoryExpression( - int factoryId, Request request, out ImMapEntry entry) - { - entry = null; - var cache = FactoryExpressionCache; - if (cache != null) - { - var map = cache[factoryId & CACHE_SLOT_COUNT_MASK]; - if (map != null) - { - entry = map.GetEntryOrDefault(factoryId); - if (entry != null) - { - var reuse = request.Reuse; - if (reuse == Reuse.Transient) - return entry.Value.Transient; - - if (reuse is CurrentScopeReuse scoped) - { - if (scoped.Name == null) - return entry.Value.Scoped; - - var named = entry.Value.ScopedToName; - if (named != null) - for (var i = 0; i < named.Length; i++) - if (Equals(named[i].Key, scoped.Name)) - return named[i].Value; - } - } - } - } - - return null; - } - - internal void CacheFactoryExpression(int factoryId, Request request, Expression expr, ImMapEntry entry = null) - { - if (entry == null) - { - if (FactoryExpressionCache == null) - Interlocked.CompareExchange(ref FactoryExpressionCache, - new ImMap[CACHE_SLOT_COUNT], null); - - ref var map = ref FactoryExpressionCache[factoryId & CACHE_SLOT_COUNT_MASK]; - if (map == null) - Interlocked.CompareExchange(ref map, ImMap.Empty, null); - - entry = map.GetEntryOrDefault(factoryId); - if (entry == null) - { - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(factoryId), m) != m) - Ref.Swap(ref map, factoryId, (x, id) => x.AddOrKeep(id)); - entry = map.GetEntryOrDefault(factoryId); - } - } - - var reuse = request.Reuse; - if (reuse == Reuse.Transient) - { - entry.Value.Transient = expr; - } - else if (reuse is CurrentScopeReuse scoped) - { - if (scoped.Name == null) - entry.Value.Scoped = expr; - else - { - var named = entry.Value.ScopedToName; - if (named == null) - { - entry.Value.ScopedToName = scoped.Name.Pair(expr).One(); - } - else - { - var i = named.Length - 1; - for (; i >= 0; i--) - if (Equals(named[i].Key, scoped.Name)) - break; - if (i == -1) - { - var newNamed = new KeyValuePair[named.Length + 1]; - Array.Copy(named, 0, newNamed, 0, named.Length); - newNamed[named.Length] = scoped.Name.Pair(expr); - entry.Value.ScopedToName = newNamed; - } - } - } - } - } - - private enum IsChangePermitted { Permitted, Error, Ignored } - private readonly IsChangePermitted _isChangePermitted; - - private Registry(ImMap> wrapperFactories = null) - : this(ImMap>.Empty, ImMap>.Empty, wrapperFactories ?? ImMap>.Empty, - null, null, null, // caches are initialized to `null` to quickly check that they - IsChangePermitted.Permitted) - { } - - private Registry( - ImMap> services, - ImMap> decorators, - ImMap> wrappers, - ImMap>[] defaultFactoryCache, - ImMap>[] keyedFactoryCache, - ImMap[] factoryExpressionCache, - IsChangePermitted isChangePermitted) - { - Services = services; - Decorators = decorators; - Wrappers = wrappers; - DefaultFactoryCache = defaultFactoryCache; - KeyedFactoryCache = keyedFactoryCache; - FactoryExpressionCache = factoryExpressionCache; - _isChangePermitted = isChangePermitted; - } - - public Registry WithoutCache() => - new Registry(Services, Decorators, Wrappers, null, null, null, _isChangePermitted); - - internal Registry WithServices(ImMap> services) => - services == Services ? this : - new Registry(services, Decorators, Wrappers, - // Using Copy is fine when you have only the registrations because the caches will be null and no actual copy will be done. - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), - _isChangePermitted); - - private Registry WithDecorators(ImMap> decorators) => - decorators == Decorators ? this : - new Registry(Services, decorators, Wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), _isChangePermitted); - - private Registry WithWrappers(ImMap> wrappers) => - wrappers == Wrappers ? this : - new Registry(Services, Decorators, wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), _isChangePermitted); - - public IEnumerable GetServiceRegistrations() - { - foreach (var entry in Services.Enumerate()) - { - if (entry.Value.Value is Factory factory) - yield return new ServiceRegistrationInfo(factory, entry.Value.Key, null); - else - { - var factories = ((FactoriesEntry)entry.Value.Value).Factories; - foreach (var f in factories.Enumerate()) - yield return new ServiceRegistrationInfo(f.Value, entry.Value.Key, f.Key); - } - } - } - - public Registry Register(Factory factory, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered, object serviceKey) - { - if (_isChangePermitted != IsChangePermitted.Permitted) - return _isChangePermitted == IsChangePermitted.Ignored ? this - : Throw.For(Error.NoMoreRegistrationsAllowed, - serviceType, serviceKey != null ? "with key " + serviceKey : string.Empty, factory); - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - return factory.FactoryType == FactoryType.Service - ? serviceKey == null - ? WithDefaultService(factory, serviceTypeHash, serviceType, ifAlreadyRegistered) - : WithKeyedService(factory, serviceTypeHash, serviceType, ifAlreadyRegistered, serviceKey) - : factory.FactoryType == FactoryType.Decorator - ? WithDecorators(Decorators.AddOrUpdate(serviceTypeHash, serviceType, factory.One(), - (_, oldf, newf) => oldf.To().Append((Factory[])newf))) - : WithWrappers(Wrappers.AddOrUpdate(serviceTypeHash, serviceType, factory)); - } - - public Factory[] GetRegisteredFactories(Type serviceType, object serviceKey, FactoryType factoryType) - { - serviceType = serviceType.ThrowIfNull(); - switch (factoryType) - { - case FactoryType.Wrapper: - { - // first checking for the explicitly provided say `MyWrapper` - if (Wrappers.GetValueOrDefault(serviceType) is Factory wrapper) - return wrapper.One(); - - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null && - Wrappers.GetValueOrDefault(openGenServiceType) is Factory openGenWrapper) - return openGenWrapper.One(); - - if (serviceType.GetArrayElementTypeOrNull() != null && - Wrappers.GetValueOrDefault(typeof(IEnumerable<>)) is Factory collectionWrapper) - return collectionWrapper.One(); - - return null; - } - case FactoryType.Decorator: - { - var decorators = Decorators.GetValueOrDefault(serviceType) as Factory[]; - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null) - decorators = decorators.Append(Decorators.GetValueOrDefault(openGenServiceType) as Factory[]); - return decorators; - } - default: - { - var entry = Services.GetValueOrDefault(serviceType); - if (entry == null) - return null; - - if (entry is Factory factory) - return serviceKey == null || DefaultKey.Value.Equals(serviceKey) ? factory.One() : null; - - var factories = ((FactoriesEntry)entry).Factories; - if (serviceKey == null) // get all the factories - return factories.Visit(new List(), (x, l) => l.Add(x.Value)).ToArray(); - - return factories.GetValueOrDefault(serviceKey)?.One(); - } - } - } - - public bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, - Func condition) - { - serviceType = serviceType.ThrowIfNull(); - switch (factoryType) - { - case FactoryType.Wrapper: - { - // first checking for the explicitly provided say `MyWrapper` - if (Wrappers.GetValueOrDefault(serviceType) is Factory wrapper && - (condition == null || condition(wrapper))) - return true; - - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null && - Wrappers.GetValueOrDefault(openGenServiceType) is Factory openGenWrapper && - (condition == null || condition(openGenWrapper))) - return true; - - if (serviceType.GetArrayElementTypeOrNull() != null && - Wrappers.GetValueOrDefault(typeof(IEnumerable<>)) is Factory collectionWrapper && - (condition == null || condition(collectionWrapper))) - return true; - - return false; - } - case FactoryType.Decorator: - { - if (Decorators.GetValueOrDefault(serviceType) is Factory[] decorators && decorators.Length != 0 && - (condition == null || decorators.FindFirst(condition) != null)) - return true; - - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null && - Decorators.GetValueOrDefault(openGenServiceType) is Factory[] openGenDecorators && openGenDecorators.Length != 0 && - (condition == null || openGenDecorators.FindFirst(condition) != null)) - return true; - - return false; - } - default: // services - { - // note: We are not checking the open-generic for the closed-generic service type - // to be able to explicitly understand what registration is available - open or the closed-generic - var entry = Services.GetValueOrDefault(serviceType); - if (entry == null) - return false; - - if (entry is Factory factory) - return serviceKey == null || DefaultKey.Value.Equals(serviceKey) - ? condition == null || condition(factory) - : false; - - var factories = ((FactoriesEntry)entry).Factories; - if (serviceKey == null) - return condition == null || factories.FindFirstOrDefault(f => condition(f.Value)) != null; - - factory = factories.GetValueOrDefault(serviceKey); - return factory != null && (condition == null || condition(factory)); - } - } - } - - public bool ClearCache(int hash, Type serviceType, object serviceKey, FactoryType factoryType) - { - var factories = GetRegisteredFactories(serviceType, serviceKey, factoryType); - if (factories.IsNullOrEmpty()) - return false; - - for (var i = 0; i < factories.Length; i++) - DropFactoryCache(factories[i], hash, serviceType, serviceKey); - - return true; - } - private Registry WithDefaultService(Factory factory, int serviceTypeHash, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered) - { - var services = Services; - object newEntry = factory; - var oldEntry = services.GetValueOrDefault(serviceTypeHash, serviceType); - if (oldEntry != null) - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.AppendNotKeyed: - newEntry = (oldEntry as FactoriesEntry ?? FactoriesEntry.Empty.With((Factory)oldEntry)).With(factory); - break; - case IfAlreadyRegistered.Throw: - newEntry = oldEntry is FactoriesEntry oldFactoriesEntry && oldFactoriesEntry.LastDefaultKey == null - ? oldFactoriesEntry.With(factory) - : Throw.For(Error.UnableToRegisterDuplicateDefault, serviceType, factory, oldEntry); - break; - case IfAlreadyRegistered.Replace: - if (oldEntry is FactoriesEntry facEntryToReplace) - { - if (facEntryToReplace.LastDefaultKey == null) - newEntry = facEntryToReplace.With(factory); - else - { - // remove defaults but keep keyed (issue #569) by collecting the only keyed factories - // and using them in a new factory entry - var keyedFactories = facEntryToReplace.Factories.Fold( - ImHashMap.Empty, - (x, map) => x.Key is DefaultKey == false ? map.AddOrUpdate(x.Key, x.Value) : map); - if (!keyedFactories.IsEmpty) - newEntry = new FactoriesEntry(DefaultKey.Value, - keyedFactories.AddOrUpdate(DefaultKey.Value, factory)); - } - } - break; - case IfAlreadyRegistered.AppendNewImplementation: - var oldImplFacsEntry = oldEntry as FactoriesEntry; - if (oldImplFacsEntry != null && oldImplFacsEntry.LastDefaultKey == null) - newEntry = oldImplFacsEntry.With(factory); - else - { - var oldFactory = oldEntry as Factory; - var implementationType = factory.ImplementationType; - if (implementationType == null || - oldFactory != null && oldFactory.ImplementationType != implementationType) - newEntry = (oldImplFacsEntry ?? FactoriesEntry.Empty.With(oldFactory)).With(factory); - else if (oldImplFacsEntry != null) - { - var isNewImplType = true; - foreach (var f in oldImplFacsEntry.Factories.Enumerate()) - if (f.Value.ImplementationType == implementationType) - { - isNewImplType = false; - break; - } - - newEntry = isNewImplType - ? (oldImplFacsEntry ?? FactoriesEntry.Empty.With(oldFactory)).With(factory) - : oldEntry; - } - } - break; - default: // IfAlreadyRegisteredKeepDefaultService - newEntry = oldEntry is FactoriesEntry oldFacsEntry && oldFacsEntry.LastDefaultKey == null - ? oldFacsEntry.With(factory) - : oldEntry; - break; - } - } - - // services did not change - if (newEntry == oldEntry) - return this; - - var newServices = services.AddOrUpdate(serviceTypeHash, serviceType, newEntry); - var newRegistry = new Registry(newServices, Decorators, Wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), _isChangePermitted); - - if (oldEntry != null) - { - if (oldEntry is Factory oldFactory) - newRegistry.DropFactoryCache(oldFactory, serviceTypeHash, serviceType); - else if (oldEntry is FactoriesEntry oldFactoriesEntry && oldFactoriesEntry?.LastDefaultKey != null) - oldFactoriesEntry.Factories.Visit(new{ newRegistry, serviceTypeHash, serviceType }, (x, s) => - { - if (x.Key is DefaultKey) - s.newRegistry.DropFactoryCache(x.Value, s.serviceTypeHash, s.serviceType); - }); - } - - return newRegistry; - } - - private Registry WithKeyedService(Factory factory, int serviceTypeHash, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered, object serviceKey) - { - object newEntry = null; - var services = Services; - var oldEntry = services.GetValueOrDefault(serviceTypeHash, serviceType); - if (oldEntry != null) - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.Keep: - if (oldEntry is Factory factoryToKeep) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey).With(factoryToKeep); - else - { - var oldFacs = (FactoriesEntry)oldEntry; - if (oldFacs.Factories.Contains(serviceKey)) - return this; // keep the old registry - newEntry = new FactoriesEntry(oldFacs.LastDefaultKey, - oldFacs.Factories.AddOrUpdate(serviceKey, factory)); - } - break; - case IfAlreadyRegistered.Replace: - if (oldEntry is Factory factoryToReplace) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey).With(factoryToReplace); - else - newEntry = new FactoriesEntry(((FactoriesEntry)oldEntry).LastDefaultKey, - ((FactoriesEntry)oldEntry).Factories.AddOrUpdate(serviceKey, factory)); - break; - default: - if (oldEntry is Factory defaultFactory) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey).With(defaultFactory); - else - { - var oldFacs = (FactoriesEntry)oldEntry; - var oldFac = oldFacs.Factories.GetValueOrDefault(serviceKey); - if (oldFac != null) - Throw.It(Error.UnableToRegisterDuplicateKey, serviceKey, serviceKey, oldFac); - newEntry = new FactoriesEntry(oldFacs.LastDefaultKey, - ((FactoriesEntry)oldEntry).Factories.AddOrUpdate(serviceKey, factory)); - } - break; - } - } - - if (newEntry == null) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey); - - var newServices = services.AddOrUpdate(serviceTypeHash, serviceType, newEntry); - var newRegistry = new Registry(newServices, Decorators, Wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), - _isChangePermitted); - - if (oldEntry != null && ifAlreadyRegistered == IfAlreadyRegistered.Replace && - oldEntry is FactoriesEntry updatedOldFactories && - updatedOldFactories.Factories.TryFind(serviceKey, out var droppedFactory)) - newRegistry.DropFactoryCache(droppedFactory, serviceTypeHash, serviceType, serviceKey); - - return newRegistry; - } - - // todo: optimize allocations away - public Registry Unregister(FactoryType factoryType, Type serviceType, object serviceKey, Func condition) - { - if (_isChangePermitted != IsChangePermitted.Permitted) - return _isChangePermitted == IsChangePermitted.Ignored ? this - : Throw.For(Error.NoMoreUnregistrationsAllowed, - serviceType, serviceKey != null ? "with key " + serviceKey : string.Empty, factoryType); - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - switch (factoryType) - { - case FactoryType.Wrapper: - object removedWrapper = null; - var registry = WithWrappers(Wrappers.Update(serviceTypeHash, serviceType, null, (_, factory, _null) => - { - if (factory != null && condition != null && !condition((Factory)factory)) - return factory; - removedWrapper = factory; - return null; - })); - - if (removedWrapper == null) - return this; - registry.DropFactoryCache((Factory)removedWrapper, serviceTypeHash, serviceType); - return registry; - - case FactoryType.Decorator: - Factory[] removedDecorators = null; - // todo: minimize allocations in the lambdas below - if (condition == null) - registry = WithDecorators(Decorators.Update(serviceTypeHash, serviceType, null, (_, factories, _null) => - { - removedDecorators = (Factory[])factories; - return null; - })); - else - registry = WithDecorators(Decorators.Update(serviceTypeHash, serviceType, null, (_, factories, _null) => - { - removedDecorators = ((Factory[])factories).Match(condition); - return removedDecorators == factories ? null : factories.To().Except(removedDecorators).ToArray(); - })); - - if (removedDecorators.IsNullOrEmpty()) - return this; - - for (var i = 0; i < removedDecorators.Length; i++) - registry.DropFactoryCache(removedDecorators[i], serviceTypeHash, serviceType); - - return registry; - - default: - return UnregisterServiceFactory(serviceType, serviceKey, condition); - } - } - - // todo: optimize allocations away - private Registry UnregisterServiceFactory(Type serviceType, object serviceKey = null, Func condition = null) - { - object removed = null; // Factory or FactoriesEntry or Factory[] - ImMap> services; - var hash = RuntimeHelpers.GetHashCode(serviceType); - if (serviceKey == null && condition == null) // simplest case with simplest handling - services = Services.Update(hash, serviceType, null, (_, entry, _null) => - { - removed = entry; - return null; - }); - else - services = Services.Update(hash, serviceType, null, (_, entry, _null) => - { - if (entry == null) - return null; - - if (entry is Factory) - { - if ((serviceKey != null && !DefaultKey.Value.Equals(serviceKey)) || - (condition != null && !condition((Factory)entry))) - return entry; // keep entry - removed = entry; // otherwise remove it (the only case if serviceKey == DefaultKey.Value) - return null; - } - - var factoriesEntry = (FactoriesEntry)entry; - var oldFactories = factoriesEntry.Factories; - var remainingFactories = ImHashMap.Empty; - if (serviceKey == null) // automatically means condition != null - { - // keep factories for which condition is true - remainingFactories = oldFactories.Fold(remainingFactories, - (oldFac, remainingFacs) => condition != null && !condition(oldFac.Value) - ? remainingFacs.AddOrUpdate(oldFac.Key, oldFac.Value) - : remainingFacs); - } - else // serviceKey is not default, which automatically means condition == null - { - // set to null factory with specified key if its found - remainingFactories = oldFactories; - var factory = oldFactories.GetValueOrDefault(serviceKey); - if (factory != null) - remainingFactories = oldFactories.Height > 1 - ? oldFactories.UpdateToDefault(serviceKey.GetHashCode(), serviceKey) - : ImHashMap.Empty; - } - - if (remainingFactories.IsEmpty) - { - // if no more remaining factories, then delete the whole entry - removed = entry; - return null; - } - - // todo: huh - no perf here? - removed = oldFactories.Enumerate().Except(remainingFactories.Enumerate()).Select(f => f.Value).ToArray(); - - if (remainingFactories.Height == 1 && DefaultKey.Value.Equals(remainingFactories.Key)) - return remainingFactories.Value; // replace entry with single remaining default factory - - // update last default key if current default key was removed - var newDefaultKey = factoriesEntry.LastDefaultKey; - if (newDefaultKey != null && remainingFactories.GetValueOrDefault(newDefaultKey) == null) - newDefaultKey = remainingFactories.Enumerate().Select(x => x.Key) - .OfType().OrderByDescending(key => key.RegistrationOrder).FirstOrDefault(); - return new FactoriesEntry(newDefaultKey, remainingFactories); - }); - - if (removed == null) - return this; - - var registry = WithServices(services); - - var removedFactory = removed as Factory; - if (removedFactory != null) - registry.DropFactoryCache(removedFactory, hash, serviceType, serviceKey); - else - (removed as Factory[] ?? - ((FactoriesEntry)removed).Factories.Enumerate().Select(f => f.Value).ToArray()) - .ForEach(x => registry.DropFactoryCache(x, hash, serviceType, serviceKey)); - - return registry; - } - - internal void DropFactoryCache(Factory factory, int hash, Type serviceType, object serviceKey = null) - { - if (DefaultFactoryCache != null || KeyedFactoryCache != null) - { - if (factory.FactoryGenerator == null) - { - var d = DefaultFactoryCache; - if (d != null) - Ref.Swap(ref d[hash & CACHE_SLOT_COUNT_MASK], hash, serviceType, - (x, h, t) => (x ?? ImMap>.Empty).UpdateToDefault(h, t)); - - var k = KeyedFactoryCache; - if (k != null) - Ref.Swap(ref k[hash & CACHE_SLOT_COUNT_MASK], hash, serviceType, - (x, h, t) => (x ?? ImMap>.Empty).UpdateToDefault(h, t)); - } - else - { - // We cannot remove generated factories, because they are keyed by implementation type and we may remove wrong factory - // a safe alternative is dropping the whole cache - DefaultFactoryCache = null; - KeyedFactoryCache = null; - } - } - - if (FactoryExpressionCache != null) - { - var e = FactoryExpressionCache; - if (e != null) - Ref.Swap(ref e[factory.FactoryID & CACHE_SLOT_COUNT_MASK], - factory.FactoryID, (x, i) => (x ?? ImMap.Empty).UpdateToDefault(i)); - } - } - - public Registry WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow) => - new Registry(Services, Decorators, Wrappers, - DefaultFactoryCache, KeyedFactoryCache, FactoryExpressionCache, - ignoreInsteadOfThrow ? IsChangePermitted.Ignored : IsChangePermitted.Error); - } - - private Container(Rules rules, Ref registry, IScope singletonScope, - IScopeContext scopeContext = null, IScope ownCurrentScope = null, - int disposed = 0, StackTrace disposeStackTrace = null, - IResolverContext parent = null) - { - Rules = rules; - - _registry = registry; - - _singletonScope = singletonScope; - _scopeContext = scopeContext; - _ownCurrentScope = ownCurrentScope; - - _disposed = disposed; - _disposeStackTrace = disposeStackTrace; - - _parent = parent; - } - - private void SetInitialFactoryID() - { - var lastGeneratedId = 0; - GetLastGeneratedFactoryID(ref lastGeneratedId); - if (lastGeneratedId > Factory._lastFactoryID) - Factory._lastFactoryID = lastGeneratedId + 1; - } - - #endregion - } - - /// Special service key with info about open-generic service type - public sealed class OpenGenericTypeKey : IConvertibleToExpression - { - /// Open-generic required service-type - public readonly Type RequiredServiceType; - - /// Optional key - public readonly object ServiceKey; - - /// Constructs the thing - public OpenGenericTypeKey(Type requiredServiceType, object serviceKey) - { - RequiredServiceType = requiredServiceType.ThrowIfNull(); - ServiceKey = serviceKey; - } - - /// - public override string ToString() => - new StringBuilder(nameof(OpenGenericTypeKey)).Append('(') - .Print(RequiredServiceType).Append(", ").Print(ServiceKey) - .Append(')').ToString(); - - /// - public override bool Equals(object obj) - { - var other = obj as OpenGenericTypeKey; - return other != null && - other.RequiredServiceType == RequiredServiceType && - Equals(other.ServiceKey, ServiceKey); - } - - /// - public override int GetHashCode() => Hasher.Combine(RequiredServiceType, ServiceKey); - - /// - public Expression ToExpression(Func fallbackConverter) => - New(_ctor, Constant(RequiredServiceType, typeof(Type)), fallbackConverter(ServiceKey)); - - private static readonly ConstructorInfo _ctor = typeof(OpenGenericTypeKey) - .GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 2); - } - - ///Hides/wraps object with disposable interface. - public sealed class HiddenDisposable - { - internal static ConstructorInfo Ctor = typeof(HiddenDisposable).GetTypeInfo().DeclaredConstructors.First(); - internal static FieldInfo ValueField = typeof(HiddenDisposable).GetTypeInfo().GetDeclaredField(nameof(Value)); - - /// Wrapped value - public readonly object Value; - - /// Wraps the value - public HiddenDisposable(object value) { Value = value; } - } - - /// Interpreter of expression - where possible uses knowledge of DryIoc internals to avoid reflection - public static class Interpreter - { - /// Calls `TryInterpret` inside try-catch and unwraps/re-throws `ContainerException` from the reflection `TargetInvocationException` - public static bool TryInterpretAndUnwrapContainerException( - IResolverContext r, Expression expr, bool useFec, out object result) - { - try - { - return Interpreter.TryInterpret(r, expr, FactoryDelegateCompiler.ResolverContextParamExpr, r, null, useFec, out result); - } - catch (TargetInvocationException tex) when (tex.InnerException != null) - { - // restore the original exception which is expected by the consumer code - tex.InnerException.TryRethrowWithPreservedStackTrace(); - result = null; - return false; - } - } - - /// Stores parent lambda params and args - public sealed class ParentLambdaArgs - { - /// Parent or the `null` for the root - public readonly ParentLambdaArgs ParentWithArgs; - - /// Params - public readonly object ParamExprs; - - /// Args - public readonly object ParamValues; - - /// Constructs with parent parent or `null` for the root - public ParentLambdaArgs(ParentLambdaArgs parentWithArgs, object paramExprs, object paramValues) - { - ParentWithArgs = parentWithArgs; - ParamExprs = paramExprs; - ParamValues = paramValues; - } - } - - //private static object GetPoolOrNewObjects(object[][] objsPool, int count) => - // count < 8 ? objsPool[count - 1] ?? new object[count] : new object[count]; - - //private static void ReturnObjecstsToPool(object[][] objsPool, object[] objs) - //{ - // var length = objs.Length; - // if (length < 8) - // objsPool[length - 1] = objs; - //} - - /// Interprets passed expression - public static bool TryInterpret(IResolverContext r, Expression expr, - object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec, out object result - //, object[][] objsPools = null - ) - { - result = null; - switch (expr.NodeType) - { - case ExprType.Constant: - { - result = ((ConstantExpression)expr).Value; - return true; - } - case ExprType.New: - { - var newExpr = (NewExpression)expr; -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgCount = newExpr.FewArgumentCount; - if (fewArgCount >= 0) - { - if (fewArgCount == 0) - { - result = newExpr.Constructor.Invoke(ArrayTools.Empty()); - return true; - } - - if (fewArgCount == 1) - { - var fewArgs = new object[1]; - var fewArgsExpr = ((OneArgumentNewExpression)newExpr).Argument; - if (!TryInterpret(r, fewArgsExpr, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - - if (fewArgCount == 2) - { - var fewArgs = new object[2]; - var fewArgsExpr = ((TwoArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - - if (fewArgCount == 3) - { - var fewArgs = new object[3]; - var fewArgsExpr = ((ThreeArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - - if (fewArgCount == 4) - { - var fewArgs = new object[4]; - var fewArgsExpr = ((FourArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - if (fewArgCount == 5) - { - var fewArgs = new object[5]; - var fewArgsExpr = ((FiveArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3]) || - !TryInterpret(r, fewArgsExpr.Argument4, paramExprs, paramValues, parentArgs, useFec, out fewArgs[4])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - } -#endif - var newArgs = newExpr.Arguments.ToListOrSelf(); - var newArgCount = newArgs.Count; - if (newArgCount == 0) - result = newExpr.Constructor.Invoke(ArrayTools.Empty()); - else - { - var args = new object[newArgCount]; - for (var i = 0; i < args.Length; i++) - if (!TryInterpret(r, newArgs[i], paramExprs, paramValues, parentArgs, useFec, out args[i])) - return false; - result = newExpr.Constructor.Invoke(args); - } - return true; - } - case ExprType.Call: - { - return TryInterpretMethodCall(r, expr, paramExprs, paramValues, parentArgs, useFec, ref result); - } - case ExprType.Convert: - { - var convertExpr = (UnaryExpression)expr; - if (!TryInterpret(r, convertExpr.Operand, paramExprs, paramValues, parentArgs, useFec, out var instance)) - return false; - // skip conversion for null and for directly assignable type - if (instance == null || instance.GetType().IsAssignableTo(convertExpr.Type)) - result = instance; - else - result = Converter.ConvertWithOperator(instance, convertExpr.Type, expr); - return true; - } - case ExprType.MemberAccess: - { - var memberExpr = (MemberExpression)expr; - var instanceExpr = memberExpr.Expression; - object instance = null; - if (instanceExpr != null && !TryInterpret(r, instanceExpr, paramExprs, paramValues, parentArgs, useFec, out instance)) - return false; - - if (memberExpr.Member is FieldInfo field) - { - result = field.GetValue(instance); - return true; - } - - if (memberExpr.Member is PropertyInfo prop) - { - result = prop.GetValue(instance, null); - return true; - } - - return false; - } - case ExprType.MemberInit: - { - var memberInit = (MemberInitExpression)expr; - if (!TryInterpret(r, memberInit.NewExpression, paramExprs, paramValues, parentArgs, useFec, out var instance)) - return false; - - var bindings = memberInit.Bindings; - for (var i = 0; i < bindings.Count; i++) - { - var binding = (MemberAssignment)bindings[i]; - if (!TryInterpret(r, binding.Expression, paramExprs, paramValues, parentArgs, useFec, out var memberValue)) - return false; - - var field = binding.Member as FieldInfo; - if (field != null) - field.SetValue(instance, memberValue); - else - ((PropertyInfo)binding.Member).SetValue(instance, memberValue, null); - } - - result = instance; - return true; - } - case ExprType.NewArrayInit: - { - var newArray = (NewArrayExpression)expr; - var itemExprs = newArray.Expressions.ToListOrSelf(); - var items = new object[itemExprs.Count]; - - for (var i = 0; i < items.Length; i++) - if (!TryInterpret(r, itemExprs[i], paramExprs, paramValues, parentArgs, useFec, out items[i])) - return false; - - result = Converter.ConvertMany(items, newArray.Type.GetElementType()); - return true; - } - case ExprType.Invoke: - { - var invokeExpr = (InvocationExpression)expr; - var delegateExpr = invokeExpr.Expression; - - // The majority of cases the delegate will be a well known `FactoryDelegate` - so calling it directly - if (delegateExpr.Type == typeof(FactoryDelegate) && - delegateExpr is ConstantExpression delegateConstExpr) - { - if (!TryInterpret(r, invokeExpr.Arguments[0], paramExprs, paramValues, parentArgs, useFec, out var resolver)) - return false; - result = ((FactoryDelegate)delegateConstExpr.Value)((IResolverContext)resolver); - return true; - } - -#if !SUPPORTS_DELEGATE_METHOD - return false; -#else - if (!TryInterpret(r, delegateExpr, paramExprs, paramValues, parentArgs, useFec, out var delegateObj)) - return false; - var lambda = (Delegate)delegateObj; - - var argExprs = invokeExpr.Arguments.ToListOrSelf(); - if (argExprs.Count == 0) - result = lambda.GetMethodInfo().Invoke(lambda.Target, ArrayTools.Empty()); - else - { - var args = new object[argExprs.Count]; - for (var i = 0; i < args.Length; i++) - if (!TryInterpret(r, argExprs[i], paramExprs, paramValues, parentArgs, useFec, out args[i])) - return false; - result = lambda.GetMethodInfo().Invoke(lambda.Target, args); - } - return true; -#endif - } - case ExprType.Parameter: - { - if (expr == paramExprs) - { - result = paramValues; - return true; - } - - if (paramExprs is IList multipleParams) - for (var i = 0; i < multipleParams.Count; i++) - if (expr == multipleParams[i]) - { - result = ((object[])paramValues)[i]; - return true; - } - - if (parentArgs != null) - { - for (var p = parentArgs; p != null; p = p.ParentWithArgs) - { - if (expr == p.ParamExprs) - { - result = p.ParamValues; - return true; - } - - multipleParams = p.ParamExprs as IList; - if (multipleParams != null) - { - for (var i = 0; i < multipleParams.Count; i++) - if (expr == multipleParams[i]) - { - result = ((object[])paramValues)[i]; - return true; - } - } - } - } - return false; - } - case ExprType.Lambda: - { - return TryInterpretNestedLambda(r, (LambdaExpression)expr, paramExprs, paramValues, parentArgs, useFec, ref result); - } - default: - break; - } - - return false; - } - - private static bool TryInterpretNestedLambda(IResolverContext r, LambdaExpression lambdaExpr, - object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec, ref object result) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var returnType = lambdaExpr.ReturnType; -#else - var returnType = lambdaExpr.Type.GetTypeInfo().GetDeclaredMethod("Invoke").ReturnType; -#endif - if (paramExprs != null) - parentArgs = new ParentLambdaArgs(parentArgs, paramExprs, paramValues); - - var bodyExpr = lambdaExpr.Body; - var lambdaParams = lambdaExpr.Parameters; - var paramCount = lambdaParams.Count; - if (paramCount == 0) - { - if (returnType != typeof(void)) - { - result = new Func(() => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, null, null, parentArgs, useFec)); - if (returnType != typeof(object)) - result = _convertFuncMethod.MakeGenericMethod(returnType).Invoke(null, new[] { result }); - } - else - { - result = new Action(() => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, null, null, parentArgs, useFec)); - } - } - else if (paramCount == 1) - { - var paramExpr = lambdaParams[0]; - if (returnType != typeof(void)) - { - result = new Func(arg => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, paramExpr, arg, parentArgs, useFec)); - if (paramExpr.Type != typeof(object) || returnType != typeof(object)) - result = _convertOneArgFuncMethod.MakeGenericMethod(paramExpr.Type, returnType).Invoke(null, new[] { result }); - } - else - { - result = new Action(arg => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, paramExpr, arg, parentArgs, useFec)); - if (paramExpr.Type != typeof(object)) - result = _convertOneArgActionMethod.MakeGenericMethod(paramExpr.Type).Invoke(null, new[] { result }); - } - } - else if (paramCount == 2) - { - var paramExpr0 = lambdaParams[0]; - var paramExpr1 = lambdaParams[1]; - if (returnType != typeof(void)) - { - result = new Func((arg0, arg1) => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, new[] { arg0, arg1 }, parentArgs, useFec)); - - if (paramExpr0.Type != typeof(object) || paramExpr1.Type != typeof(object) || returnType != typeof(object)) - result = _convertTwoArgFuncMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, returnType).Invoke(null, new[] { result }); - } - else - { - result = new Action((arg0, arg1) => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, new[] { arg0, arg1 }, parentArgs, useFec)); - - if (paramExpr0.Type != typeof(object) || paramExpr1.Type != typeof(object)) - result = _convertTwoArgActionMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type).Invoke(null, new[] { result }); - } - } - else if (paramCount == 3) - { - var paramExpr0 = lambdaParams[0]; - var paramExpr1 = lambdaParams[1]; - var paramExpr2 = lambdaParams[2]; - if (returnType != typeof(void)) - { - result = new Func(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertThreeArgFuncMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type, returnType) - .Invoke(null, new[] { result }); - } - else - { - result = new Action(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertThreeArgActionMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type) - .Invoke(null, new[] { result }); - } - } - else if (paramCount == 4) - { - var paramExpr0 = lambdaParams[0]; - var paramExpr1 = lambdaParams[1]; - var paramExpr2 = lambdaParams[2]; - var paramExpr3 = lambdaParams[3]; - if (returnType != typeof(void)) - { - result = new Func(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertFourArgFuncMethod - .MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type, paramExpr3.Type, returnType) - .Invoke(null, new[] {result}); - } - else - { - result = new Action(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertFourArgActionMethod - .MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type, paramExpr3.Type) - .Invoke(null, new[] {result}); - } - } - else - return false; - - var resultType = result.GetType(); - var lambdaType = lambdaExpr.Type; - if ((resultType.GetGenericDefinitionOrNull() ?? resultType) != (lambdaType.GetGenericDefinitionOrNull() ?? lambdaType)) - { -#if SUPPORTS_DELEGATE_METHOD - result = ((Delegate)result).GetMethodInfo().CreateDelegate(lambdaType, ((Delegate)result).Target); -#else - return false; -#endif - } - - return true; - } - - private static object TryInterpretNestedLambdaBodyAndUnwrapException(IResolverContext r, - Expression bodyExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { - try - { - if (!TryInterpret(r, bodyExpr, paramExprs, paramValues, parentArgs, useFec, out var lambdaResult)) - Throw.It(Error.UnableToInterpretTheNestedLambda, bodyExpr); - return lambdaResult; - } - catch (TargetInvocationException tex) when (tex.InnerException != null) - { - // restore the original excpetion which is expected by the consumer code - throw tex.InnerException; - } - } - - internal static Func ConvertFunc(Func f) => () => (R)f(); - private static readonly MethodInfo _convertFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertFunc)); - - internal static Func ConvertOneArgFunc(Func f) => a => (R)f(a); - private static readonly MethodInfo _convertOneArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertOneArgFunc)); - - internal static Action ConvertOneArgAction(Action f) => a => f(a); - private static readonly MethodInfo _convertOneArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertOneArgAction)); - - internal static Func ConvertTwoArgFunc(Func f) => (a0, a1) => (R)f(a0, a1); - private static readonly MethodInfo _convertTwoArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertTwoArgFunc)); - - internal static Action ConvertTwoArgAction(Action f) => (a0, a1) => f(a0, a1); - private static readonly MethodInfo _convertTwoArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertTwoArgAction)); - - internal static Func ConvertThreeArgFunc(Func f) => (a0, a1, a2) => (R)f(new object[] {a0, a1, a2}); - private static readonly MethodInfo _convertThreeArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertThreeArgFunc)); - - internal static Action ConvertThreeArgAction(Action f) => (a0, a1, a2) => f(new object[] {a0, a1, a2}); - private static readonly MethodInfo _convertThreeArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertThreeArgAction)); - - internal static Func ConvertFourArgFunc(Func f) => (a0, a1, a2, a3) => (R)f(new object[] { a0, a1, a2, a3 }); - private static readonly MethodInfo _convertFourArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertFourArgFunc)); - - internal static Action ConvertFourArgAction(Action f) => (a0, a1, a2, a3) => f(new object[] { a0, a1, a2, a3 }); - private static readonly MethodInfo _convertFourArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertFourArgAction)); - - private static bool TryInterpretMethodCall(IResolverContext r, Expression expr, - object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec, ref object result) - { - if (ReferenceEquals(expr, ResolverContext.RootOrSelfExpr)) - { - result = r.Root ?? r; - return true; - } - - var callExpr = (MethodCallExpression)expr; - var method = callExpr.Method; - var methodDeclaringType = method.DeclaringType; - - if (methodDeclaringType == typeof(CurrentScopeReuse)) - { - if (method == CurrentScopeReuse.GetScopedViaFactoryDelegateNoDisposalIndexMethod) - { - result = InterpretGetScopedViaFactoryDelegateNoDisposalIndex(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - if (method == CurrentScopeReuse.GetScopedViaFactoryDelegateMethod) - { - result = InterpretGetScopedViaFactoryDelegate(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - if (method == CurrentScopeReuse.GetNameScopedViaFactoryDelegateMethod) - { - result = InterpretGetNameScopedViaFactoryDelegate(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - if (method == CurrentScopeReuse.GetScopedOrSingletonViaFactoryDelegateMethod) - { - result = InterpretGetScopedOrSingletonViaFactoryDelegate(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - var callArgs = callExpr.Arguments.ToListOrSelf(); - var resolver = r; - if (!ReferenceEquals(callArgs[0], FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, callArgs[0], paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - if (method == CurrentScopeReuse.TrackScopedOrSingletonMethod) - { - if (!TryInterpret(resolver, callArgs[1], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = CurrentScopeReuse.TrackScopedOrSingleton(resolver, service); - return true; - } - - if (method == CurrentScopeReuse.TrackScopedMethod) - { - var scope = resolver.GetCurrentScope((bool)((ConstantExpression)callArgs[1]).Value); - if (scope == null) - result = null; // result is null in this case - else - { - if (!TryInterpret(resolver, callArgs[2], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = scope.TrackDisposable(service /* todo: what is with `disposalOrder`*/); - } - - return true; - } - - if (method == CurrentScopeReuse.TrackNameScopedMethod) - { - var scope = resolver.GetNamedScope(ConstValue(callArgs[1]), (bool)ConstValue(callArgs[2])); - if (scope == null) - result = null; // result is null in this case - else - { - if (!TryInterpret(resolver, callArgs[3], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = scope.TrackDisposable(service); - } - - return true; - } - } - else if (methodDeclaringType == typeof(IScope)) - { - var callArgs = callExpr.Arguments.ToListOrSelf(); - if (method == Scope.GetOrAddViaFactoryDelegateMethod) - { - r = r.Root ?? r; - - // check if scoped dependency is already in scope, then just return it - var factoryId = (int) ConstValue(callArgs[0]); - if (!r.SingletonScope.TryGet(out result, factoryId)) - { - result = r.SingletonScope.TryGetOrAddWithoutClosure(factoryId, r, - ((LambdaExpression) callArgs[1]).Body, useFec, - (rc, e, uf) => - { - if (TryInterpret(rc, e, paramExprs, paramValues, parentArgs, uf, out var value)) - return value; - return e.CompileToFactoryDelegate(uf, ((IContainer) rc).Rules.UseInterpretation)(rc); - }, - (int) ConstValue(callArgs[3])); - } - - return true; - } - - if (method == Scope.TrackDisposableMethod) - { - r = r.Root ?? r; - if (!TryInterpret(r, callArgs[0], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = r.SingletonScope.TrackDisposable(service, (int) ConstValue(callArgs[1])); - return true; - } - } - else if (methodDeclaringType == typeof(IResolver)) - { - var resolver = r; - if (!ReferenceEquals(callExpr.Object, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, callExpr.Object, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - var callArgs = callExpr.Arguments.ToListOrSelf(); - if (method == Resolver.ResolveFastMethod) - { - result = resolver.Resolve((Type) ConstValue(callArgs[0]), (IfUnresolved) ConstValue(callArgs[1])); - return true; - } - - if (method == Resolver.ResolveMethod) - { - object serviceKey = null, preResolveParent = null, resolveArgs = null; - if (!TryInterpret(resolver, callArgs[1], paramExprs, paramValues, parentArgs, useFec, out serviceKey) || - !TryInterpret(resolver, callArgs[4], paramExprs, paramValues, parentArgs, useFec, out preResolveParent) || - !TryInterpret(resolver, callArgs[5], paramExprs, paramValues, parentArgs, useFec, out resolveArgs)) - return false; - - result = resolver.Resolve((Type) ConstValue(callArgs[0]), serviceKey, - (IfUnresolved) ConstValue(callArgs[2]), - (Type) ConstValue(callArgs[3]), (Request) preResolveParent, (object[]) resolveArgs); - return true; - } - - if (method == Resolver.ResolveManyMethod) - { - object serviceKey = null, preResolveParent = null, resolveArgs = null; - if (!TryInterpret(resolver, callArgs[1], paramExprs, paramValues, parentArgs, useFec, out serviceKey) || - !TryInterpret(resolver, callArgs[3], paramExprs, paramValues, parentArgs, useFec, out preResolveParent) || - !TryInterpret(resolver, callArgs[4], paramExprs, paramValues, parentArgs, useFec, out resolveArgs)) - return false; - - result = resolver.ResolveMany((Type) ConstValue(callArgs[0]), serviceKey, (Type) ConstValue(callArgs[2]), - (Request) preResolveParent, (object[]) resolveArgs); - return true; - } - } - - // fallback to reflection invocation - object instance = null; - var callObjectExpr = callExpr.Object; - if (callObjectExpr != null && !TryInterpret(r, callObjectExpr, paramExprs, paramValues, parentArgs, useFec, out instance)) - return false; - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgCount = callExpr.FewArgumentCount; - if (fewArgCount >= 0) - { - if (fewArgCount == 0) - { - result = callExpr.Method.Invoke(instance, ArrayTools.Empty()); - return true; - } - - if (fewArgCount == 1) - { - var fewArgs = new object[1]; - var fewArgsExpr = ((OneArgumentMethodCallExpression)callExpr).Argument; - if (!TryInterpret(r, fewArgsExpr, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - - if (fewArgCount == 2) - { - var fewArgs = new object[2]; - var fewArgsExpr = ((TwoArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - - if (fewArgCount == 3) - { - var fewArgs = new object[3]; - var fewArgsExpr = ((ThreeArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - - if (fewArgCount == 4) - { - var fewArgs = new object[4]; - var fewArgsExpr = ((FourArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - if (fewArgCount == 5) - { - var fewArgs = new object[5]; - var fewArgsExpr = ((FiveArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3]) || - !TryInterpret(r, fewArgsExpr.Argument4, paramExprs, paramValues, parentArgs, useFec, out fewArgs[4])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - } -#endif - var args = callExpr.Arguments.ToListOrSelf(); - var callArgCount = args.Count; - if (callArgCount == 0) - result = method.Invoke(instance, ArrayTools.Empty()); - else - { - var argObjects = new object[callArgCount]; - for (var i = 0; i < argObjects.Length; i++) - if (!TryInterpret(r, args[i], paramExprs, paramValues, parentArgs, useFec, out argObjects[i])) - return false; - result = method.Invoke(instance, argObjects); - } - - return true; - } - - private static object InterpretGetScopedViaFactoryDelegateNoDisposalIndex(IResolverContext resolver, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgExpr = (FourArgumentsMethodCallExpression)callExpr; - var resolverArg = fewArgExpr.Argument0; -#else - var args = callExpr.Arguments.ToListOrSelf(); - var resolverArg = args[0]; -#endif - if (!ReferenceEquals(resolverArg, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, resolverArg, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - var scope = (Scope)resolver.CurrentScope; - if (scope == null) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var throwIfNoScopeArg = fewArgExpr.Argument1; -#else - var throwIfNoScopeArg = args[1]; -#endif - return (bool)((ConstantExpression)throwIfNoScopeArg).Value ? Throw.For(Error.NoCurrentScope, resolver) : null; - } - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var factoryIdArg = fewArgExpr.Argument2; -#else - var factoryIdArg = args[2]; -#endif - var id = (int)((ConstantExpression)factoryIdArg).Value; - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var lambdaArg = fewArgExpr.Argument3; -#else - var lambdaArg = args[3]; -#endif - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambdaArg is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(resolver); - else - { - var body = ((LambdaExpression)lambdaArg).Body; - if (!TryInterpret(resolver, body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = body.CompileToFactoryDelegate(useFec, ((IContainer)resolver).Rules.UseInterpretation)(resolver); - } - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - scope.AddUnorderedDisposable(disp); - } - - return itemRef.Value; - } - - private static object InterpretGetScopedViaFactoryDelegate(IResolverContext resolver, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgExpr = (FiveArgumentsMethodCallExpression)callExpr; - var resolverArg = fewArgExpr.Argument0; -#else - var args = callExpr.Arguments.ToListOrSelf(); - var resolverArg = args[0]; -#endif - if (!ReferenceEquals(resolverArg, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, resolverArg, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - var scope = (Scope)resolver.CurrentScope; - if (scope == null) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var throwIfNoScopeArg = fewArgExpr.Argument1; -#else - var throwIfNoScopeArg = args[1]; -#endif - return (bool)((ConstantExpression)throwIfNoScopeArg).Value ? Throw.For(Error.NoCurrentScope, resolver) : null; - } - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var factoryIdArg = fewArgExpr.Argument2; -#else - var factoryIdArg = args[2]; -#endif - var id = (int)((ConstantExpression)factoryIdArg).Value; - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var lambdaArg = fewArgExpr.Argument3; -#else - var lambdaArg = args[3]; -#endif - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambdaArg is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(resolver); - else if (!TryInterpret(resolver, ((LambdaExpression)lambdaArg).Body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = ((LambdaExpression)lambdaArg).Body.CompileToFactoryDelegate(useFec, - ((IContainer)resolver).Rules.UseInterpretation)(resolver); - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var disposalOrderArg = fewArgExpr.Argument4; -#else - var disposalOrderArg = args[4]; -#endif - var disposalOrder = (int)((ConstantExpression)disposalOrderArg).Value; - if (disposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, disposalOrder); - } - } - - return itemRef.Value; - } - - private static object InterpretGetNameScopedViaFactoryDelegate(IResolverContext r, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { - var args = callExpr.Arguments.ToListOrSelf(); - - if (!ReferenceEquals(args[0], FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(r, args[0], paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - r = (IResolverContext)resolverObj; - } - - var scope = (Scope)r.GetNamedScope(((ConstantExpression)args[1]).Value, (bool)((ConstantExpression)args[2]).Value); - if (scope == null) - return null; // result is null in this case - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // check if scoped dependency is already in scope, then just return it - var id = (int)((ConstantExpression)args[3]).Value; - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { - var lambda = args[4]; - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambda is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(r); - else if (!TryInterpret(r, ((LambdaExpression)lambda).Body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = ((LambdaExpression)lambda).Body.CompileToFactoryDelegate(useFec, - ((IContainer)r).Rules.UseInterpretation)(r); - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - { - var disposalOrder = (int)((ConstantExpression)args[5]).Value; - if (disposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, disposalOrder); - } - } - - return itemRef.Value; - } - - private static object InterpretGetScopedOrSingletonViaFactoryDelegate(IResolverContext r, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgExpr = (FourArgumentsMethodCallExpression)callExpr; - var resolverArg = fewArgExpr.Argument0; -#else - var args = callExpr.Arguments.ToListOrSelf(); - var resolverArg = args[0]; -#endif - if (!ReferenceEquals(resolverArg, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(r, resolverArg, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - r = (IResolverContext)resolverObj; - } - - var scope = (Scope)(r.CurrentScope ?? r.SingletonScope); - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var factoryIdArg = fewArgExpr.Argument1; -#else - var factoryIdArg = args[1]; -#endif - var id = (int)((ConstantExpression)factoryIdArg).Value; - - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var lambda = fewArgExpr.Argument2; -#else - var lambda = args[2]; -#endif - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambda is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(r); - else if (!TryInterpret(r, ((LambdaExpression)lambda).Body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = ((LambdaExpression)lambda).Body.CompileToFactoryDelegate(useFec, - ((IContainer)r).Rules.UseInterpretation)(r); - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var disposalOrderArg = fewArgExpr.Argument3; -#else - var disposalOrderArg = args[3]; -#endif - var disposalOrder = (int)((ConstantExpression)disposalOrderArg).Value; - if (disposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, disposalOrder); - } - } - - return itemRef.Value; - } - - [MethodImpl((MethodImplOptions)256)] - private static object ConstValue(Expression expr) => ((ConstantExpression)expr).Value; - } - - internal static class Converter - { - public static object ConvertWithOperator(object source, Type targetType, Expression expr) - { - var sourceType = source.GetType(); - var sourceConvertOp = sourceType.FindConvertOperator(sourceType, targetType); - if (sourceConvertOp != null) - return sourceConvertOp.Invoke(null, new[] { source }); - - var targetConvertOp = targetType.FindConvertOperator(sourceType, targetType); - if (targetConvertOp == null) - Throw.It(Error.NoConversionOperatorFoundWhenInterpretingTheConvertExpression, source, targetType, expr); - return targetConvertOp.Invoke(null, new[] { source }); - } - - public static object ConvertMany(object[] source, Type targetType) => - _convertManyMethod.MakeGenericMethod(targetType).Invoke(null, source.One()); - - public static R[] DoConvertMany(object[] items) - { - if (items == null && items.Length == 0) - return ArrayTools.Empty(); - - var results = new R[items.Length]; - for (var i = 0; i < items.Length; i++) - results[i] = (R)items[i]; - return results; - } - - private static readonly MethodInfo _convertManyMethod = - typeof(Converter).GetTypeInfo().GetDeclaredMethod(nameof(DoConvertMany)); - } - - /// Compiles expression to factory delegate. - public static class FactoryDelegateCompiler - { - /// Resolver context parameter expression in FactoryDelegate. - public static readonly ParameterExpression ResolverContextParamExpr = Parameter(typeof(IResolverContext), "r"); - - /// [Obsolete("Not used anymore")] - public static readonly Type[] FactoryDelegateParamTypes = { typeof(IResolverContext) }; - - /// Optimization: singleton array with the parameter expression of IResolverContext - public static readonly ParameterExpression[] FactoryDelegateParamExprs = { ResolverContextParamExpr }; - - /// Strips the unnecessary or adds the necessary cast to expression return result - public static Expression NormalizeExpression(this Expression expr) - { - if (expr.NodeType == System.Linq.Expressions.ExpressionType.Convert) - { - var operandExpr = ((UnaryExpression)expr).Operand; - if (operandExpr.Type == typeof(object)) - return operandExpr; - } - - if (expr.Type != typeof(void) && expr.Type.IsValueType()) - return Convert(expr, typeof(object)); - - return expr; - } - - /// Wraps service creation expression (body) into and returns result lambda expression. - public static Expression WrapInFactoryExpression(this Expression expression) => - Lambda(expression.NormalizeExpression(), FactoryDelegateParamExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , typeof(object) -#endif - ); - - /// First wraps the input service expression into lambda expression and - /// then compiles lambda expression to actual used for service resolution. - public static FactoryDelegate CompileToFactoryDelegate( - this Expression expression, bool useFastExpressionCompiler, bool preferInterpretation) - { - expression = expression.NormalizeExpression(); - if (expression is ConstantExpression constExpr) - return constExpr.Value.ToFactoryDelegate; - - if (!preferInterpretation && useFastExpressionCompiler) - { - var factoryDelegate = (FactoryDelegate)(FastExpressionCompiler.LightExpression.ExpressionCompiler.TryCompileBoundToFirstClosureParam( - typeof(FactoryDelegate), expression, FactoryDelegateParamExprs, - new[] { typeof(FastExpressionCompiler.LightExpression.ExpressionCompiler.ArrayClosure), typeof(IResolverContext) }, typeof(object))); - if (factoryDelegate != null) - return factoryDelegate; - } - - // fallback for platforms when FastExpressionCompiler is not supported, - // or just in case when some expression is not supported (did not found one yet) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - return Lambda(expression, FactoryDelegateParamExprs, typeof(object)).ToLambdaExpression() -#else - return Lambda(expression, FactoryDelegateParamExprs) -#endif - .Compile( -#if SUPPORTS_EXPRESSION_COMPILE_WITH_PREFER_INTERPRETATION_PARAM - preferInterpretation -#endif - ); - } - - /// Compiles lambda expression to actual `FactoryDelegate` wrapper. - public static object CompileToFactoryDelegate(this Expression expression, - Type factoryDelegateType, Type resultType, bool useFastExpressionCompiler, bool preferInterpretation) - { - if (!preferInterpretation && useFastExpressionCompiler) - { - var factoryDelegate = (FastExpressionCompiler.LightExpression.ExpressionCompiler.TryCompileBoundToFirstClosureParam( - factoryDelegateType, expression, FactoryDelegateParamExprs, - new[] { typeof(FastExpressionCompiler.LightExpression.ExpressionCompiler.ArrayClosure), typeof(IResolverContext) }, resultType)); - if (factoryDelegate != null) - return factoryDelegate; - } - - // fallback for platforms when FastExpressionCompiler is not supported, - // or just in case when some expression is not supported (did not found one yet) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - return Lambda(factoryDelegateType, expression, FactoryDelegateParamExprs, resultType).ToLambdaExpression() -#else - return Lambda(factoryDelegateType, expression, FactoryDelegateParamExprs) -#endif - .Compile( -#if SUPPORTS_EXPRESSION_COMPILE_WITH_PREFER_INTERPRETATION_PARAM - preferInterpretation -#endif - ); - } - - /// [Obsolete("Use the version with `preferInterpretation` parameter instead")] - public static FactoryDelegate CompileToFactoryDelegate(this Expression expression, - bool useFastExpressionCompiler = false) - { - expression = expression.NormalizeExpression(); - - // Optimization for constants - if (expression is ConstantExpression ce) - return ce.Value.ToFactoryDelegate; - - if (useFastExpressionCompiler) - { - var factoryDelegate = (FactoryDelegate)(FastExpressionCompiler.LightExpression.ExpressionCompiler.TryCompileBoundToFirstClosureParam( - typeof(FactoryDelegate), expression, FactoryDelegateParamExprs, - new[] { typeof(FastExpressionCompiler.LightExpression.ExpressionCompiler.ArrayClosure), typeof(IResolverContext) }, typeof(object))); - - if (factoryDelegate != null) - return factoryDelegate; - } - - // fallback for platforms when FastExpressionCompiler is not supported, - // or just in case when some expression is not supported (did not found one yet) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - return Lambda(expression, FactoryDelegateParamExprs, typeof(object)).ToLambdaExpression().Compile(); -#else - return Lambda(expression, FactoryDelegateParamExprs).Compile(); -#endif - } - - // todo: remove unused - /// Restores the expression from LightExpression, or returns itself if already an Expression. - public static System.Linq.Expressions.Expression ToExpression(this Expression expr) => -#if SUPPORTS_FAST_EXPRESSION_COMPILER - expr.ToExpression(); -#else - expr; -#endif - } - - /// Container extended features. - public static class ContainerTools - { - /// The default key for services registered into container created by - public const string FacadeKey = "@facade"; // todo: use invisible keys #555 - - /// Allows to register new specially keyed services which will facade the same default service, - /// registered earlier. May be used to "override" registrations when testing the container - public static IContainer CreateFacade(this IContainer container, string facadeKey = FacadeKey) => - container.With(rules => rules - .WithDefaultRegistrationServiceKey(facadeKey) - .WithFactorySelector(Rules.SelectKeyedOverDefaultFactory(facadeKey))); - - /// Shares all of container state except the cache and the new rules. - public static IContainer With(this IContainer container, - Func configure = null, IScopeContext scopeContext = null) => - container.With(configure?.Invoke(container.Rules) ?? container.Rules, scopeContext ?? container.ScopeContext, - RegistrySharing.CloneAndDropCache, container.SingletonScope); - - /// Prepares container for expression generation. - public static IContainer WithExpressionGeneration(this IContainer container, bool allowRuntimeState = false) => - container.With(rules => rules.WithExpressionGeneration(allowRuntimeState)); - - /// Returns new container with all expression, delegate, items cache removed/reset. - /// But it will preserve resolved services in Singleton/Current scope. - public static IContainer WithoutCache(this IContainer container) => - container.With(container.Rules, container.ScopeContext, - RegistrySharing.CloneAndDropCache, container.SingletonScope); - - /// Creates new container with state shared with original, except for the singletons and cache. - public static IContainer WithoutSingletonsAndCache(this IContainer container) => - container.With(container.Rules, container.ScopeContext, - RegistrySharing.CloneAndDropCache, singletonScope: null); - - /// Shares the setup with original container but copies the registrations, so the new registrations - /// won't be visible in original. Registrations include decorators and wrappers as well. - public static IContainer WithRegistrationsCopy(this IContainer container, bool preserveCache = false) => - container.With(container.Rules, container.ScopeContext, - preserveCache ? RegistrySharing.CloneButKeepCache : RegistrySharing.CloneAndDropCache, - container.SingletonScope); - - /// For given instance resolves and sets properties and fields. - /// It respects rules set per container, - /// or if rules are not set it uses . - public static TService InjectPropertiesAndFields(this IResolverContext r, TService instance) => - r.InjectPropertiesAndFields(instance, null); - - /// For given instance resolves and sets properties and fields. You may specify what - /// properties and fields. - public static TService InjectPropertiesAndFields(this IResolverContext r, TService instance, - params string[] propertyAndFieldNames) - { - r.InjectPropertiesAndFields(instance, propertyAndFieldNames); - return instance; - } - - /// Creates service using container for injecting parameters without registering anything in - /// if the TYPE is not registered yet. - /// Container to use for type creation and injecting its dependencies. - /// Type to instantiate. Wrappers (Func, Lazy, etc.) is also supported. - /// Setup for the concrete type, e.g. `TrackDisposableTransient` - /// (optional) Injection rules to select constructor/factory method, inject parameters, - /// properties and fields. - /// The default is - /// Object instantiated by constructor or object returned by factory method. - public static object New(this IContainer container, Type concreteType, Setup setup, Made made = null, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) - { - var containerClone = container.With(container.Rules, container.ScopeContext, - registrySharing, container.SingletonScope); - - var implType = containerClone.GetWrappedType(concreteType, null); - - var condition = setup == null && made == null ? null - : made == null ? (Func)(f => f.Setup == setup) - : setup == null ? (Func)(f => f.Made == made) - : (f => f.Made == made && f.Setup == setup); - - if (!containerClone.IsRegistered(implType, condition: condition)) - containerClone.Register(implType, made: made, setup: setup); - - // No need to Dispose facade because it shares singleton/open scopes with source container, and disposing source container does the job. - return containerClone.Resolve(concreteType, IfUnresolved.Throw); - } - - /// Creates service using container for injecting parameters without registering anything in . - /// Container to use for type creation and injecting its dependencies. - /// Type to instantiate. Wrappers (Func, Lazy, etc.) is also supported. - /// (optional) Injection rules to select constructor/factory method, inject parameters, - /// properties and fields. - /// The default is - /// Object instantiated by constructor or object returned by factory method. - public static object New(this IContainer container, Type concreteType, Made made = null, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) => - container.New(concreteType, setup: null, made, registrySharing); - - /// Creates service using container for injecting parameters without registering anything in . - /// Type to instantiate. - /// Container to use for type creation and injecting its dependencies. - /// (optional) Injection rules to select constructor/factory method, inject parameters, properties and fields. - /// The default is - /// Object instantiated by constructor or object returned by factory method. - public static T New(this IContainer container, Made made = null, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) => - (T)container.New(typeof(T), made, registrySharing); - - /// Creates service given strongly-typed creation expression. - /// Can be used to invoke arbitrary method returning some value with injecting its parameters from container. - /// Method or constructor result type. - /// Container to use for injecting dependencies. - /// Creation expression. - /// The default is - /// Created result. - public static T New(this IContainer container, Made.TypedMade made, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) => - (T)container.New(typeof(T), made, registrySharing); - - // todo: vNext: remove, replaced by Registrator.RegisterMapping - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Container New service type. - /// Existing registered service type. - /// (optional) (optional) - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IContainer container, Type serviceType, Type registeredServiceType, - object serviceKey = null, object registeredServiceKey = null) => - Registrator.RegisterMapping(container, - serviceType, registeredServiceType, serviceKey, registeredServiceKey); - - // todo: vNext: remove, replaced by Registrator.RegisterMapping - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Container - /// New service type. - /// Existing registered service type. - /// (optional) (optional) - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IContainer container, - object serviceKey = null, object registeredServiceKey = null) => - Registrator.RegisterMapping(container, - typeof(TService), typeof(TRegisteredService), serviceKey, registeredServiceKey); - - // todo: Remove in VNext? - /// Forwards to . - public static void RegisterPlaceholder(this IContainer container, Type serviceType, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - Registrator.RegisterPlaceholder(container, serviceType, ifAlreadyRegistered, serviceKey); - - // todo: vNext: Remove, replaced by Registrator.RegisterPlaceholder - /// Register a service without implementation which can be provided later in terms - /// of normal registration with IfAlreadyRegistered.Replace parameter. - /// When the implementation is still not provided when the placeholder service is accessed, - /// then the exception will be thrown. - /// This feature allows you to postpone decision on implementation until it is later known. - /// Internally the empty factory is registered with the setup asResolutionCall set to true. - /// That means, instead of placing service instance into graph expression we put here redirecting call to - /// container Resolve. - public static void RegisterPlaceholder(this IContainer container, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - container.RegisterPlaceholder(typeof(TService), ifAlreadyRegistered, serviceKey); - - /// Obsolete: please use WithAutoFallbackDynamicRegistration - [Obsolete("Please use WithAutoFallbackDynamicRegistration instead")] - public static IContainer WithAutoFallbackResolution(this IContainer container, - IEnumerable implTypes, - Func changeDefaultReuse = null, - Func condition = null) => - container.ThrowIfNull().With(rules => - rules.WithUnknownServiceResolvers( - Rules.AutoRegisterUnknownServiceRule(implTypes, changeDefaultReuse, condition))); - - /// Obsolete: please use WithAutoFallbackDynamicRegistration - [Obsolete("Please use WithAutoFallbackDynamicRegistration instead")] - public static IContainer WithAutoFallbackResolution(this IContainer container, - IEnumerable implTypeAssemblies, - Func changeDefaultReuse = null, - Func condition = null) => - container.WithAutoFallbackResolution(implTypeAssemblies.ThrowIfNull() - .SelectMany(assembly => assembly.GetLoadedTypes()) - .Where(Registrator.IsImplementationType).ToArray(), - changeDefaultReuse, condition); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - Func> getImplTypes, Func factory = null) => - container.ThrowIfNull() - .With(rules => rules.WithDynamicRegistrationsAsFallback( - Rules.AutoFallbackDynamicRegistrations(getImplTypes, factory))); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, params Type[] implTypes) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypes); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - IReuse reuse, params Type[] implTypes) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypes, implType => new ReflectionFactory(implType, reuse)); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - IReuse reuse, Setup setup, params Type[] implTypes) => - container.WithAutoFallbackDynamicRegistrations( - (ignoredServiceType, ignoredServiceKey) => implTypes, - implType => new ReflectionFactory(implType, reuse, setup: setup)); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - Func> getImplTypeAssemblies, - Func factory = null) => - container.ThrowIfNull().With(rules => rules.WithDynamicRegistrations( - Rules.AutoFallbackDynamicRegistrations( - (serviceType, serviceKey) => - { - var assemblies = getImplTypeAssemblies(serviceType, serviceKey); - if (assemblies == null) - return Empty(); - return assemblies - .SelectMany(ReflectionTools.GetLoadedTypes) - .Where(Registrator.IsImplementationType) - .ToArray(); - }, - factory))); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - params Assembly[] implTypeAssemblies) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypeAssemblies); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - IEnumerable implTypeAssemblies) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypeAssemblies); - - /// Creates new container with provided parameters and properties - /// to pass the custom dependency values for injection. The old parameters and properties are overridden, - /// but not replaced. - /// Container to work with. - /// (optional) Parameters specification, can be used to proved custom values. - /// (optional) Properties and fields specification, can be used to proved custom values. - /// New container with adjusted rules. - /// (_ => "Nya!")); - /// var a = c.Resolve(); // where A accepts string parameter in constructor - /// Assert.AreEqual("Nya!", a.Message) - /// ]]> - public static IContainer WithDependencies(this IContainer container, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - container.With(rules => rules.With(Made.Of( - parameters: rules.Parameters.OverrideWith(parameters), - propertiesAndFields: rules.PropertiesAndFields.OverrideWith(propertiesAndFields)), - overrideRegistrationMade: true)); - - /// Result of GenerateResolutionExpressions methods - public class GeneratedExpressions - { - /// Resolutions roots - public readonly List>> - Roots = new List>>(); - - /// Dependency of Resolve calls - public readonly List> - ResolveDependencies = new List>(); - - /// Errors - public readonly List> - Errors = new List>(); - } - - /// Generates expressions for specified roots and their "Resolve-call" dependencies. - /// Wraps exceptions into errors. The method does not create any actual services. - /// You may use Factory . - public static GeneratedExpressions GenerateResolutionExpressions(this IContainer container, - Func, IEnumerable> getRoots = null, bool allowRuntimeState = false) - { - var generatingContainer = container.WithExpressionGeneration(allowRuntimeState); - var regs = generatingContainer.GetServiceRegistrations(); - var roots = getRoots != null ? getRoots(regs) : regs.Select(r => r.ToServiceInfo()); - - var result = new GeneratedExpressions(); - foreach (var root in roots) - { - try - { - var request = Request.Create(generatingContainer, root); - var expr = generatingContainer.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - continue; - - result.Roots.Add(root.Pair(expr.WrapInFactoryExpression() -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToLambdaExpression() -#endif - )); - } - catch (ContainerException ex) - { - result.Errors.Add(root.Pair(ex)); - } - } - - var depExprs = generatingContainer.Rules.DependencyResolutionCallExprs.Value; - result.ResolveDependencies.AddRange(depExprs.Enumerate().Select(r => r.Key.Pair(r.Value))); - return result; - } - - /// Generates expressions for provided root services - public static GeneratedExpressions GenerateResolutionExpressions( - this IContainer container, Func condition) => - container.GenerateResolutionExpressions(regs => regs.Where(condition.ThrowIfNull()).Select(r => r.ToServiceInfo())); - - /// Generates expressions for provided root services - public static GeneratedExpressions GenerateResolutionExpressions( - this IContainer container, params ServiceInfo[] roots) => - container.GenerateResolutionExpressions(roots.ToFunc, IEnumerable>); - - /// Excluding open-generic registrations, cause you need to provide type arguments to actually create these types. - public static bool DefaultValidateCondition(ServiceRegistrationInfo reg) => !reg.ServiceType.IsOpenGeneric(); - - // todo: Should we have a version which is throws by default? - // todo: Should we break it by making the condition a mandatory? - because it the pass to avoid problems of validating the unnecessary dependencies - /// Helps to find potential problems in service registration setup. - /// Method tries to resolve the specified registrations, collects exceptions, and - /// returns them to user. Does not create any actual service objects. - /// You must specify to define your resolution roots, - /// otherwise container will try to resolve all registrations, - /// which usually is not realistic case to validate. - public static KeyValuePair[] Validate(this IContainer container, - Func condition = null) - { - var noOpenGenericsWithCondition = condition == null - ? (Func)DefaultValidateCondition - : (r => condition(r) && DefaultValidateCondition(r)); - - var roots = container.GetServiceRegistrations().Where(noOpenGenericsWithCondition).Select(r => r.ToServiceInfo()).ToArray(); - if (roots.Length == 0) - Throw.It(Error.FoundNoRootsToValidate, container); - - return container.Validate(roots); - } - - /// Helps to find potential problems when resolving the . - /// Method will collect the exceptions when resolving or injecting the specific root. - /// Does not create any actual service objects. - /// You must specify to define your resolution roots, - /// otherwise container will try to resolve all registrations, - /// which usually is not realistic case to validate. - public static KeyValuePair[] Validate( - this IContainer container, params ServiceInfo[] roots) - { - var validatingContainer = container.With(rules => rules.ForValidate()); - - List> errors = null; - for (var i = 0; i < roots.Length; i++) - { - var root = roots[i]; - try - { - var request = Request.Create(validatingContainer, root); - var expr = validatingContainer.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - continue; - } - catch (ContainerException ex) - { - if (errors == null) - errors = new List>(); - errors.Add(root.Pair(ex)); - } - } - - return errors?.ToArray() ?? ArrayTools.Empty>(); - } - - /// Re-constructs the whole request chain as request creation expression. - public static Expression GetRequestExpression(this IContainer container, Request request, - RequestFlags requestParentFlags = default(RequestFlags)) - { - if (request.IsEmpty) - return (requestParentFlags & RequestFlags.OpensResolutionScope) != 0 - ? Request.EmptyOpensResolutionScopeRequestExpr - : Request.EmptyRequestExpr; - - var flags = request.Flags | requestParentFlags; - var r = requestParentFlags == default(RequestFlags) ? request : request.WithFlags(flags); - - // When not for generation, using run-time request object to Minimize generated object graph. - if (!container.Rules.UsedForExpressionGeneration) - return Constant(r.IsolateRequestChain()); - - // recursively ask for parent expression until it is empty - var parentExpr = container.GetRequestExpression(request.DirectParent); - - var serviceType = r.ServiceType; - var ifUnresolved = r.IfUnresolved; - var requiredServiceType = r.RequiredServiceType; - var serviceKey = r.ServiceKey; - - var metadataKey = r.MetadataKey; - var metadata = r.Metadata; - - var factoryID = r.FactoryID; - var factoryType = r.FactoryType; - var implementationType = r.ImplementationType; - var decoratedFactoryID = r.DecoratedFactoryID; - - var serviceTypeExpr = Constant(serviceType); - var factoryIdExpr = Constant(factoryID); - var implTypeExpr = Constant(implementationType); - var reuseExpr = r.Reuse == null ? Constant(null) - : r.Reuse.ToExpression(it => container.GetConstantExpression(it)); - - if (ifUnresolved == IfUnresolved.Throw && - requiredServiceType == null && serviceKey == null && metadataKey == null && metadata == null && - factoryType == FactoryType.Service && flags == default(RequestFlags) && decoratedFactoryID == 0) - return Call(parentExpr, Request.PushMethodWith4Args.Value, - serviceTypeExpr, factoryIdExpr, implTypeExpr, reuseExpr); - - var requiredServiceTypeExpr = Constant(requiredServiceType); - var serviceKeyExpr = container.GetConstantExpression(serviceKey, typeof(object)); - var factoryTypeExpr = Constant(factoryType); - var flagsExpr = Constant(flags); - - if (ifUnresolved == IfUnresolved.Throw && - metadataKey == null && metadata == null && decoratedFactoryID == 0) - return Call(parentExpr, Request.PushMethodWith8Args.Value, - serviceTypeExpr, requiredServiceTypeExpr, serviceKeyExpr, - factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr); - - var ifUnresolvedExpr = Constant(ifUnresolved); - var decoratedFactoryIDExpr = Constant(decoratedFactoryID); - - if (metadataKey == null && metadata == null) - return Call(parentExpr, Request.PushMethodWith10Args.Value, - serviceTypeExpr, requiredServiceTypeExpr, serviceKeyExpr, ifUnresolvedExpr, - factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr, decoratedFactoryIDExpr); - - var metadataKeyExpr = Constant(metadataKey); - var metadataExpr = container.GetConstantExpression(metadata, typeof(object)); - - return Call(parentExpr, Request.PushMethodWith12Args.Value, - serviceTypeExpr, requiredServiceTypeExpr, serviceKeyExpr, metadataKeyExpr, metadataExpr, ifUnresolvedExpr, - factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr, decoratedFactoryIDExpr); - } - - /// Clears delegate and expression cache for specified . - /// But does not clear instances of already resolved/created singletons and scoped services! - public static bool ClearCache(this IContainer container, FactoryType? factoryType = null, object serviceKey = null) => - container.ClearCache(typeof(T), factoryType, serviceKey); - - /// Clears delegate and expression cache for specified service. - /// But does not clear instances of already resolved/created singletons and scoped services! - public static bool ClearCache(this IContainer container, Type serviceType, - FactoryType? factoryType = null, object serviceKey = null) => - container.ClearCache(serviceType, factoryType, serviceKey); - } - - /// Interface used to convert reuse instance to expression. - public interface IConvertibleToExpression - { - /// Returns expression representation without closure. - /// Use to converting the sub-items, constants to container. - Expression ToExpression(Func fallbackConverter); - } - - /// Used to represent multiple default service keys. - /// Exposes to determine order of service added. - public sealed class DefaultKey : IConvertibleToExpression - { - /// Default value. - public static readonly DefaultKey Value = new DefaultKey(0); - - /// Allows to determine service registration order. - public readonly int RegistrationOrder; - - /// Returns the default key with specified registration order. - public static DefaultKey Of(int registrationOrder) => - registrationOrder == 0 ? Value : new DefaultKey(registrationOrder); - - private static readonly MethodInfo _ofMethod = - typeof(DefaultKey).GetTypeInfo().GetDeclaredMethod(nameof(Of)); - - /// Converts to expression - public Expression ToExpression(Func fallbackConverter) => - Call(_ofMethod, Constant(RegistrationOrder)); - - /// Returns next default key with increased . - public DefaultKey Next() => Of(RegistrationOrder + 1); - - /// Compares keys based on registration order. The null (represents default) key is considered equal. - public override bool Equals(object key) => - key == null || (key as DefaultKey)?.RegistrationOrder == RegistrationOrder; - - /// Returns registration order as hash. - public override int GetHashCode() => RegistrationOrder; - - /// Prints registration order to string. - public override string ToString() => GetType().Name + "(" + RegistrationOrder + ")"; - - private DefaultKey(int registrationOrder) - { - RegistrationOrder = registrationOrder; - } - } - - /// Represents default key for dynamic registrations - public sealed class DefaultDynamicKey : IConvertibleToExpression - { - /// Default value. - public static readonly DefaultDynamicKey Value = new DefaultDynamicKey(0); - - /// Associated ID. - public readonly int RegistrationOrder; - - /// Returns dynamic key with specified ID. - public static DefaultDynamicKey Of(int registrationOrder) => - registrationOrder == 0 ? Value : new DefaultDynamicKey(registrationOrder); - - private static readonly MethodInfo _ofMethod = - typeof(DefaultDynamicKey).GetTypeInfo().GetDeclaredMethod(nameof(Of)); - - /// Converts to expression - public Expression ToExpression(Func fallbackConverter) => - Call(_ofMethod, Constant(RegistrationOrder)); - - /// Returns next dynamic key with increased . - public DefaultDynamicKey Next() => Of(RegistrationOrder + 1); - - /// Compares key's IDs. The null (default) key is considered equal! - public override bool Equals(object key) => - key == null || (key as DefaultDynamicKey)?.RegistrationOrder == RegistrationOrder; - - /// Returns key index as hash. - public override int GetHashCode() => RegistrationOrder; - - /// Prints registration order to string. - public override string ToString() => GetType().Name + "(" + RegistrationOrder + ")"; - - private DefaultDynamicKey(int registrationOrder) - { - RegistrationOrder = registrationOrder; - } - } - - /// Extends IResolver to provide an access to scope hierarchy. - public interface IResolverContext : IResolver, IDisposable - { - /// True if container is disposed. - bool IsDisposed { get; } - - /// Parent context of the scoped context. - IResolverContext Parent { get; } - - /// The root context of the scoped context. - IResolverContext Root { get; } - - /// Singleton scope, always associated with root scope. - IScope SingletonScope { get; } - - /// Optional ambient scope context. - IScopeContext ScopeContext { get; } - - /// Current opened scope. May return the current scope from if context is not null. - IScope CurrentScope { get; } - - /// Creates the resolver context with specified current Container-OWN scope - IResolverContext WithCurrentScope(IScope scope); - - /// Put instance into the current scope or singletons. - void UseInstance(Type serviceType, object instance, IfAlreadyRegistered IfAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey); - - /// Puts instance created via the passed factory on demand into the current or singleton scope - void Use(Type serviceType, FactoryDelegate factory); - - /// For given instance resolves and sets properties and fields. - void InjectPropertiesAndFields(object instance, string[] propertyAndFieldNames); - } - - /// Provides a usable abstractions for - public static class ResolverContext - { - /// Just a sugar that allow to get root or self container. - public static IResolverContext RootOrSelf(this IResolverContext r) => r.Root ?? r; - - internal static readonly PropertyInfo ParentProperty = - typeof(IResolverContext).Property(nameof(IResolverContext.Parent)); - - internal static readonly MethodInfo OpenScopeMethod = - typeof(ResolverContext).GetTypeInfo().GetDeclaredMethod(nameof(OpenScope)); - - /// Returns root or self resolver based on request. - public static Expression GetRootOrSelfExpr(Request request) => - request.DirectParent.IsSingletonOrDependencyOfSingleton && !request.OpensResolutionScope && !request.IsDirectlyWrappedInFunc() - ? RootOrSelfExpr - : FactoryDelegateCompiler.ResolverContextParamExpr; - - /// Resolver context parameter expression in FactoryDelegate. - public static readonly Expression ParentExpr = - Property(FactoryDelegateCompiler.ResolverContextParamExpr, ParentProperty); - - /// Resolver parameter expression in FactoryDelegate. - public static readonly Expression RootOrSelfExpr = - Call(typeof(ResolverContext).GetTypeInfo().GetDeclaredMethod(nameof(RootOrSelf)), - FactoryDelegateCompiler.ResolverContextParamExpr); - - /// Resolver parameter expression in FactoryDelegate. - public static readonly Expression SingletonScopeExpr = - Property(FactoryDelegateCompiler.ResolverContextParamExpr, - typeof(IResolverContext).Property(nameof(IResolverContext.SingletonScope))); - - /// Access to scopes in FactoryDelegate. - public static readonly Expression CurrentScopeExpr = - Property(FactoryDelegateCompiler.ResolverContextParamExpr, - typeof(IResolverContext).Property(nameof(IResolverContext.CurrentScope))); - - /// Indicates that context is scoped - that's is only possible if container is not the Root one and has a Parent context - public static bool IsScoped(this IResolverContext r) => r.Parent != null; - - /// Provides access to the current scope - may return `null` if ambient scope context has it scope changed in-between - public static IScope GetCurrentScope(this IResolverContext r, bool throwIfNotFound) => - r.CurrentScope ?? (throwIfNotFound ? Throw.For(Error.NoCurrentScope, r) : null); - - /// Gets current scope matching the - public static IScope GetNamedScope(this IResolverContext r, object name, bool throwIfNotFound) - { - var currentScope = r.CurrentScope; - if (currentScope == null) - return throwIfNotFound ? Throw.For(Error.NoCurrentScope, r) : null; - - if (name == null) - return currentScope; - - if (name is IScopeName scopeName) - { - for (var s = currentScope; s != null; s = s.Parent) - if (scopeName.Match(s.Name)) - return s; - } - else - { - for (var s = currentScope; s != null; s = s.Parent) - if (ReferenceEquals(name, s.Name) || name.Equals(s.Name)) - return s; - } - - return !throwIfNotFound ? null : Throw.For(Error.NoMatchedScopeFound, name, currentScope); - } - - /// Opens scope with optional name and optional tracking of new scope in a parent scope. - /// Parent context to use. - /// (optional) - /// (optional) Instructs to additionally store the opened scope in parent, - /// so it will be disposed when parent is disposed. If no parent scope is available the scope will be tracked by Singleton scope. - /// Used to dispose a resolution scope. - /// Scoped resolver context. - /// (); - /// handler.Handle(data); - /// } - /// ]]> - public static IResolverContext OpenScope(this IResolverContext r, object name = null, bool trackInParent = false) - { - if (r.ScopeContext == null) - { - // todo: may use `r.OwnCurrentScope` when its moved to `IResolverContext` from `IContainer` - var parentScope = r.CurrentScope; - var newOwnScope = new Scope(parentScope, name); - if (trackInParent) - (parentScope ?? r.SingletonScope).TrackDisposable(newOwnScope); - return r.WithCurrentScope(newOwnScope); - } - - var newContextScope = name == null - ? r.ScopeContext.SetCurrent(parent => new Scope(parent)) - : r.ScopeContext.SetCurrent(parent => new Scope(parent, name)); - - if (trackInParent) - (newContextScope.Parent ?? r.SingletonScope).TrackDisposable(newContextScope); - return r.WithCurrentScope(null); - } - - internal static bool TryGetUsedInstance(this IResolverContext r, Type serviceType, out object instance) - { - instance = null; - return r.CurrentScope? .TryGetUsedInstance(r, serviceType, out instance) == true - || r.SingletonScope.TryGetUsedInstance(r, serviceType, out instance); - } - - /// A bit if sugar to track disposable in singleton or current scope - public static T TrackDisposable(this IResolverContext r, T instance) where T : IDisposable => - (T)(r.SingletonScope ?? r.CurrentScope).TrackDisposable(instance); - } - - /// The result delegate generated by DryIoc for service creation. - public delegate object FactoryDelegate(IResolverContext r); - - /// The stronly typed delegate for service creation registered as a Wrapper. - public delegate TService FactoryDelegate(IResolverContext r); - - /// Adds to Container support for: - /// - /// Open-generic services - /// Service generics wrappers and arrays using extension point. - /// Supported wrappers include: Func of , Lazy, Many, IEnumerable, arrays, Meta, KeyValuePair, DebugExpression. - /// All wrapper factories are added into collection of . - /// unregistered resolution rule. - /// - public static class WrappersSupport - { - /// Supported Func types. - public static readonly Type[] FuncTypes = - { - typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>), - typeof(Func<,,,,,>), typeof(Func<,,,,,,>), typeof(Func<,,,,,,,>) - }; - - /// Supported Action types. Yeah, action I can resolve or inject void returning method as action. - public static readonly Type[] ActionTypes = - { - typeof(Action), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>), - typeof(Action<,,,,>), typeof(Action<,,,,,>), typeof(Action<,,,,,,>) - }; - - /// Supported open-generic collection types - all the interfaces implemented by array. - public static readonly Type[] SupportedCollectionTypes = - typeof(object[]).GetImplementedInterfaces().Match(t => t.IsGeneric(), t => t.GetGenericTypeDefinition()); - - /// Returns true if type is supported , and false otherwise. - public static bool IsFunc(this Type type) - { - var genericDefinition = type.GetGenericDefinitionOrNull(); - return genericDefinition != null && FuncTypes.IndexOfReference(genericDefinition) != -1; - } - - internal static int CollectionWrapperID { get; private set; } - - /// Registered wrappers by their concrete or generic definition service type. - public static readonly ImMap> Wrappers = BuildSupportedWrappers(); - - private static ImMap> BuildSupportedWrappers() - { - var wrappers = ImMap>.Empty; - - var arrayExpr = new ExpressionFactory(GetArrayExpression, setup: Setup.Wrapper); - CollectionWrapperID = arrayExpr.FactoryID; - - var arrayInterfaces = SupportedCollectionTypes; - for (var i = 0; i < arrayInterfaces.Length; i++) - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(arrayInterfaces[i]), arrayInterfaces[i], arrayExpr); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(LazyEnumerable<>)), typeof(LazyEnumerable<>), - new ExpressionFactory(GetLazyEnumerableExpressionOrDefault, setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Lazy<>)), typeof(Lazy<>), - new ExpressionFactory(r => GetLazyExpressionOrDefault(r), setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(KeyValuePair<,>)), typeof(KeyValuePair<,>), - new ExpressionFactory(GetKeyValuePairExpressionOrDefault, setup: Setup.WrapperWith(1))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Meta<,>)), typeof(Meta<,>), - new ExpressionFactory(GetMetaExpressionOrDefault, setup: Setup.WrapperWith(0))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Tuple<,>)), typeof(Tuple<,>), - new ExpressionFactory(GetMetaExpressionOrDefault, setup: Setup.WrapperWith(0))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(System.Linq.Expressions.LambdaExpression)), typeof(System.Linq.Expressions.LambdaExpression), - new ExpressionFactory(GetLambdaExpressionExpressionOrDefault, setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(FactoryDelegate)), typeof(FactoryDelegate), - new ExpressionFactory(GetFactoryDelegateExpressionOrDefault, setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(FactoryDelegate<>)), typeof(FactoryDelegate<>), - new ExpressionFactory(GetFactoryDelegateExpressionOrDefault, setup: Setup.WrapperWith(0))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Func<>)), typeof(Func<>), - new ExpressionFactory(GetFuncOrActionExpressionOrDefault, setup: Setup.Wrapper)); - - for (var i = 0; i < FuncTypes.Length; i++) - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(FuncTypes[i]), FuncTypes[i], - new ExpressionFactory(GetFuncOrActionExpressionOrDefault, setup: Setup.WrapperWith(i))); - - for (var i = 0; i < ActionTypes.Length; i++) - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(ActionTypes[i]), ActionTypes[i], - new ExpressionFactory(GetFuncOrActionExpressionOrDefault, - setup: Setup.WrapperWith(unwrap: typeof(void).ToFunc))); - - wrappers = wrappers.AddContainerInterfaces(); - return wrappers; - } - - private static ImMap> AddContainerInterfaces(this ImMap> wrappers) - { - var resolverContextExpr = new ExpressionFactory( - ResolverContext.GetRootOrSelfExpr, - Reuse.Transient, Setup.WrapperWith(preventDisposal: true)); - - var containerExpr = new ExpressionFactory( - r => Convert(ResolverContext.GetRootOrSelfExpr(r), r.ServiceType), - Reuse.Transient, Setup.WrapperWith(preventDisposal: true)); - - wrappers = wrappers - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IResolverContext)), typeof(IResolverContext), resolverContextExpr) - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IResolver)), typeof(IResolver), resolverContextExpr) - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IContainer)), typeof(IContainer), containerExpr) - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IRegistrator)), typeof(IRegistrator), containerExpr) -#if SUPPORTS_ISERVICE_PROVIDER - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IServiceProvider)), typeof(IServiceProvider), resolverContextExpr) -#endif - ; - - return wrappers; - } - - internal static readonly MethodInfo ToArrayMethod = - typeof(ArrayTools).GetTypeInfo().GetDeclaredMethod(nameof(ArrayTools.ToArrayOrSelf)); - - private static Expression GetArrayExpression(Request request) - { - var collectionType = request.GetActualServiceType(); - var container = request.Container; - var rules = container.Rules; - - var itemType = collectionType.GetArrayElementTypeOrNull() ?? collectionType.GetGenericParamsAndArgs()[0]; - - if (rules.ResolveIEnumerableAsLazyEnumerable) - { - var lazyEnumerableExpr = GetLazyEnumerableExpressionOrDefault(request); - return collectionType.GetGenericDefinitionOrNull() != typeof(IEnumerable<>) - ? Call(ToArrayMethod.MakeGenericMethod(itemType), lazyEnumerableExpr) - : lazyEnumerableExpr; - } - - var requiredItemType = container.GetWrappedType(itemType, request.RequiredServiceType); - - var items = container.GetAllServiceFactories(requiredItemType) - .Map(x => new ServiceRegistrationInfo(x.Value, requiredItemType, x.Key)) - .ToArrayOrSelf(); - - if (requiredItemType.IsClosedGeneric()) - { - var requiredItemOpenGenericType = requiredItemType.GetGenericDefinitionOrNull(); - var openGenericItems = container.GetAllServiceFactories(requiredItemOpenGenericType) - .Map(f => new ServiceRegistrationInfo(f.Value, requiredItemType, - new OpenGenericTypeKey(requiredItemType.GetGenericDefinitionOrNull(), f.Key))) - .ToArrayOrSelf(); - items = items.Append(openGenericItems); - } - - // Append registered generic types with compatible variance, - // e.g. for IHandler - IHandler is compatible with IHandler if B : A. - if (requiredItemType.IsGeneric() && - rules.VariantGenericTypesInResolvedCollection) - { - var variantGenericItems = container.GetServiceRegistrations() - .Match(x => - x.ServiceType.IsGeneric() && - x.ServiceType.GetGenericTypeDefinition() == requiredItemType.GetGenericTypeDefinition() && - x.ServiceType != requiredItemType && x.ServiceType.IsAssignableTo(requiredItemType)) - .ToArrayOrSelf(); - items = items.Append(variantGenericItems); - } - - // Composite pattern support: filter out composite parent service skip wrappers and decorators - var parent = request.Parent; - if (parent.FactoryType != FactoryType.Service) - parent = parent.FirstOrDefault(p => p.FactoryType == FactoryType.Service) ?? Request.Empty; - - // check fast for the parent of the same type - if (!parent.IsEmpty && parent.GetActualServiceType() == requiredItemType) - { - items = items.Match(parent.FactoryID, (pID, x) => x.Factory.FactoryID != pID); - if (requiredItemType.IsGeneric()) - items = items.Match(parent.FactoryID, - (pID, x) => x.Factory.FactoryGenerator?.GeneratedFactories.Enumerate().FindFirst(f => f.Value.FactoryID == pID) == null); - } - - // Return collection of single matched item if key is specified. - var serviceKey = request.ServiceKey; - if (serviceKey != null) - items = items.Match(serviceKey, (key, x) => key.Equals(x.OptionalServiceKey)); - - var metadataKey = request.MetadataKey; - var metadata = request.Metadata; - if (metadataKey != null || metadata != null) - items = items.Match(metadataKey.Pair(metadata), (m, x) => x.Factory.Setup.MatchesMetadata(m.Key, m.Value)); - - var itemExprs = Empty(); - if (!items.IsNullOrEmpty()) - { - Array.Sort(items); // to resolve the items in order of registration - - for (var i = 0; i < items.Length; i++) - { - var item = items[i]; - var itemRequest = request.Push(itemType, item.OptionalServiceKey, - IfUnresolved.ReturnDefaultIfNotRegistered, requiredServiceType: item.ServiceType); - - var itemFactory = container.ResolveFactory(itemRequest); - var itemExpr = itemFactory?.GetExpressionOrDefault(itemRequest); - if (itemExpr != null) - itemExprs = itemExprs.AppendOrUpdate(itemExpr); - } - } - - return NewArrayInit(itemType, itemExprs); - } - - private static Expression GetLazyEnumerableExpressionOrDefault(Request request) - { - var container = request.Container; - var collectionType = request.ServiceType; - var itemType = collectionType.GetArrayElementTypeOrNull() ?? collectionType.GetGenericParamsAndArgs()[0]; - var requiredItemType = container.GetWrappedType(itemType, request.RequiredServiceType); - - var resolverExpr = ResolverContext.GetRootOrSelfExpr(request); - var preResolveParentExpr = container.GetRequestExpression(request); - - var resolveManyExpr = Call(resolverExpr, Resolver.ResolveManyMethod, - Constant(itemType), - container.GetConstantExpression(request.ServiceKey), - Constant(requiredItemType), - preResolveParentExpr, - request.GetInputArgsExpr()); - - return New(typeof(LazyEnumerable<>).MakeGenericType(itemType) - .GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 1), - // cast to object is not required cause Resolve already returns IEnumerable - itemType == typeof(object) ? (Expression)resolveManyExpr : Call(_enumerableCastMethod.MakeGenericMethod(itemType), resolveManyExpr)); - } - - private static readonly MethodInfo _enumerableCastMethod = - typeof(Enumerable).GetTypeInfo().GetDeclaredMethod(nameof(Enumerable.Cast)); - - /// Gets the expression for wrapper. - /// The resolution request. - /// if set to true then check for service registration before creating resolution expression. - /// Expression: new Lazy(() => r.Resolve{TService}(key, ifUnresolved, requiredType))]]> - public static Expression GetLazyExpressionOrDefault(Request request, bool nullWrapperForUnresolvedService = false) - { - var lazyType = request.GetActualServiceType(); - var serviceType = lazyType.GetGenericParamsAndArgs()[0]; - var serviceRequest = request.Push(serviceType); - - var container = request.Container; - if (!container.Rules.FuncAndLazyWithoutRegistration) - { - var serviceFactory = container.ResolveFactory(serviceRequest); - if (serviceFactory == null) - return request.IfUnresolved == IfUnresolved.Throw ? null : Constant(null, lazyType); - serviceRequest = serviceRequest.WithResolvedFactory(serviceFactory, skipRecursiveDependencyCheck: true); - } - - // creates: r => new Lazy(() => r.Resolve(key)) - // or for singleton : r => new Lazy(() => r.Root.Resolve(key)) - var serviceExpr = Resolver.CreateResolutionExpression(serviceRequest); - - // The conversion is required in .NET 3.5 to handle lack of covariance for Func - // So that Func may be used for Func - if (serviceExpr.Type != serviceType) - serviceExpr = Convert(serviceExpr, serviceType); - - var lazyValueFactoryType = typeof(Func<>).MakeGenericType(serviceType); - var wrapperCtor = lazyType.Constructor(lazyValueFactoryType); - - return New(wrapperCtor, Lambda(lazyValueFactoryType, serviceExpr, Empty() -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , serviceType -#endif - )); - } - - private static Expression GetFuncOrActionExpressionOrDefault(Request request) - { - var wrapperType = request.GetActualServiceType(); - var isAction = wrapperType == typeof(Action); - if (!isAction) - { - var openGenericWrapperType = wrapperType.GetGenericDefinitionOrNull().ThrowIfNull(); - if (FuncTypes.IndexOfReference(openGenericWrapperType) == -1) - Throw.If(!(isAction = ActionTypes.IndexOfReference(openGenericWrapperType) != -1)); - } - - var argTypes = wrapperType.GetGenericParamsAndArgs(); - var argCount = isAction ? argTypes.Length : argTypes.Length - 1; - var serviceType = isAction ? typeof(void) : argTypes[argCount]; - - var argExprs = Empty(); // may be empty, that's OK - if (argCount != 0) - { - argExprs = new ParameterExpression[argCount]; - for (var i = 0; i < argCount; ++i) - // assign valid unique argument names for code generation - argExprs[i] = Parameter(argTypes[i], argTypes[i].Name + "@" + i); // todo: optimize string allocations - request = request.WithInputArgs(argExprs); - } - - var serviceRequest = request.Push(serviceType, flags: RequestFlags.IsWrappedInFunc | RequestFlags.IsDirectlyWrappedInFunc); - var container = request.Container; - var serviceExpr = container.Rules.FuncAndLazyWithoutRegistration && !isAction - ? Resolver.CreateResolutionExpression(serviceRequest) - : container.ResolveFactory(serviceRequest)?.GetExpressionOrDefault(serviceRequest); - - if (serviceExpr == null) - return null; - - // The conversion to handle lack of covariance for Func in .NET 3.5 - // So that Func may be used for Func - if (!isAction && serviceExpr.Type != serviceType) - serviceExpr = Convert(serviceExpr, serviceType); - - return Lambda(wrapperType, serviceExpr, argExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , isAction ? typeof(void) : serviceType -#endif - ); - } - - private static Expression GetLambdaExpressionExpressionOrDefault(Request request) - { - var serviceType = request.RequiredServiceType - .ThrowIfNull(Error.ResolutionNeedsRequiredServiceType, request); - request = request.Push(serviceType); - var expr = request.Container.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - return null; - return Constant(expr.WrapInFactoryExpression() -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToLambdaExpression() -#endif - , typeof(LambdaExpression)); - } - - private static Expression GetFactoryDelegateExpressionOrDefault(Request request) - { - Type serviceType; - var wrapperType = request.ServiceType; - if (wrapperType == typeof(FactoryDelegate)) - serviceType = request.RequiredServiceType.ThrowIfNull(Error.ResolutionNeedsRequiredServiceType, request); - else - serviceType = request.RequiredServiceType ?? wrapperType.GetGenericParamsAndArgs()[0]; - - request = request.Push(serviceType); - var container = request.Container; - var expr = container.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - return null; - - var rules = container.Rules; - if (wrapperType == typeof(FactoryDelegate)) - return Constant(expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation)); - - return Constant( - expr.CompileToFactoryDelegate(wrapperType, serviceType, rules.UseFastExpressionCompiler, rules.UseInterpretation), - wrapperType); - } - - private static Expression GetKeyValuePairExpressionOrDefault(Request request) - { - var keyValueType = request.GetActualServiceType(); - var typeArgs = keyValueType.GetGenericParamsAndArgs(); - var serviceKeyType = typeArgs[0]; - var serviceKey = request.ServiceKey; - if (serviceKey == null && serviceKeyType.IsValueType() || - serviceKey != null && !serviceKeyType.IsTypeOf(serviceKey)) - return null; - - var serviceType = typeArgs[1]; - var serviceRequest = request.Push(serviceType, serviceKey); - var serviceFactory = request.Container.ResolveFactory(serviceRequest); - var serviceExpr = serviceFactory?.GetExpressionOrDefault(serviceRequest); - if (serviceExpr == null) - return null; - - var keyExpr = request.Container.GetConstantExpression(serviceKey, serviceKeyType); - return New( - keyValueType.GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 2), - keyExpr, serviceExpr); - } - - /// Discovers and combines service with its setup metadata. - /// Works with any generic type with first Type arg - Service type and second Type arg - Metadata type, - /// and constructor with Service and Metadata arguments respectively. - /// - if service key is not specified in request then method will search for all - /// registered factories with the same metadata type ignoring keys. - /// - if metadata is IDictionary{string, object}, - /// then the First value matching the TMetadata type will be returned. - public static Expression GetMetaExpressionOrDefault(Request request) - { - var metaType = request.GetActualServiceType(); - var typeArgs = metaType.GetGenericParamsAndArgs(); - - var metaCtor = metaType.GetConstructorOrNull(typeArgs) - .ThrowIfNull(Error.NotFoundMetaCtorWithTwoArgs, typeArgs, request); - - var metadataType = typeArgs[1]; - var serviceType = typeArgs[0]; - - var container = request.Container; - var requiredServiceType = container.GetWrappedType(serviceType, request.RequiredServiceType); - - var factories = container - .GetAllServiceFactories(requiredServiceType, bothClosedAndOpenGenerics: true) - .ToArrayOrSelf(); - - if (factories.Length == 0) - return null; - - var serviceKey = request.ServiceKey; - if (serviceKey != null) - { - factories = factories.Match(serviceKey, (key, f) => key.Equals(f.Key)); - if (factories.Length == 0) - return null; - } - - // if the service keys for some reason are not unique - factories = factories - .Match(metadataType, (mType, f) => - { - var metadata = f.Value.Setup.Metadata; - if (metadata == null) - return false; - - if (mType == typeof(object)) - return true; - - var metadataDict = metadata as IDictionary; - if (metadataDict != null) - return mType == typeof(IDictionary) || metadataDict.Values.Any(m => mType.IsTypeOf(m)); - - return mType.IsTypeOf(metadata); - }); - - if (factories.Length == 0) - return null; - - // Prevent non-determinism when more than 1 factory is matching the metadata - if (factories.Length > 1) - { - if (request.IfUnresolved == IfUnresolved.Throw) - Throw.It(Error.UnableToSelectFromManyRegistrationsWithMatchingMetadata, metadataType, factories, request); - return null; - } - - var factory = factories[0]; - if (factory == null) - return null; - - serviceKey = factory.Key; - - var serviceRequest = request.Push(serviceType, serviceKey); - var serviceFactory = container.ResolveFactory(serviceRequest); - var serviceExpr = serviceFactory?.GetExpressionOrDefault(serviceRequest); - if (serviceExpr == null) - return null; - - var resultMetadata = factory.Value.Setup.Metadata; - if (metadataType != typeof(object)) - { - var resultMetadataDict = resultMetadata as IDictionary; - if (resultMetadataDict != null && metadataType != typeof(IDictionary)) - resultMetadata = resultMetadataDict.Values.FirstOrDefault(m => metadataType.IsTypeOf(m)); - } - - var metadataExpr = container.GetConstantExpression(resultMetadata, metadataType); - return New(metaCtor, serviceExpr, metadataExpr); - } - } - - /// Represents info required for dynamic registration: service key, factory, - /// and option how to combine dynamic with normal registrations. - public sealed class DynamicRegistration - { - /// Factory - public readonly Factory Factory; - - /// Optional: will be by default. - public readonly IfAlreadyRegistered IfAlreadyRegistered; - - /// Optional service key: if null the default will be used. - public readonly object ServiceKey; - - /// Constructs the info - public DynamicRegistration(Factory factory, - IfAlreadyRegistered ifAlreadyRegistered = IfAlreadyRegistered.AppendNotKeyed, object serviceKey = null) - { - Factory = factory.ThrowIfNull().DoNotCache(); - ServiceKey = serviceKey; - IfAlreadyRegistered = ifAlreadyRegistered; - } - } - - /// Defines resolution/registration rules associated with Container instance. They may be different for different containers. - public sealed class Rules - { - /// Default rules as staring point. - public static readonly Rules Default = new Rules(); - - /// Default rules as staring point. - public static readonly Rules MicrosoftDependencyInjectionRules = new Rules( - (DEFAULT_SETTINGS | Settings.TrackingDisposableTransients) & ~Settings.ThrowOnRegisteringDisposableTransient, - Rules.SelectLastRegisteredFactory(), Reuse.Transient, - Made.Of(DryIoc.FactoryMethod.ConstructorWithResolvableArguments), - IfAlreadyRegistered.AppendNotKeyed, - DefaultDependencyDepthToSplitObjectGraph, null, null, null, null, null); - - /// Default value for - public const int DefaultDependencyDepthToSplitObjectGraph = 20; - - /// Nested dependency depth to split an object graph - public int DependencyDepthToSplitObjectGraph { get; private set; } - - /// Sets . - /// Set to prevent split. - /// To disable the limit please use - public Rules WithDependencyDepthToSplitObjectGraph(int depth) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, depth < 1 ? 1 : depth, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Disables the limitation. - public Rules WithoutDependencyDepthToSplitObjectGraph() => WithDependencyDepthToSplitObjectGraph(int.MaxValue); - - /// Shorthand to - public FactoryMethodSelector FactoryMethod => _made.FactoryMethod; - - /// Shorthand to - public ParameterSelector Parameters => _made.Parameters; - - /// Shorthand to - public PropertiesAndFieldsSelector PropertiesAndFields => _made.PropertiesAndFields; - - /// Instructs to override per-registration made settings with these rules settings. - public bool OverrideRegistrationMade => - (_settings & Settings.OverrideRegistrationMade) != 0; - - /// Returns new instance of the rules new Made composed out of - /// provided factory method, parameters, propertiesAndFields. - public Rules With( - FactoryMethodSelector factoryMethod = null, - ParameterSelector parameters = null, - PropertiesAndFieldsSelector propertiesAndFields = null) => - With(Made.Of(factoryMethod, parameters, propertiesAndFields)); - - /// Returns new instance of the rules with specified . - /// New Made.Of rules. - /// Instructs to override registration level Made.Of - /// New rules. - public Rules With(Made made, bool overrideRegistrationMade = false) => - new Rules( - _settings | (overrideRegistrationMade ? Settings.OverrideRegistrationMade : 0), - FactorySelector, DefaultReuse, - _made == Made.Default - ? made - : Made.Of( - made.FactoryMethod ?? _made.FactoryMethod, - made.Parameters ?? _made.Parameters, - made.PropertiesAndFields ?? _made.PropertiesAndFields), - DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Service key to be used instead on `null` in registration. - public object DefaultRegistrationServiceKey { get; } - - /// Sets the - public Rules WithDefaultRegistrationServiceKey(object serviceKey) => - serviceKey == null ? this : - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, serviceKey); - - /// Defines single factory selector delegate. - /// Provides service request leading to factory selection. - /// Registered factories with corresponding key to select from. - /// Single selected factory, or null if unable to select. - public delegate Factory FactorySelectorRule(Request request, KeyValuePair[] factories); - - /// Rules to select single matched factory default and keyed registered factory/factories. - /// Selectors applied in specified array order, until first returns not null . - /// Default behavior is to throw on multiple registered default factories, cause it is not obvious what to use. - public FactorySelectorRule FactorySelector { get; } - - /// Sets - public Rules WithFactorySelector(FactorySelectorRule rule) => - new Rules(_settings | (rule == SelectLastRegisteredFactory ? Settings.SelectLastRegisteredFactory : default(Settings)), - rule, DefaultReuse, _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Select last registered factory from the multiple default. - public static FactorySelectorRule SelectLastRegisteredFactory() => SelectLastRegisteredFactory; - private static Factory SelectLastRegisteredFactory(Request request, KeyValuePair[] factories) - { - var serviceKey = request.ServiceKey; - for (var i = factories.Length - 1; i >= 0; i--) - { - var factory = factories[i]; - if (factory.Key.Equals(serviceKey)) - return factory.Value; - } - return null; - } - - //we are watching you...public static - /// Prefer specified service key (if found) over default key. - /// Help to override default registrations in Open Scope scenarios: - /// I may register service with key and resolve it as default in current scope. - public static FactorySelectorRule SelectKeyedOverDefaultFactory(object serviceKey) => - (r, fs) => fs.FindFirst(serviceKey, (key, f) => f.Key.Equals(key)).Value ?? - fs.FindFirst(f => f.Key.Equals(null)).Value; - - /// Specify the method signature for returning multiple keyed factories. - /// This is dynamic analog to the normal Container Registry. - /// Requested service type. - /// (optional) If null will request all factories of - /// Key-Factory pairs. - public delegate IEnumerable DynamicRegistrationProvider(Type serviceType, object serviceKey); - - /// Providers for resolving multiple not-registered services. Null by default. - public DynamicRegistrationProvider[] DynamicRegistrationProviders { get; private set; } - - // todo: Should I use Settings.UseDynamicRegistrationsAsFallback, 5 tests are failing only - /// Appends dynamic registration rules. - public Rules WithDynamicRegistrations(params DynamicRegistrationProvider[] rules) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders.Append(rules), UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Appends dynamic registration rules - /// And additionally specifies to use dynamic registrations only when no normal registrations found! - /// Rules to append. New Rules. - public Rules WithDynamicRegistrationsAsFallback(params DynamicRegistrationProvider[] rules) => - new Rules(_settings | Settings.UseDynamicRegistrationsAsFallbackOnly, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders.Append(rules), UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Specifies to use dynamic registrations only when no normal registrations found - public bool UseDynamicRegistrationsAsFallbackOnly => - (_settings & Settings.UseDynamicRegistrationsAsFallbackOnly) != 0; - - /// Defines delegate to return factory for request not resolved by registered factories or prior rules. - /// Applied in specified array order until return not null . - public delegate Factory UnknownServiceResolver(Request request); - - /// Gets rules for resolving not-registered services. Null by default. - public UnknownServiceResolver[] UnknownServiceResolvers { get; private set; } - - /// Appends resolver to current unknown service resolvers. - public Rules WithUnknownServiceResolvers(params UnknownServiceResolver[] rules) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers.Append(rules), - DefaultRegistrationServiceKey); - - /// Removes specified resolver from unknown service resolvers, and returns new Rules. - /// If no resolver was found then will stay the same instance, - /// so it could be check for remove success or fail. - public Rules WithoutUnknownServiceResolver(UnknownServiceResolver rule) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers.Remove(rule), - DefaultRegistrationServiceKey); - - /// Sugar on top of to simplify setting the diagnostic action. - /// Does not guard you from action throwing an exception. Actually can be used to throw your custom exception - /// instead of . - public Rules WithUnknownServiceHandler(Action handler) => - WithUnknownServiceResolvers(request => - { - handler(request); - return null; - }); - - /// The alternative is ConcreteTypeDynamicRegistrations - public static UnknownServiceResolver AutoResolveConcreteTypeRule(Func condition = null) => - request => - { - var concreteType = request.GetActualServiceType(); - if (!concreteType.IsImplementationType() || concreteType.IsPrimitive() || - condition != null && !condition(request)) - return null; - - var openGenericServiceType = concreteType.GetGenericDefinitionOrNull(); - if (openGenericServiceType != null && WrappersSupport.Wrappers.GetValueOrDefault(openGenericServiceType) != null) - return null; - - var factory = new ReflectionFactory(concreteType, - made: DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic); - - // to enable fallback to other rules if unresolved try to resolve expression first and return null - return factory.GetExpressionOrDefault(request.WithIfUnresolved(IfUnresolved.ReturnDefault)) != null ? factory : null; - }; - - /// Rule to automatically resolves non-registered service type which is: nor interface, nor abstract. - /// For constructor selection we are using . - /// The resolution creates transient services. - /// (optional) Condition for requested service type and key. - /// (optional) Reuse for concrete types. - /// New rule. - public static DynamicRegistrationProvider ConcreteTypeDynamicRegistrations( - Func condition = null, IReuse reuse = null) => - AutoFallbackDynamicRegistrations((serviceType, serviceKey) => - { - if (serviceType.IsAbstract() || - serviceType.IsOpenGeneric() || // service type in principle should be concrete, so should not be open-generic - condition != null && !condition(serviceType, serviceKey)) - return null; - - // exclude concrete service types which are pre-defined DryIoc wrapper types - var openGenericServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenericServiceType != null && WrappersSupport.Wrappers.GetValueOrDefault(openGenericServiceType) != null) - return null; - - return serviceType.One(); // use concrete service type as implementation type - }, - implType => - { - ReflectionFactory factory = null; - - // the condition checks that factory is resolvable - factory = new ReflectionFactory(implType, reuse, - DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic, - Setup.With(condition: req => factory?.GetExpressionOrDefault(req.WithIfUnresolved(IfUnresolved.ReturnDefault)) != null)); - - return factory; - }); - - /// Automatically resolves non-registered service type which is: nor interface, nor abstract. - /// The resolution creates Transient services. - public Rules WithConcreteTypeDynamicRegistrations( - Func condition = null, IReuse reuse = null) => - WithDynamicRegistrationsAsFallback(ConcreteTypeDynamicRegistrations(condition, reuse)); - - /// Replaced with `WithConcreteTypeDynamicRegistrations` - public Rules WithAutoConcreteTypeResolution(Func condition = null) => - new Rules(_settings | Settings.AutoConcreteTypeResolution, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers.Append(AutoResolveConcreteTypeRule(condition)), - DefaultRegistrationServiceKey); - - /// Creates dynamic fallback registrations for the requested service type - /// with provided . - /// Fallback means that the dynamic registrations will be applied Only if no normal registrations - /// exist for the requested service type, hence the "fallback". - /// Implementation types to select for service. - /// (optional) Handler to customize the factory, e.g. - /// specify reuse or setup. Handler should not return null. - /// Registration provider. - public static DynamicRegistrationProvider AutoFallbackDynamicRegistrations( - Func> getImplementationTypes, - Func factory = null) - { - // cache factory for implementation type to enable reuse semantics - var factories = Ref.Of(ImHashMap.Empty); - - return (serviceType, serviceKey) => - { - if (!serviceType.IsServiceType()) - return Enumerable.Empty(); - - var implementationTypes = getImplementationTypes(serviceType, serviceKey); - - return implementationTypes.Match( - implType => implType.IsImplementingServiceType(serviceType), - implType => - { - var implTypeHash = RuntimeHelpers.GetHashCode(implType); - var implFactory = factories.Value.GetValueOrDefault(implTypeHash, implType); - if (implFactory == null) - { - if (factory == null) - factories.Swap(fs => (implFactory = fs.GetValueOrDefault(implTypeHash, implType)) != null ? fs - : fs.AddOrUpdate(implTypeHash, implType, implFactory = new ReflectionFactory(implType))); - else - factories.Swap(fs => (implFactory = fs.GetValueOrDefault(implTypeHash, implType)) != null ? fs - : fs.AddOrUpdate(implTypeHash, implType, implFactory = factory.Invoke(implType).ThrowIfNull())); - } - - // We nullify default keys (usually passed by ResolveMany to resolve the specific factory in order) - // so that `CombineRegisteredWithDynamicFactories` may assign the key again. - // Given that the implementation types are unchanged then the new keys assignement will be the same the last one, - // so that the factory resolution will correctly match the required factory by key. - // e.g. bitbucket issue #396 - var theKey = serviceKey is DefaultDynamicKey ? null : serviceKey; - return new DynamicRegistration(implFactory, IfAlreadyRegistered.Keep, theKey); - }); - }; - } - - /// Obsolete: replaced by - [Obsolete("Replaced by " + nameof(AutoFallbackDynamicRegistrations), false)] - public static UnknownServiceResolver AutoRegisterUnknownServiceRule(IEnumerable implTypes, - Func changeDefaultReuse = null, Func condition = null) => - request => - { - if (condition != null && !condition(request)) - return null; - - var scope = request.Container.CurrentScope; - var reuse = scope != null ? Reuse.ScopedTo(scope.Name) : Reuse.Singleton; - - if (changeDefaultReuse != null) - reuse = changeDefaultReuse(reuse, request); - - var requestedServiceType = request.GetActualServiceType(); - request.Container.RegisterMany(implTypes, reuse, - serviceTypeCondition: serviceType => - serviceType.IsOpenGeneric() && requestedServiceType.IsClosedGeneric() - ? serviceType == requestedServiceType.GetGenericTypeDefinition() - : serviceType == requestedServiceType); - - return request.Container.GetServiceFactoryOrDefault(request); - }; - - /// See - public IReuse DefaultReuse { get; } - - /// The reuse used in case if reuse is unspecified (null) in Register methods. - public Rules WithDefaultReuse(IReuse reuse) => - new Rules(_settings, FactorySelector, reuse ?? Reuse.Transient, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Replaced by WithDefaultReuse because for some cases InsteadOfTransient does not make sense. - [Obsolete("Replaced by WithDefaultReuse because for some cases ..InsteadOfTransient does not make sense.", error: false)] - public Rules WithDefaultReuseInsteadOfTransient(IReuse reuse) => WithDefaultReuse(reuse); - - /// Given item object and its type should return item "pure" expression presentation, - /// without side-effects or external dependencies. - /// e.g. for string "blah" Expression.Constant("blah", typeof(string)). - /// If unable to convert should return null. - public delegate Expression ItemToExpressionConverterRule(object item, Type itemType); - - /// . - public ItemToExpressionConverterRule ItemToExpressionConverter { get; private set; } - - /// Specifies custom rule to convert non-primitive items to their expression representation. - /// That may be required because DryIoc by default does not support non-primitive service keys and registration metadata. - /// To enable non-primitive values support DryIoc need a way to recreate them as expression tree. - public Rules WithItemToExpressionConverter(ItemToExpressionConverterRule itemToExpressionOrDefault) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, itemToExpressionOrDefault, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// . - public bool ThrowIfDependencyHasShorterReuseLifespan => - (_settings & Settings.ThrowIfDependencyHasShorterReuseLifespan) != 0; - - /// Turns off throwing exception when dependency has shorter reuse lifespan than its parent or ancestor. - /// New rules with new setting value. - public Rules WithoutThrowIfDependencyHasShorterReuseLifespan() => - WithSettings(_settings & ~Settings.ThrowIfDependencyHasShorterReuseLifespan); - - /// - public bool ThrowOnRegisteringDisposableTransient => - (_settings & Settings.ThrowOnRegisteringDisposableTransient) != 0; - - /// Turns Off the rule . - /// Allows to register disposable transient but it is up to you to handle their disposal. - /// You can use to actually track disposable transient in - /// container, so that disposal will be handled by container. - public Rules WithoutThrowOnRegisteringDisposableTransient() => - WithSettings(_settings & ~Settings.ThrowOnRegisteringDisposableTransient); - - /// - public bool TrackingDisposableTransients => - (_settings & Settings.TrackingDisposableTransients) != 0; - - /// Turns tracking of disposable transients in dependency parent scope, or in current scope if service - /// is resolved directly. - /// - /// If there is no open scope at the moment then resolved transient won't be tracked and it is up to you - /// to dispose it! That's is similar situation to creating service by new - you have full control. - /// - /// If dependency wrapped in Func somewhere in parent chain then it also won't be tracked, because - /// Func supposedly means multiple object creation and for container it is not clear what to do, so container - /// delegates that to user. Func here is the similar to Owned relationship type in Autofac library. - /// - /// Turning this setting On automatically turns off . - public Rules WithTrackingDisposableTransients() => - WithSettings((_settings | Settings.TrackingDisposableTransients) - & ~Settings.ThrowOnRegisteringDisposableTransient); - - /// . - public bool EagerCachingSingletonForFasterAccess => - (_settings & Settings.EagerCachingSingletonForFasterAccess) != 0; - - /// Turns off optimization: creating singletons during resolution of object graph. - public Rules WithoutEagerCachingSingletonForFasterAccess() => - WithSettings(_settings & ~Settings.EagerCachingSingletonForFasterAccess); - - /// . - public Ref> DependencyResolutionCallExprs { get; private set; } - - /// Indicates that container is used for generation purposes, so it should use less runtime state - public bool UsedForExpressionGeneration => (_settings & Settings.UsedForExpressionGeneration) != 0; - - private Settings GetSettingsForExpressionGeneration(bool allowRuntimeState = false) => - _settings & ~Settings.EagerCachingSingletonForFasterAccess - & ~Settings.ImplicitCheckForReuseMatchingScope - & ~Settings.UseInterpretationForTheFirstResolution - & ~Settings.UseInterpretation - | Settings.UsedForExpressionGeneration - | (allowRuntimeState ? 0 : Settings.ThrowIfRuntimeStateRequired); - - /// Specifies to generate ResolutionCall dependency creation expression and stores the result - /// in the-per rules collection. - public Rules WithExpressionGeneration(bool allowRuntimeState = false) => - new Rules(GetSettingsForExpressionGeneration(allowRuntimeState), FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - Ref.Of(ImHashMap.Empty), ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Indicates that rules are used for the validation, e.g. the rules created in `Validate` method - public bool UsedForValidation => (_settings & Settings.UsedForValidation) != 0; - - private Settings GetSettingsForValidation() => - _settings & ~Settings.EagerCachingSingletonForFasterAccess - & ~Settings.ImplicitCheckForReuseMatchingScope - | Settings.UsedForValidation; - - /// Specifies to generate ResolutionCall dependency creation expression and stores the result - /// in the-per rules collection. - public Rules ForValidate() => - new Rules(GetSettingsForValidation(), - FactorySelector, DefaultReuse, _made, DefaultIfAlreadyRegistered, - int.MaxValue, // lifts the `DefaultDependencyDepthToSplitObjectGraph` to the Max, so we will construct the whole tree - null, - ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// - public bool ImplicitCheckForReuseMatchingScope => - (_settings & Settings.ImplicitCheckForReuseMatchingScope) != 0; - - /// Removes implicit Factory for non-transient service. - /// The Condition filters out factory without matching scope. - public Rules WithoutImplicitCheckForReuseMatchingScope() => - WithSettings(_settings & ~Settings.ImplicitCheckForReuseMatchingScope); - - /// Removes runtime optimizations preventing an expression generation. - public Rules ForExpressionGeneration(bool allowRuntimeState = false) => WithSettings(GetSettingsForExpressionGeneration()); - - /// . - public bool ResolveIEnumerableAsLazyEnumerable => - (_settings & Settings.ResolveIEnumerableAsLazyEnumerable) != 0; - - /// Specifies to resolve IEnumerable as LazyEnumerable. - public Rules WithResolveIEnumerableAsLazyEnumerable() => - WithSettings(_settings | Settings.ResolveIEnumerableAsLazyEnumerable); - - /// . - public bool VariantGenericTypesInResolvedCollection => - (_settings & Settings.VariantGenericTypesInResolvedCollection) != 0; - - /// Flag instructs to include covariant compatible types in resolved collection. - public Rules WithoutVariantGenericTypesInResolvedCollection() => - WithSettings(_settings & ~Settings.VariantGenericTypesInResolvedCollection); - - /// . - public IfAlreadyRegistered DefaultIfAlreadyRegistered { get; } - - /// Specifies default setting for container. By default is . - /// Example of use: specify Keep as a container default, then set AppendNonKeyed for explicit collection registrations. - public Rules WithDefaultIfAlreadyRegistered(IfAlreadyRegistered rule) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, rule, DependencyDepthToSplitObjectGraph, DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// . - public bool ThrowIfRuntimeStateRequired => - (_settings & Settings.ThrowIfRuntimeStateRequired) != 0; - - /// Specifies to throw an exception in attempt to resolve service which require runtime state for resolution. - /// Runtime state may be introduced by RegisterDelegate, RegisterInstance, or registering with non-primitive service key, or metadata. - public Rules WithThrowIfRuntimeStateRequired() => - WithSettings(_settings | Settings.ThrowIfRuntimeStateRequired); - - /// . - public bool CaptureContainerDisposeStackTrace => - (_settings & Settings.CaptureContainerDisposeStackTrace) != 0; - - /// Instructs to capture Dispose stack-trace to include it later into - /// exception for easy diagnostics. - public Rules WithCaptureContainerDisposeStackTrace() => - WithSettings(_settings | Settings.CaptureContainerDisposeStackTrace); - - /// Allows Func with args specify its own reuse (sharing) behavior. - public bool IgnoringReuseForFuncWithArgs => - (_settings & Settings.IgnoringReuseForFuncWithArgs) != 0; - - /// Allows Func with args specify its own reuse (sharing) behavior. - public Rules WithIgnoringReuseForFuncWithArgs() => - WithSettings(_settings | Settings.IgnoringReuseForFuncWithArgs); - - /// Allows Func of service to be resolved even without registered service. - public bool FuncAndLazyWithoutRegistration => - (_settings & Settings.FuncAndLazyWithoutRegistration) != 0; - - /// Allows Func of service to be resolved even without registered service. - public Rules WithFuncAndLazyWithoutRegistration() => - WithSettings(_settings | Settings.FuncAndLazyWithoutRegistration); - - /// Commands to use FastExpressionCompiler - set by default. - public bool UseFastExpressionCompiler => - (_settings & Settings.UseFastExpressionCompilerIfPlatformSupported) != 0; - - /// Fallbacks to system `Expression.Compile()` - public Rules WithoutFastExpressionCompiler() => - WithSettings(_settings & ~Settings.UseFastExpressionCompilerIfPlatformSupported); - - /// Subject-subject - public bool UseInterpretationForTheFirstResolution => - (_settings & Settings.UseInterpretationForTheFirstResolution) != 0; - - /// Fallbacks to system `Expression.Compile()` - public Rules WithoutInterpretationForTheFirstResolution() => - WithSettings(_settings & ~Settings.UseInterpretationForTheFirstResolution & ~Settings.UseInterpretation); - - /// Subject - public bool UseInterpretation => - (_settings & Settings.UseInterpretation) != 0; - - /// Uses DryIoc own interpretation mechanism or is falling back to `Compile(preferInterpretation: true)` - public Rules WithUseInterpretation() => - WithSettings(_settings | Settings.UseInterpretation | Settings.UseInterpretationForTheFirstResolution); - - /// Uses DryIoc own interpretation mechanism or is falling back to `Compile(preferInterpretation: true)` - public Rules WithoutUseInterpretation() => - WithSettings(_settings & ~Settings.UseInterpretation); - - /// If Decorator reuse is not set instructs to use `Decorator.SetupWith(useDecarateeReuse: true)` - public bool UseDecorateeReuseForDecorators => - (_settings & Settings.UseDecorateeReuseForDecorators) != 0; - - /// If Decorator reuse is not set instructs to use `Decorator.SetupWith(useDecarateeReuse: true)` - public Rules WithUseDecorateeReuseForDecorators() => - WithSettings(_settings | Settings.UseDecorateeReuseForDecorators); - - /// Outputs most notable non-default rules - public override string ToString() - { - if (this == Default) - return "Rules.Default"; - - string s = ""; - if (_settings != DEFAULT_SETTINGS) - { - var addedSettings = _settings & ~DEFAULT_SETTINGS; - if (addedSettings != 0) - s = "Rules with {" + addedSettings + "}"; - var removedSettings = DEFAULT_SETTINGS & ~_settings; - if (removedSettings != 0) - s += (s != "" ? " and without {" : "Rules without {") + removedSettings + "}"; - } - - if (DependencyDepthToSplitObjectGraph != DefaultDependencyDepthToSplitObjectGraph) - s += " with DepthToSplitObjectGraph=" + DependencyDepthToSplitObjectGraph; - - if (DefaultReuse != null && DefaultReuse != Reuse.Transient) - s += (s != "" ? NewLine : "Rules ") + " with DefaultReuse=" + DefaultReuse; - - if (FactorySelector != null) - { - s += (s != "" ? NewLine : "Rules ") + " with FactorySelector="; - if (FactorySelector == SelectLastRegisteredFactory) - s += nameof(Rules.SelectLastRegisteredFactory); - else - s += ""; - } - - if (_made != Made.Default) - s += (s != "" ? NewLine : "Rules ") + " with Made=" + _made; - - return s; - } - -#region Implementation - - private Rules() - { - _made = Made.Default; - _settings = DEFAULT_SETTINGS; - DefaultReuse = Reuse.Transient; - DefaultIfAlreadyRegistered = IfAlreadyRegistered.AppendNotKeyed; - DependencyDepthToSplitObjectGraph = DefaultDependencyDepthToSplitObjectGraph; - } - - private Rules(Settings settings, - FactorySelectorRule factorySelector, - IReuse defaultReuse, - Made made, - IfAlreadyRegistered defaultIfAlreadyRegistered, - int dependencyDepthToSplitObjectGraph, - Ref> dependencyResolutionCallExprs, - ItemToExpressionConverterRule itemToExpressionConverter, - DynamicRegistrationProvider[] dynamicRegistrationProviders, - UnknownServiceResolver[] unknownServiceResolvers, - object defaultRegistrationServiceKey) - { - _settings = settings; - _made = made; - FactorySelector = factorySelector; - DefaultReuse = defaultReuse; - DefaultIfAlreadyRegistered = defaultIfAlreadyRegistered; - DependencyDepthToSplitObjectGraph = dependencyDepthToSplitObjectGraph; - DependencyResolutionCallExprs = dependencyResolutionCallExprs; - ItemToExpressionConverter = itemToExpressionConverter; - DynamicRegistrationProviders = dynamicRegistrationProviders; - UnknownServiceResolvers = unknownServiceResolvers; - DefaultRegistrationServiceKey = defaultRegistrationServiceKey; - } - - private Rules WithSettings(Settings newSettings) => - new Rules(newSettings, - FactorySelector, DefaultReuse, _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - private readonly Made _made; - - [Flags] - private enum Settings - { - ThrowIfDependencyHasShorterReuseLifespan = 1 << 1, - ThrowOnRegisteringDisposableTransient = 1 << 2, - TrackingDisposableTransients = 1 << 3, - ImplicitCheckForReuseMatchingScope = 1 << 4, - VariantGenericTypesInResolvedCollection = 1 << 5, - ResolveIEnumerableAsLazyEnumerable = 1 << 6, - EagerCachingSingletonForFasterAccess = 1 << 7, - ThrowIfRuntimeStateRequired = 1 << 8, - CaptureContainerDisposeStackTrace = 1 << 9, - UseDynamicRegistrationsAsFallbackOnly = 1 << 10, - IgnoringReuseForFuncWithArgs = 1 << 11, - OverrideRegistrationMade = 1 << 12, - FuncAndLazyWithoutRegistration = 1 << 13, - AutoConcreteTypeResolution = 1 << 14, // informational flag - SelectLastRegisteredFactory = 1 << 15,// informational flag - UsedForExpressionGeneration = 1 << 16, - UseFastExpressionCompilerIfPlatformSupported = 1 << 17, - UseInterpretationForTheFirstResolution = 1 << 18, - UseInterpretation = 1 << 19, - UseDecorateeReuseForDecorators = 1 << 20, - UsedForValidation = 1 << 21 // informational flag, will appear in exceptions during validation - } - - private const Settings DEFAULT_SETTINGS - = Settings.ThrowIfDependencyHasShorterReuseLifespan - | Settings.ThrowOnRegisteringDisposableTransient - | Settings.ImplicitCheckForReuseMatchingScope - | Settings.VariantGenericTypesInResolvedCollection - | Settings.EagerCachingSingletonForFasterAccess - | Settings.UseFastExpressionCompilerIfPlatformSupported - | Settings.UseInterpretationForTheFirstResolution; - - private Settings _settings; - -#endregion - } - - /// Wraps constructor or factory method optionally with factory instance to create service. - public sealed class FactoryMethod - { - /// Constructor or method to use for service creation. - public readonly MemberInfo ConstructorOrMethodOrMember; - - /// Identifies factory service if factory method is instance member. - public readonly ServiceInfo FactoryServiceInfo; - - /// Alternatively you may just provide an expression for factory - public readonly Expression FactoryExpression; - - /// Contains resolved parameter expressions found when looking for most resolvable constructor - internal readonly Expression[] ResolvedParameterExpressions; - - /// Wraps method and factory instance. - /// Where is constructor, static or instance method, property or field. - public static FactoryMethod Of(MemberInfo ctorOrMethodOrMember, ServiceInfo factoryInfo = null) - { - if (ctorOrMethodOrMember == null) - Throw.It(Error.PassedCtorOrMemberIsNull); - - if (ctorOrMethodOrMember is ConstructorInfo == false && !ctorOrMethodOrMember.IsStatic()) - { - if (factoryInfo == null) - Throw.It(Error.PassedMemberIsNotStaticButInstanceFactoryIsNull, ctorOrMethodOrMember); - } - else - { - if (factoryInfo != null) - Throw.It(Error.PassedMemberIsStaticButInstanceFactoryIsNotNull, ctorOrMethodOrMember, factoryInfo); - } - - return new FactoryMethod(ctorOrMethodOrMember, factoryInfo); - } - - /// Wraps method and factory instance. - /// Where is constructor, static or instance method, property or field. - public static FactoryMethod Of(MemberInfo methodOrMember, object factoryInstance) - { - factoryInstance.ThrowIfNull(); - methodOrMember.ThrowIfNull(Error.PassedCtorOrMemberIsNull); - if (methodOrMember.IsStatic()) - Throw.It(Error.PassedMemberIsStaticButInstanceFactoryIsNotNull, methodOrMember, factoryInstance); - return new FactoryMethod(methodOrMember, Constant(factoryInstance)); - } - - /// Discovers the static factory method or member by name in . - /// Should play nice with C# operator. - public static FactoryMethod Of(string methodOrMemberName) => - Of(typeof(TFactory).GetAllMembers().FindFirst(m => m.Name == methodOrMemberName)); - - /// Pretty prints wrapped method. - public override string ToString() - { - var s = new StringBuilder("{") - .Print(ConstructorOrMethodOrMember.DeclaringType) - .Append('.').Append(ConstructorOrMethodOrMember); - if (FactoryServiceInfo != null) - s.Append(" of factory service ").Append(FactoryServiceInfo); - if (FactoryExpression != null) - s.Append(" of factory expression ").Append(FactoryExpression); - return s.Append('}').ToString(); - } - - private struct CtorWithParameters - { - public ConstructorInfo Ctor; - public ParameterInfo[] Params; - } - - private static void OrderByParamsLengthDescendingViaInsertionSort(CtorWithParameters[] items) - { - int i, j; - for (i = 1; i < items.Length; i++) - { - var tmp = items[i]; - - for (j = i; j >= 1 && tmp.Params.Length > items[j - 1].Params.Length; j--) - { - ref var target = ref items[j]; - var source = items[j - 1]; - target.Ctor = source.Ctor; - target.Params = source.Params; - } - - ref var x = ref items[j]; - x.Ctor = tmp.Ctor; - x.Params = tmp.Params; - } - } - - /// Easy way to specify non-public and most resolvable constructor. - /// (optional) Instructs to select constructor with max number of params which all are resolvable. - /// (optional) Consider the non-public constructors. - /// Constructor or null if not found. - public static FactoryMethodSelector Constructor(bool mostResolvable = false, bool includeNonPublic = false) => request => - { - var implType = request.ImplementationType; - if (implType == null) - Throw.It(Error.ImplTypeIsNotSpecifiedForAutoCtorSelection, request); - - var ctors = includeNonPublic ? implType.GetInstanceConstructors() : implType.GetPublicInstanceConstructors(); - var ctorCount = ctors.Length; - if (ctorCount == 0) - return null; - - // if there is only one constructor then use it - if (ctorCount == 1) - return new FactoryMethod(ctors[0]); - - // stop here if you need a lookup for most resolvable constructor - if (!mostResolvable) - return null; - - var rules = request.Rules; - var selector = rules.OverrideRegistrationMade - ? request.Made.Parameters.OverrideWith(rules.Parameters) - : rules.Parameters.OverrideWith(request.Made.Parameters); - var paramSelector = selector(request); - - var throwIfCtorNotFound = request.IfUnresolved != IfUnresolved.ReturnDefault; - if (throwIfCtorNotFound) - request = request.WithIfUnresolved(IfUnresolved.ReturnDefault); - - var ctorsWithParameters = new CtorWithParameters[ctors.Length]; - if (ctors.Length == 2) - { - ref var pos0 = ref ctorsWithParameters[0]; - ref var pos1 = ref ctorsWithParameters[1]; - - var ctor0Params = ctors[0].GetParameters(); - var ctor1Params = ctors[1].GetParameters(); - if (ctor1Params.Length > ctor0Params.Length) - { - pos0.Ctor = ctors[1]; - pos0.Params = ctor1Params; - pos1.Ctor = ctors[0]; - pos1.Params = ctor0Params; - } - else - { - pos0.Ctor = ctors[0]; - pos0.Params = ctor0Params; - pos1.Ctor = ctors[1]; - pos1.Params = ctor1Params; - } - } - else - { - for (var i = 0; i < ctors.Length; i++) - { - var x = ctors[i]; - ref var pos = ref ctorsWithParameters[i]; - pos.Ctor = x; - pos.Params = x.GetParameters(); - } - - OrderByParamsLengthDescendingViaInsertionSort(ctorsWithParameters); - } - - var mostUsedArgCount = -1; - ConstructorInfo mostResolvedCtor = null; - Expression[] mostResolvedExprs = null; - for (var c = 0; c < ctorsWithParameters.Length; ++c) - { - var parameters = ctorsWithParameters[c].Params; - if (parameters.Length == 0) - { - mostResolvedCtor = mostResolvedCtor ?? ctorsWithParameters[c].Ctor; - break; - } - - // If the most resolved expressions (constructor) is found and the next one has less parameters, we exit. - if (mostResolvedExprs != null && mostResolvedExprs.Length > parameters.Length) - break; - - // Otherwise for similar parameters count constructor we prefer the one with most used input args / custom values - // Should count custom values provided via `Resolve(args)`, `Func`, `Parameter.Of...(_ -> arg)`, `container.Use(arg)` - var usedInputArgOrUsedOrCustomValueCount = 0; - var inputArgs = request.InputArgExprs; - var argsUsedMask = 0; - var paramExprs = new Expression[parameters.Length]; - - for (var i = 0; i < parameters.Length; i++) - { - var param = parameters[i]; - if (inputArgs != null) - { - var inputArgExpr = - ReflectionFactory.TryGetExpressionFromInputArgs(param.ParameterType, inputArgs, - ref argsUsedMask); - if (inputArgExpr != null) - { - ++usedInputArgOrUsedOrCustomValueCount; - paramExprs[i] = inputArgExpr; - continue; - } - } - - var paramInfo = paramSelector(param) ?? ParameterServiceInfo.Of(param); - var paramRequest = request.Push(paramInfo); - var paramDetails = paramInfo.Details; - var usedOrCustomValExpr = - ReflectionFactory.TryGetUsedInstanceOrCustomValueExpression(request, paramRequest, - paramDetails); - if (usedOrCustomValExpr != null) - { - ++usedInputArgOrUsedOrCustomValueCount; - paramExprs[i] = usedOrCustomValExpr; - continue; - } - - var injectedExpr = request.Container.ResolveFactory(paramRequest)?.GetExpressionOrDefault(paramRequest); - if (injectedExpr == null || - // When param is an empty array / collection, then we may use a default value instead (#581) - paramDetails.DefaultValue != null && - injectedExpr.NodeType == System.Linq.Expressions.ExpressionType.NewArrayInit && - ((NewArrayExpression) injectedExpr).Expressions.Count == 0) - { - // Check if parameter dependency itself (without propagated parent details) - // does not allow default, then stop checking the rest of parameters. - if (paramDetails.IfUnresolved == IfUnresolved.Throw) - { - paramExprs = null; - break; - } - - injectedExpr = paramDetails.DefaultValue != null - ? request.Container.GetConstantExpression(paramDetails.DefaultValue) - : paramRequest.ServiceType.GetDefaultValueExpression(); - } - - paramExprs[i] = injectedExpr; - } - - if (paramExprs != null && usedInputArgOrUsedOrCustomValueCount > mostUsedArgCount) - { - mostUsedArgCount = usedInputArgOrUsedOrCustomValueCount; - mostResolvedCtor = ctorsWithParameters[c].Ctor; - mostResolvedExprs = paramExprs; - } - } - - if (mostResolvedCtor == null) - return Throw.For(throwIfCtorNotFound, - Error.UnableToFindCtorWithAllResolvableArgs, request.InputArgExprs, request); - - return new FactoryMethod(mostResolvedCtor, mostResolvedExprs); - }; - - /// Easy way to specify default constructor to be used for resolution. - public static FactoryMethodSelector DefaultConstructor(bool includeNonPublic = false) => request => - request.ImplementationType.ThrowIfNull(Error.ImplTypeIsNotSpecifiedForAutoCtorSelection, request) - .GetConstructorOrNull(includeNonPublic, Empty()) - ?.To(ctor => new FactoryMethod(ctor)); - - /// Better be named `ConstructorWithMostResolvableArguments`. - /// Searches for public constructor with most resolvable parameters or throws if not found. - /// Works both for resolving service and `Func{TArgs..., TService}` - public static readonly FactoryMethodSelector ConstructorWithResolvableArguments = - Constructor(mostResolvable: true); - - /// Searches for constructor (including non public ones) with most - /// resolvable parameters or throws if not found. - /// Works both for resolving service and Func{TArgs..., TService} - public static readonly FactoryMethodSelector ConstructorWithResolvableArgumentsIncludingNonPublic = - Constructor(mostResolvable: true, includeNonPublic: true); - - /// Just creates a thingy from the constructor - public FactoryMethod(ConstructorInfo constructor) => ConstructorOrMethodOrMember = constructor; - - private FactoryMethod(MemberInfo constructorOrMethodOrMember, ServiceInfo factoryServiceInfo = null) - { - ConstructorOrMethodOrMember = constructorOrMethodOrMember; - FactoryServiceInfo = factoryServiceInfo; - } - - internal FactoryMethod(MemberInfo constructorOrMethodOrMember, Expression factoryExpression) - { - ConstructorOrMethodOrMember = constructorOrMethodOrMember; - FactoryExpression = factoryExpression; - } - - internal FactoryMethod(ConstructorInfo ctor, Expression[] resolvedParameterExpressions) - { - ConstructorOrMethodOrMember = ctor; - ResolvedParameterExpressions = resolvedParameterExpressions; - } - } - - /// Rules how to: - /// Select constructor for creating service with . - /// Specify how to resolve constructor parameters with . - /// Specify what properties/fields to resolve and how with . - /// - public class Made - { - /// Returns delegate to select constructor based on provided request. - public FactoryMethodSelector FactoryMethod { get; private set; } - - /// Return type of strongly-typed factory method expression. - public Type FactoryMethodKnownResultType { get; private set; } - - [Flags] - private enum MadeDetails - { - NoConditionals = 0, - ImplTypeDependsOnRequest = 1 << 1, - ImplMemberDependsOnRequest = 1 << 3, - HasCustomDependencyValue = 1 << 4 - } - - private readonly MadeDetails _details; - - /// Has any conditional flags - public bool IsConditional => _details != MadeDetails.NoConditionals; - - /// True is made has properties or parameters with custom value. - /// That's mean the whole made become context based which affects caching. - public bool HasCustomDependencyValue => (_details & MadeDetails.HasCustomDependencyValue) != 0; - - /// Indicates that the implementation type depends on request. - public bool IsConditionalImplementation => (_details & MadeDetails.ImplTypeDependsOnRequest) != 0; - - /// Indicates that the member depends on request - public bool IsImplMemberDependsOnRequest => (_details & MadeDetails.ImplMemberDependsOnRequest) != 0; - - /// Specifies how constructor parameters should be resolved: - /// parameter service key and type, throw or return default value if parameter is unresolved. - public ParameterSelector Parameters { get; private set; } - - /// Specifies what should be used when resolving property or field. - public PropertiesAndFieldsSelector PropertiesAndFields { get; private set; } - - /// Outputs whatever is possible (known) for Made - public override string ToString() - { - if (this == Default) - return "Made.Default"; - - var s = "{"; - if (FactoryMethod != null) - { - s += (s == "{" ? "" : ", ") + "FactoryMethod="; - if (FactoryMethod == DryIoc.FactoryMethod.ConstructorWithResolvableArguments) - s += nameof(DryIoc.FactoryMethod.ConstructorWithResolvableArguments); - else if (FactoryMethod == DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic) - s += nameof(DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic); - else - s += ""; - } - - if (FactoryMethodKnownResultType != null) - s += (s == "{" ? "" : ", ") + "FactoryMethodKnownResultType=" + FactoryMethodKnownResultType; - if (HasCustomDependencyValue) - s += (s == "{" ? "" : ", ") + "HasCustomDependencyValue=true"; - if (PropertiesAndFields != null) - s += (s == "{" ? "" : ", ") + "PropertiesAndFields="; - if (Parameters != null) - s += (s == "{" ? "" : ", ") + "ParameterSelector="; - return s + "}"; - } - - /// Container will use some sensible defaults for service creation. - public static readonly Made Default = new Made(); - - /// Creates rules with only specified. - public static implicit operator Made(FactoryMethodSelector factoryMethod) => - Of(factoryMethod); - - /// Creates rules with only specified. - public static implicit operator Made(ParameterSelector parameters) => - Of(parameters: parameters); - - /// Creates rules with only specified. - public static implicit operator Made(PropertiesAndFieldsSelector propertiesAndFields) => - Of(propertiesAndFields: propertiesAndFields); - - /// Specifies injections rules for Constructor, Parameters, Properties and Fields. If no rules specified returns rules. - public static Made Of(FactoryMethodSelector factoryMethod = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null, - bool isConditionalImlementation = false) => - factoryMethod == null && parameters == null && propertiesAndFields == null && !isConditionalImlementation ? Default : - new Made(factoryMethod, parameters, propertiesAndFields, isConditionalImlementation: isConditionalImlementation); - - /// Specifies injections rules for Constructor, Parameters, Properties and Fields. If no rules specified returns rules. - /// Known factory method. - /// (optional) (optional) - /// New injection rules. - public static Made Of(FactoryMethod factoryMethod, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) - { - var methodReturnType = factoryMethod.ThrowIfNull() - .ConstructorOrMethodOrMember.GetReturnTypeOrDefault(); - - // Normalizes open-generic type to open-generic definition, - // because for base classes and return types it may not be the case (they may be partially closed). - if (methodReturnType != null && methodReturnType.IsOpenGeneric()) - methodReturnType = methodReturnType.GetGenericTypeDefinition(); - - return new Made(factoryMethod.ToFunc, parameters, propertiesAndFields, methodReturnType); - } - - /// Creates factory method specification - public static Made Of(MemberInfo factoryMethodOrMember, ServiceInfo factoryInfo = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - Of(DryIoc.FactoryMethod.Of(factoryMethodOrMember, factoryInfo), parameters, propertiesAndFields); - - /// Creates factory specification with implementation type, conditionally depending on request. - public static Made Of(Func getImplType, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - Of(r => DryIoc.FactoryMethod.Of(getImplType(r).SingleConstructor()), - parameters, propertiesAndFields, isConditionalImlementation: true); - - /// Creates factory specification with method or member selector based on request. - /// Where is method, or constructor, or member selector. - public static Made Of(Func getMethodOrMember, ServiceInfo factoryInfo = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - new Made(r => DryIoc.FactoryMethod.Of(getMethodOrMember(r), factoryInfo), - parameters, propertiesAndFields, isImplMemberDependsOnRequest: true); - - /// Creates factory specification with method or member selector based on request. - /// Where Method, or constructor, or member selector. - public static Made Of(Func getMethodOrMember, Func factoryInfo, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - new Made(r => DryIoc.FactoryMethod.Of(getMethodOrMember(r), factoryInfo(r)), - parameters, propertiesAndFields, isImplMemberDependsOnRequest: true); - - /// Defines how to select constructor from implementation type. - /// Where is delegate taking implementation type as input - /// and returning selected constructor info. - public static Made Of(Func getConstructor, ParameterSelector parameters = null, - PropertiesAndFieldsSelector propertiesAndFields = null) => - Of(r => DryIoc.FactoryMethod.Of(getConstructor(r.ImplementationType).ThrowIfNull(Error.GotNullConstructorFromFactoryMethod, r)), - parameters, propertiesAndFields); - - /// Defines factory method using expression of constructor call (with properties), or static method call. - /// Type with constructor or static method. - /// Expression tree with call to constructor with properties: - /// new Car(Arg.Of()) { Color = Arg.Of("CarColor") }]]> - /// or static method call Car.Create(Arg.Of())]]> - /// (optional) Primitive custom values for dependencies. - /// New Made specification. - public static TypedMade Of( - System.Linq.Expressions.Expression> serviceReturningExpr, - params Func[] argValues) => - FromExpression(member => _ => DryIoc.FactoryMethod.Of(member), serviceReturningExpr, argValues); - - /// Defines creation info from factory method call Expression without using strings. - /// You can supply any/default arguments to factory method, they won't be used, it is only to find the . - /// Factory type. Factory product type. - /// Returns or resolves factory instance. - /// Method, property or field expression returning service. - /// (optional) Primitive custom values for dependencies. - /// New Made specification. - public static TypedMade Of( - Func> getFactoryInfo, - System.Linq.Expressions.Expression> serviceReturningExpr, - params Func[] argValues) - where TFactory : class - { - getFactoryInfo.ThrowIfNull(); - return FromExpression(member => request => DryIoc.FactoryMethod.Of(member, getFactoryInfo(request)), - serviceReturningExpr, argValues); - } - - /// Composes Made.Of expression with known factory instance and expression to get a service - public static TypedMade Of( - TFactory factoryInstance, - System.Linq.Expressions.Expression> serviceReturningExpr, - params Func[] argValues) - where TFactory : class - { - factoryInstance.ThrowIfNull(); - return FromExpression( - member => request => DryIoc.FactoryMethod.Of(member, factoryInstance), - serviceReturningExpr, argValues); - } - - private static TypedMade FromExpression( - Func getFactoryMethodSelector, - System.Linq.Expressions.LambdaExpression serviceReturningExpr, params Func[] argValues) - { - var callExpr = serviceReturningExpr.ThrowIfNull().Body; - if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.Convert) // proceed without Cast expression. - return FromExpression(getFactoryMethodSelector, - System.Linq.Expressions.Expression.Lambda(((System.Linq.Expressions.UnaryExpression)callExpr).Operand, - Empty()), - argValues); - - MemberInfo ctorOrMethodOrMember; - IList argExprs = null; - IList memberBindingExprs = null; - ParameterInfo[] parameters = null; - - if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.New || - callExpr.NodeType == System.Linq.Expressions.ExpressionType.MemberInit) - { - var newExpr = callExpr as System.Linq.Expressions.NewExpression ?? ((System.Linq.Expressions.MemberInitExpression)callExpr).NewExpression; - ctorOrMethodOrMember = newExpr.Constructor; - parameters = newExpr.Constructor.GetParameters(); - argExprs = newExpr.Arguments; - if (callExpr is System.Linq.Expressions.MemberInitExpression) - memberBindingExprs = ((System.Linq.Expressions.MemberInitExpression)callExpr).Bindings; - } - else if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.Call) - { - var methodCallExpr = (System.Linq.Expressions.MethodCallExpression)callExpr; - ctorOrMethodOrMember = methodCallExpr.Method; - parameters = methodCallExpr.Method.GetParameters(); - argExprs = methodCallExpr.Arguments; - } - else if (callExpr.NodeType == ExprType.Invoke) - { - var invokeExpr = (System.Linq.Expressions.InvocationExpression)callExpr; - var invokedDelegateExpr = invokeExpr.Expression; - var invokeMethod = invokedDelegateExpr.Type.GetTypeInfo().GetDeclaredMethod(nameof(Action.Invoke)); - ctorOrMethodOrMember = invokeMethod; - parameters = invokeMethod.GetParameters(); - argExprs = invokeExpr.Arguments; - } - - else if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.MemberAccess) - { - var member = ((System.Linq.Expressions.MemberExpression)callExpr).Member; - Throw.If(!(member is PropertyInfo) && !(member is FieldInfo), - Error.UnexpectedFactoryMemberExpressionInMadeOf, member, serviceReturningExpr); - ctorOrMethodOrMember = member; - } - else return Throw.For>(Error.NotSupportedMadeOfExpression, callExpr); - - var hasCustomValue = false; - - var parameterSelector = parameters.IsNullOrEmpty() ? null - : ComposeParameterSelectorFromArgs(ref hasCustomValue, - serviceReturningExpr, parameters, argExprs, argValues); - - var propertiesAndFieldsSelector = - memberBindingExprs == null || memberBindingExprs.Count == 0 ? null - : ComposePropertiesAndFieldsSelector(ref hasCustomValue, - serviceReturningExpr, memberBindingExprs, argValues); - - return new TypedMade(getFactoryMethodSelector(ctorOrMethodOrMember), - parameterSelector, propertiesAndFieldsSelector, hasCustomValue); - } - - /// Typed version of specified with statically typed expression tree. - public sealed class TypedMade : Made - { - internal TypedMade(FactoryMethodSelector factoryMethod = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null, - bool hasCustomValue = false) - : base(factoryMethod, parameters, propertiesAndFields, typeof(TService), hasCustomValue) - { } - } - -#region Implementation - - internal Made( - FactoryMethodSelector factoryMethod = null, ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null, - Type factoryMethodKnownResultType = null, bool hasCustomValue = false, bool isConditionalImlementation = false, - bool isImplMemberDependsOnRequest = false) - { - FactoryMethod = factoryMethod; - Parameters = parameters; - PropertiesAndFields = propertiesAndFields; - FactoryMethodKnownResultType = factoryMethodKnownResultType; - - var details = default(MadeDetails); - if (hasCustomValue) - details |= MadeDetails.HasCustomDependencyValue; - if (isConditionalImlementation) - details |= MadeDetails.ImplTypeDependsOnRequest; - if (isImplMemberDependsOnRequest) - details |= MadeDetails.ImplMemberDependsOnRequest; - _details = details; - } - - internal Made(FactoryMethod factoryMethod, Type factoryReturnType) - { - FactoryMethod = factoryMethod.ToFunc; - FactoryMethodKnownResultType = factoryReturnType; - } - - private static ParameterSelector ComposeParameterSelectorFromArgs(ref bool hasCustomValue, - System.Linq.Expressions.Expression wholeServiceExpr, ParameterInfo[] paramInfos, - IList argExprs, - params Func[] argValues) - { - var paramSelector = DryIoc.Parameters.Of; - for (var i = 0; i < argExprs.Count; i++) - { - var paramInfo = paramInfos[i]; - var methodCallExpr = argExprs[i] as System.Linq.Expressions.MethodCallExpression; - if (methodCallExpr != null) - { - Throw.If(methodCallExpr.Method.DeclaringType != typeof(Arg), - Error.UnexpectedExpressionInsteadOfArgMethodInMadeOf, methodCallExpr, wholeServiceExpr); - - if (methodCallExpr.Method.Name == Arg.ArgIndexMethodName) - { - var getArgValue = GetArgCustomValueProvider(wholeServiceExpr, methodCallExpr, argValues); - paramSelector = paramSelector.Details((r, p) => p.Equals(paramInfo) ? ServiceDetails.Of(getArgValue(r)) : null); - hasCustomValue = true; - } - else // handle service details - { - var defaultValue = paramInfo.IsOptional ? paramInfo.DefaultValue : null; - var argDetails = GetArgServiceDetails(wholeServiceExpr, - methodCallExpr, paramInfo.ParameterType, IfUnresolved.Throw, defaultValue); - paramSelector = paramSelector.Details((r, p) => p.Equals(paramInfo) ? argDetails : null); - } - } - else - { - var customValue = GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[i]); - paramSelector = paramSelector.Details((r, p) => p.Equals(paramInfo) ? ServiceDetails.Of(customValue) : null); - } - } - return paramSelector; - } - - private static PropertiesAndFieldsSelector ComposePropertiesAndFieldsSelector(ref bool hasCustomValue, - System.Linq.Expressions.Expression wholeServiceExpr, IList memberBindings, - params Func[] argValues) - { - var propertiesAndFields = DryIoc.PropertiesAndFields.Of; - for (var i = 0; i < memberBindings.Count; i++) - { - var memberAssignment = (memberBindings[i] as System.Linq.Expressions.MemberAssignment).ThrowIfNull(); - var member = memberAssignment.Member; - - var methodCallExpr = memberAssignment.Expression as System.Linq.Expressions.MethodCallExpression; - if (methodCallExpr == null) // not an Arg.Of: e.g. constant or variable - { - var customValue = GetArgExpressionValueOrThrow(wholeServiceExpr, memberAssignment.Expression); - propertiesAndFields = propertiesAndFields.OverrideWith(r => - PropertyOrFieldServiceInfo.Of(member).WithDetails(ServiceDetails.Of(customValue)).One()); - } - else - { - Throw.If(methodCallExpr.Method.DeclaringType != typeof(Arg), - Error.UnexpectedExpressionInsteadOfArgMethodInMadeOf, methodCallExpr, wholeServiceExpr); - - if (methodCallExpr.Method.Name == Arg.ArgIndexMethodName) // handle custom value - { - var getArgValue = GetArgCustomValueProvider(wholeServiceExpr, methodCallExpr, argValues); - propertiesAndFields = propertiesAndFields.OverrideWith(req => - PropertyOrFieldServiceInfo.Of(member).WithDetails(ServiceDetails.Of(getArgValue(req))).One()); - hasCustomValue = true; - } - else - { - var memberType = member.GetReturnTypeOrDefault(); - var argServiceDetails = GetArgServiceDetails(wholeServiceExpr, methodCallExpr, memberType, IfUnresolved.ReturnDefault, null); - propertiesAndFields = propertiesAndFields.OverrideWith(r => - PropertyOrFieldServiceInfo.Of(member).WithDetails(argServiceDetails).One()); - } - } - } - return propertiesAndFields; - } - - private static Func GetArgCustomValueProvider( - System.Linq.Expressions.Expression wholeServiceExpr, - System.Linq.Expressions.MethodCallExpression methodCallExpr, Func[] argValues) - { - Throw.If(argValues.IsNullOrEmpty(), Error.ArgValueIndexIsProvidedButNoArgValues, wholeServiceExpr); - - var argIndex = (int)GetArgExpressionValueOrThrow(wholeServiceExpr, methodCallExpr.Arguments[0]); - if (argIndex < 0 || argIndex >= argValues.Length) - Throw.It(Error.ArgValueIndexIsOutOfProvidedArgValues, argIndex, argValues, wholeServiceExpr); - - return argValues[argIndex]; - } - - private static ServiceDetails GetArgServiceDetails( - System.Linq.Expressions.Expression wholeServiceExpr, - System.Linq.Expressions.MethodCallExpression methodCallExpr, - Type dependencyType, IfUnresolved defaultIfUnresolved, object defaultValue) - { - var requiredServiceType = methodCallExpr.Method.GetGenericArguments().Last(); - if (requiredServiceType == dependencyType) - requiredServiceType = null; - - var serviceKey = default(object); - var metadataKey = default(string); - var metadata = default(object); - var ifUnresolved = defaultIfUnresolved; - - var hasPrevArg = false; - - var argExprs = methodCallExpr.Arguments; - if (argExprs.Count == 2 && - argExprs[0].Type == typeof(string) && - argExprs[1].Type != typeof(IfUnresolved)) // matches the Of overload for metadata - { - metadataKey = (string)GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[0]); - metadata = GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[1]); - } - else - { - for (var a = 0; a < argExprs.Count; a++) - { - var argValue = GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[a]); - if (argValue != null) - { - if (argValue is IfUnresolved) - { - ifUnresolved = (IfUnresolved)argValue; - if (hasPrevArg) // the only possible argument is default value. - { - defaultValue = serviceKey; - serviceKey = null; - } - } - else - { - serviceKey = argValue; - hasPrevArg = true; - } - } - } - } - - return ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata); - } - - private static object GetArgExpressionValueOrThrow( - System.Linq.Expressions.Expression wholeServiceExpr, - System.Linq.Expressions.Expression argExpr) - { - var valueExpr = argExpr as System.Linq.Expressions.ConstantExpression; - if (valueExpr != null) - return valueExpr.Value; - - var convert = argExpr as System.Linq.Expressions.UnaryExpression; // e.g. (object)SomeEnum.Value - if (convert != null && convert.NodeType == ExprType.Convert) - return GetArgExpressionValueOrThrow(wholeServiceExpr, - convert.Operand as System.Linq.Expressions.ConstantExpression); - - var member = argExpr as System.Linq.Expressions.MemberExpression; - if (member != null) - { - var memberOwner = member.Expression as System.Linq.Expressions.ConstantExpression; - if (memberOwner != null && memberOwner.Type.IsClosureType() && member.Member is FieldInfo) - return ((FieldInfo)member.Member).GetValue(memberOwner.Value); - } - - var newArrExpr = argExpr as System.Linq.Expressions.NewArrayExpression; - if (newArrExpr != null) - { - var itemExprs = newArrExpr.Expressions; - var items = new object[itemExprs.Count]; - for (var i = 0; i < itemExprs.Count; i++) - items[i] = GetArgExpressionValueOrThrow(wholeServiceExpr, itemExprs[i]); - - return Converter.ConvertMany(items, newArrExpr.Type.GetElementType()); - } - - return Throw.For(Error.UnexpectedExpressionInsteadOfConstantInMadeOf, - argExpr, wholeServiceExpr); - } - -#endregion - } - - /// Class for defining parameters/properties/fields service info in expressions. - /// Arg methods are NOT actually called, they just used to reflect service info from call expression. - public static class Arg - { - /// Specifies required service type of parameter or member. If required type is the same as parameter/member type, - /// the method is just a placeholder to help detect constructor or factory method, and does not have additional meaning. - public static TRequired Of() => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of() => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies if-unresolved policy. - public static TRequired Of(IfUnresolved ifUnresolved) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(IfUnresolved ifUnresolved) => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies service key. - public static TRequired Of(object serviceKey) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(object serviceKey) => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies service key. - public static TRequired Of(string metadataKey, object metadata) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(string metadataKey, object metadata) => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies if-unresolved policy. Plus specifies service key. - public static TRequired Of(IfUnresolved ifUnresolved, object serviceKey) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(IfUnresolved ifUnresolved, object serviceKey) => default(TService); - - /// Specifies required service type, default value and . - public static TRequired Of(TRequired defaultValue, IfUnresolved ifUnresolved) => default(TRequired); - - /// Specifies required service type, default value and . - public static TRequired Of(TRequired defaultValue, IfUnresolved ifUnresolved, object serviceKey) => default(TRequired); - - /// Specifies argument index starting from 0 to use corresponding custom value factory, - /// similar to String.Format "{0}, {1}, etc". - public static T Index(int argIndex) => default(T); - - /// Name is close to method itself to not forget when renaming the method. - public static string ArgIndexMethodName = "Index"; - } - - /// Contains extension methods to simplify general use cases. - public static class Registrator - { - /// The base method for registering servce with its implementation factory. Allows to specify all possible options. - public static void Register(this IRegistrator registrator, Type serviceType, Factory factory, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, false); - - /// Registers service with corresponding . - public static void Register(this IRegistrator registrator, Type serviceType, Type implementationType, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new ReflectionFactory(implementationType, reuse, made, setup), - serviceType, serviceKey, ifAlreadyRegistered, false); - - /// Registers service of . - /// ServiceType may be the same as . - public static void Register(this IRegistrator registrator, Type serviceAndMayBeImplementationType, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new ReflectionFactory(serviceAndMayBeImplementationType, reuse, made, setup), - serviceAndMayBeImplementationType, serviceKey, ifAlreadyRegistered, false); - - /// Registers service of type - /// implemented by type. - public static void Register(this IRegistrator registrator, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) - where TImplementation : TService => - registrator.Register(new ReflectionFactory(typeof(TImplementation), reuse, made, setup), - typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers implementation type with itself as service type. - public static void Register(this IRegistrator registrator, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(reuse, made, setup, ifAlreadyRegistered, serviceKey); - - /// Registers service type returned by Made expression. - public static void Register(this IRegistrator registrator, - Made.TypedMade made, IReuse reuse = null, Setup setup = null, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) where TMadeResult : TService => - registrator.Register(new ReflectionFactory(default(Type), reuse, made, setup), - typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers service returned by Made expression. - public static void Register(this IRegistrator registrator, - Made.TypedMade made, IReuse reuse = null, Setup setup = null, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.Register(made, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// - /// Registers the instance creating a "normal" DryIoc registration so you can check it via `IsRegestered`, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstance(this IRegistrator registrator, bool isChecked, Type serviceType, object instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) - { - registrator.Register(new RegisteredInstanceFactory(instance, DryIoc.Reuse.Singleton, setup), - serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: false); - - // done after registration to pass all the registration validation checks - if (instance is IDisposable && (setup == null || (!setup.PreventDisposal && !setup.WeaklyReferenced))) - (registrator as IResolverContext)?.SingletonScope.TrackDisposable(instance); - } - - /// - /// Registers the instance creating a "normal" DryIoc registration so you can check it via `IsRegestered`, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstance(this IRegistrator registrator, Type serviceType, object instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) => - registrator.RegisterInstance(false, serviceType, instance, ifAlreadyRegistered, setup, serviceKey); - - /// - /// Registers the instance creating a "normal" DryIoc registration so you can check it via `IsRegestered`, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstance(this IRegistrator registrator, T instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) => - registrator.RegisterInstance(true, typeof(T), instance, ifAlreadyRegistered, setup, serviceKey); - - /// - /// Registers the instance with possible multiple service types creating a "normal" DryIoc registration - /// so you can check it via `IsRegestered` for each service type, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstanceMany(this IRegistrator registrator, Type implType, object instance, - bool nonPublicServiceTypes = false, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) - { - instance.ThrowIfNull(); - if (implType != null) - instance.ThrowIfNotInstanceOf(implType); - else - implType = instance.GetType(); - - var serviceTypes = implType.GetImplementedServiceTypes(nonPublicServiceTypes); - - if (serviceTypes.Length == 0) - Throw.It(Error.NoServicesWereRegisteredByRegisterMany, implType.One()); - - var factory = new RegisteredInstanceFactory(instance, DryIoc.Reuse.Singleton, setup); - foreach (var serviceType in serviceTypes) - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - if (instance is IDisposable && (setup == null || (!setup.PreventDisposal && !setup.WeaklyReferenced))) - (registrator as IResolverContext)?.SingletonScope.TrackDisposable(instance); - } - - /// - /// Registers the instance with possible multiple service types creating a "normal" DryIoc registration - /// so you can check it via `IsRegestered` for each service type, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstanceMany(this IRegistrator registrator, T instance, - bool nonPublicServiceTypes = false, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) => - registrator.RegisterInstanceMany(instance.GetType(), instance, - nonPublicServiceTypes, ifAlreadyRegistered, setup, serviceKey); - - /// - /// Registers the instance with possible multiple service types creating a "normal" DryIoc registration - /// so you can check it via `IsRegestered` for each service type, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstanceMany(this IRegistrator registrator, Type[] serviceTypes, object instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) - { - var instanceType = instance.GetType(); - if (serviceTypes.IsNullOrEmpty()) - Throw.It(Error.NoServicesWereRegisteredByRegisterMany, instance); - - var factory = new RegisteredInstanceFactory(instance, DryIoc.Reuse.Singleton, setup); - - foreach (var serviceType in serviceTypes) - { - serviceType.ThrowIfNotImplementedBy(instanceType); - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - } - - if (instance is IDisposable && (setup == null || (!setup.PreventDisposal && !setup.WeaklyReferenced))) - (registrator as IResolverContext)?.SingletonScope.TrackDisposable(instance); - } - - /// List of types excluded by default from RegisterMany convention. - public static readonly string[] ExcludedGeneralPurposeServiceTypes = - { - "System.Object", - "System.IDisposable", - "System.ValueType", - "System.ICloneable", - "System.IEquatable", - "System.IComparable", - "System.Runtime.Serialization.ISerializable", - "System.Collections.IStructuralEquatable", - "System.Collections.IEnumerable", - "System.Collections.IList", - "System.Collections.ICollection", - }; - - /// Checks that type is not in the list of . - public static bool IsExcludedGeneralPurposeServiceType(this Type type) => - ExcludedGeneralPurposeServiceTypes.IndexOf((type.Namespace + "." + type.Name).Split('`')[0]) != -1; - - /// Checks that type can be used a service type. - public static bool IsServiceType(this Type type) => - !type.IsPrimitive() && !type.IsCompilerGenerated() && !type.IsExcludedGeneralPurposeServiceType(); - - /// Checks if type can be used as implementation type for reflection factory, - /// and therefore registered to container. Usually used to discover implementation types from assembly. - public static bool IsImplementationType(this Type type) => - type.IsClass() && !type.IsAbstract() && !type.IsCompilerGenerated(); - - /// Returns only those types that could be used as service types of . - /// It means that for open-generic its service type should supply all type arguments. - public static Type[] GetImplementedServiceTypes(this Type type, bool nonPublicServiceTypes = false) - { - var implementedTypes = type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType); - - var serviceTypes = nonPublicServiceTypes - ? implementedTypes.Match(t => t.IsServiceType()) - : implementedTypes.Match(t => t.IsPublicOrNestedPublic() && t.IsServiceType()); - - if (type.IsGenericDefinition()) - serviceTypes = serviceTypes.Match(type.GetGenericParamsAndArgs(), - (paramsAndArgs, x) => x.ContainsAllGenericTypeParameters(paramsAndArgs), - (_, x) => x.GetGenericDefinitionOrNull()); - - return serviceTypes; - } - - /// The same `GetImplementedServiceTypes` but instead of collecting the service types just check the is implemented - public static bool IsImplementingServiceType(this Type type, Type serviceType) - { - if (serviceType == type || serviceType == typeof(object)) - return true; - - var implTypeInfo = type.GetTypeInfo(); - if (!implTypeInfo.IsGenericTypeDefinition) - { - if (serviceType.IsInterface()) - { - foreach (var iface in implTypeInfo.ImplementedInterfaces) - if (iface == serviceType) - return true; - } - else - { - var baseType = implTypeInfo.BaseType; - for (; baseType != null && baseType != typeof(object); baseType = baseType.GetTypeInfo().BaseType) - if (serviceType == baseType) - return true; - } - } - else if (serviceType.IsGenericDefinition()) - { - var implTypeParams = implTypeInfo.GenericTypeParameters; - if (serviceType.IsInterface()) - { - foreach (var iface in implTypeInfo.ImplementedInterfaces) - if (iface.GetGenericDefinitionOrNull() == serviceType && - iface.ContainsAllGenericTypeParameters(implTypeParams)) - return true; - } - else - { - var baseType = implTypeInfo.BaseType; - for (; baseType != null && baseType != typeof(object); baseType = baseType.GetTypeInfo().BaseType) - if (baseType.GetGenericDefinitionOrNull() == serviceType && - baseType.ContainsAllGenericTypeParameters(implTypeParams)) - return true; - } - } - - return false; - } - - /// Returns the sensible services automatically discovered for RegisterMany implementation type. - /// Excludes the collection wrapper interfaces. The may be concrete, abstract or - /// generic definition. - public static Type[] GetRegisterManyImplementedServiceTypes(this Type type, bool nonPublicServiceTypes = false) => - GetImplementedServiceTypes(type, nonPublicServiceTypes) - .Match(t => !t.IsGenericDefinition() || WrappersSupport.SupportedCollectionTypes.IndexOfReference(t) == -1); - - /// Returns the types suitable to be an implementation types for : - /// actually a non abstract and not compiler generated classes. - public static IEnumerable GetImplementationTypes(this Assembly assembly) => - Portable.GetAssemblyTypes(assembly).Where(IsImplementationType); - - /// Returns the types suitable to be an implementation types for : - /// actually a non abstract and not compiler generated classes. - public static IEnumerable GetImplementationTypes(this Assembly assembly, Func condition) => - Portable.GetAssemblyTypes(assembly).Where(t => condition(t) && t.IsImplementationType()); - - /// Sugar, so you can say (Registrator.Interfaces)]]> - public static Func Interfaces = ReflectionTools.IsInterface; - - /// Checks if implements a service type, - /// along the checking if is a valid implementation type. - public static bool ImplementsServiceType(this Type type, Type serviceType) => - type.IsImplementationType() && type.IsImplementingServiceType(serviceType); - - /// Checks if implements a service type, - /// along the checking if and service type - /// are valid implementation and service types. - public static bool ImplementsServiceType(this Type type) => - type.ImplementsServiceType(typeof(TService)); - - /// Wraps the implementation type in factory. - public static Factory ToFactory(this Type implType) => new ReflectionFactory(implType); - - /// Wraps the implementation type in factory plus allow to provide factory parameters. - public static Factory ToFactory(this Type implType, IReuse reuse, Made made = null, Setup setup = null) => - new ReflectionFactory(implType, reuse, made, setup); - - /// A primary (basic) method for batch registering of implementations with possibly many service types. - /// The default factory is the with default reuse. - public static void RegisterMany(this IRegistrator registrator, - IEnumerable implTypes, Func getServiceTypes, - Func getImplFactory = null, Func getServiceKey = null, - IfAlreadyRegistered? ifAlreadyRegistered = null) - { - getImplFactory = getImplFactory ?? ToFactory; - - bool isSomeoneRegistered = false; - bool anyImplTypes = false; - foreach (var implType in implTypes) - { - anyImplTypes = true; - var serviceTypes = getServiceTypes(implType); - if (!serviceTypes.IsNullOrEmpty()) - { - var factory = getImplFactory(implType); - for (var i = 0; i < serviceTypes.Length; i++) - { - var t = serviceTypes[i]; - registrator.Register(t, factory, ifAlreadyRegistered, getServiceKey?.Invoke(implType, t)); - isSomeoneRegistered = true; - } - } - } - - if (anyImplTypes && !isSomeoneRegistered) - Throw.It(Error.NoServicesWereRegisteredByRegisterMany, implTypes); - } - - /// Batch registers implementation with possibly many service types. - public static void RegisterMany(this IRegistrator registrator, - Type[] serviceTypes, Type implType, - IReuse reuse = null, Made made = null, Setup setup = null, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.RegisterMany(new[] { implType }, serviceTypes.ToFunc, - t => t.ToFactory(reuse, made, setup), (_, __) => serviceKey, ifAlreadyRegistered); - - /// Batch registers assemblies of implementation types with possibly many service types. - /// The default factory is the with default reuse. - public static void RegisterMany(this IRegistrator registrator, - IEnumerable implTypeAssemblies, Func getServiceTypes, - Func getImplFactory = null, Func getServiceKey = null, - IfAlreadyRegistered? ifAlreadyRegistered = null) => - registrator.RegisterMany(implTypeAssemblies.ThrowIfNull().SelectMany(GetImplementationTypes), - getServiceTypes, getImplFactory, getServiceKey, ifAlreadyRegistered); - - /// Registers many implementations with their auto-figured service types. - public static void RegisterMany(this IRegistrator registrator, - IEnumerable implTypeAssemblies, Func serviceTypeCondition, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - bool nonPublicServiceTypes = false, object serviceKey = null) => - registrator.RegisterMany(implTypeAssemblies.ThrowIfNull().SelectMany(GetImplementationTypes), - reuse, made, setup, ifAlreadyRegistered, serviceTypeCondition, nonPublicServiceTypes, serviceKey); - - /// Registers many implementations with auto-figured service types. - public static void RegisterMany(this IRegistrator registrator, IEnumerable implTypes, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - Func serviceTypeCondition = null, bool nonPublicServiceTypes = false, - object serviceKey = null) => - registrator.RegisterMany(implTypes, - t => t.GetRegisterManyImplementedServiceTypes(nonPublicServiceTypes).Match(serviceTypeCondition ?? Fun.Always), - reuse == null && made == null && setup == null ? default(Func) : t => t.ToFactory(reuse, made, setup), - serviceKey == null ? default(Func) : (i, s) => serviceKey, - ifAlreadyRegistered); - - /// Registers single registration for all implemented public interfaces and base classes. - public static void RegisterMany(this IRegistrator registrator, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - Func serviceTypeCondition = null, bool nonPublicServiceTypes = false, - object serviceKey = null) => - registrator.RegisterMany(typeof(TImplementation).One(), - reuse, made, setup, ifAlreadyRegistered, serviceTypeCondition, nonPublicServiceTypes, serviceKey); - - /// Registers single registration for all implemented public interfaces and base classes. - public static void RegisterMany(this IRegistrator registrator, - Made.TypedMade made, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - Func serviceTypeCondition = null, bool nonPublicServiceTypes = false, - object serviceKey = null) => - registrator.RegisterMany(reuse, made.ThrowIfNull(), setup, - ifAlreadyRegistered, serviceTypeCondition, nonPublicServiceTypes, serviceKey); - - /// Registers a factory delegate for creating an instance of . - /// Delegate can use resolver context parameter to resolve any required dependencies, e.g.: - /// (r => new Car(r.Resolve()))]]> - /// The alternative to this method please consider using instead: - /// (Made.Of(() => new Car(Arg.Of())))]]>. - /// - public static void RegisterDelegate(this IRegistrator registrator, - Func factoryDelegate, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new DelegateFactory(factoryDelegate.ToFactoryDelegate, reuse, setup), - typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers delegate to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Type serviceType, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc>(r, serviceType, - dep1 => factory(dep1).ThrowIfNotInstanceOf(serviceType, Error.RegisteredDelegateResultIsNotOfServiceType), - reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - private const string InvokeMethodName = "Invoke"; - private static void RegisterDelegateFunc(IRegistrator r, Type serviceType, - TFunc factory, IReuse reuse, Setup setup, IfAlreadyRegistered? ifAlreadyRegistered, object serviceKey) - { - var invokeMethod = typeof(TFunc).GetTypeInfo().GetDeclaredMethod(InvokeMethodName); - var made = new Made(new FactoryMethod(invokeMethod, Constant(factory)), serviceType); - r.Register(new ReflectionFactory(serviceType, reuse, made, setup), - serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - } - - /// Minimizes the number of allocations when converting from Func to named delegate - public static object ToFactoryDelegate(this Func f, IResolverContext r) => f(r); - - /// Lifts the result to the factory delegate without allocations on capturing value in lambda closure - public static object ToFactoryDelegate(this object result, IResolverContext _) => result; - - /// Registers a factory delegate for creating an instance of . - /// Delegate can use resolver context parameter to resolve any required dependencies, e.g.: - /// (r => new Car(r.Resolve()))]]> - /// IMPORTANT: The method should be used as the last resort only! Though powerful it is a black-box for container, - /// which prevents diagnostics, plus it is easy to get memory leaks (due variables captured in delegate closure), - /// and impossible to use in compile-time scenarios. - /// Consider using instead: - /// (Made.Of(() => new Car(Arg.Of())))]]> - /// - public static void RegisterDelegate(this IRegistrator registrator, - Type serviceType, Func factoryDelegate, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) - { - if (serviceType.IsOpenGeneric()) - Throw.It(Error.RegisteringOpenGenericRequiresFactoryProvider, serviceType); - - FactoryDelegate checkedDelegate = r => factoryDelegate(r) - .ThrowIfNotInstanceOf(serviceType, Error.RegisteredDelegateResultIsNotOfServiceType); - - var factory = new DelegateFactory(checkedDelegate, reuse, setup); - - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: false); - } - - /// A special performant version mostly for integration with other libraries, - /// that already check compatiblity between delegate result and the service type - public static void RegisterDelegate(this IRegistrator registrator, - bool isChecked, Type serviceType, Func factoryDelegate, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new DelegateFactory(factoryDelegate.ToFactoryDelegate, reuse, setup), - serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers decorator function that gets decorated value as input and returns decorator. - /// Note: Delegate decorator will use of decoratee service. - public static void RegisterDelegateDecorator(this IRegistrator registrator, - Func> getDecorator, Func condition = null) - { - getDecorator.ThrowIfNull(); - - // unique key to bind decorator factory and decorator registrations - var factoryKey = new object(); - - registrator.RegisterDelegate( - _ => new DecoratorDelegateFactory(getDecorator), - serviceKey: factoryKey); - - registrator.Register(Made.Of( - _ => ServiceInfo.Of>(serviceKey: factoryKey), - f => f.Decorate(Arg.Of(), Arg.Of())), - setup: Setup.DecoratorWith(condition, useDecorateeReuse: true)); - } - - internal sealed class DecoratorDelegateFactory - { - private readonly Func> _getDecorator; - public DecoratorDelegateFactory(Func> getDecorator) { _getDecorator = getDecorator; } - public TDecoratee Decorate(TDecoratee decoratee, IResolverContext r) => _getDecorator(r).Invoke(decoratee); - } - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, TService instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(typeof(TService), instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IRegistrator r, TService instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(typeof(TService), instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IContainer c, TService instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - c.UseInstance(typeof(TService), instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, Type serviceType, object instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IRegistrator r, Type serviceType, object instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IContainer c, Type serviceType, object instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - c.UseInstance(serviceType, instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, TService instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(typeof(TService), instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IRegistrator r, Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// - /// Will become OBSOLETE in the next major version! - /// Please use `RegisterInstance` or `Use` method instead. - /// - public static void UseInstance(this IContainer c, Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - c.UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// Adding the factory directly to scope for resolution - public static void Use(this IResolverContext r, Func factory) => - r.Use(typeof(TService), factory.ToFactoryDelegate); - - /// Adding the instance directly to the scope for resolution - public static void Use(this IResolverContext r, Type serviceType, object instance) => - r.Use(serviceType, instance.ToFactoryDelegate); - - /// Adding the instance directly to the scope for resolution - public static void Use(this IResolverContext r, TService instance) => - r.Use(typeof(TService), instance.ToFactoryDelegate); - - /// Adding the factory directly to the scope for resolution - public static void Use(this IRegistrator r, Func factory) => - r.Use(typeof(TService), factory.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IRegistrator r, Type serviceType, object instance) => - r.Use(serviceType, instance.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IRegistrator r, TService instance) => - r.Use(typeof(TService), instance.ToFactoryDelegate); - - /// Adding the factory directly to scope for resolution - public static void Use(this IContainer c, Func factory) => - ((IResolverContext)c).Use(typeof(TService), factory.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IContainer c, Type serviceType, object instance) => - ((IResolverContext)c).Use(serviceType, instance.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IContainer c, TService instance) => - ((IResolverContext)c).Use(typeof(TService), instance.ToFactoryDelegate); - - /// - /// Registers initializing action that will be called after service is resolved - /// just before returning it to the caller. You can register multiple initializers for single service. - /// Or you can register initializer for type to be applied - /// for all services and use to specify the target services. - /// - public static void RegisterInitializer(this IRegistrator registrator, - Action initialize, Func condition = null) - { - initialize.ThrowIfNull(); - registrator.Register( - made: Made.Of(r => _initializerMethod.MakeGenericMethod(typeof(TTarget), r.ServiceType), - // specify ResolverContext as parameter to prevent applying initializer for injected resolver too - parameters: Parameters.Of - .Type(r => r.IsSingletonOrDependencyOfSingleton && !r.OpensResolutionScope - ? r.Container.RootOrSelf() : r.Container) - .Type(initialize.ToFunc>)), - setup: Setup.DecoratorWith( - r => r.ServiceType.IsAssignableTo() && (condition == null || condition(r)), - useDecorateeReuse: true, // issue BitBucket #230 - ensures the initialization to happen once on construction - preventDisposal: true)); // issue #215 - ensures that the initialized / decorated object does not added for the disposal twice - } - - private static readonly MethodInfo _initializerMethod = - typeof(Registrator).SingleMethod(nameof(Initializer), includeNonPublic: true); - - internal static TService Initializer( - TService service, IResolverContext resolver, Action initialize) where TService : TTarget - { - initialize(service, resolver); - return service; - } - - /// Registers dispose action for reused target service. - public static void RegisterDisposer(this IRegistrator registrator, - Action dispose, Func condition = null) - { - dispose.ThrowIfNull(); - - var disposerKey = new object(); - - registrator.RegisterDelegate(_ => new Disposer(dispose), - serviceKey: disposerKey, - // tracking instead of parent reuse, so that I can use one disposer for multiple services - setup: Setup.With(trackDisposableTransient: true)); - - var disposerType = typeof(Disposer<>).MakeGenericType(typeof(TService)); - registrator.Register( - made: Made.Of( - r => disposerType.SingleMethod("TrackForDispose").MakeGenericMethod(r.ServiceType), - ServiceInfo.Of(disposerType, serviceKey: disposerKey)), - setup: Setup.DecoratorWith( - r => r.ServiceType.IsAssignableTo() && (condition == null || condition(r)), - useDecorateeReuse: true)); - } - - internal sealed class Disposer : IDisposable - { - private readonly Action _dispose; - private int _state; - private const int TRACKED = 1, DISPOSED = 2; - private T _item; - - public Disposer(Action dispose) - { - _dispose = dispose; - } - - public S TrackForDispose(S item) where S : T - { - if (Interlocked.CompareExchange(ref _state, TRACKED, 0) != 0) - Throw.It(Error.DisposerTrackForDisposeError, _state == TRACKED ? " tracked" : "disposed"); - _item = item; - return item; - } - - public void Dispose() - { - if (Interlocked.CompareExchange(ref _state, DISPOSED, TRACKED) != TRACKED) - return; - var item = _item; - if (item != null) - { - _dispose(item); - _item = default(T); - } - } - } - - /// Returns true if is registered in container OR - /// its open generic definition is registered in container. - /// The additional implementation factory may be specified to narrow the search. - public static bool IsRegistered(this IRegistrator registrator, Type serviceType, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.IsRegistered(serviceType, serviceKey, factoryType, condition); - - /// Returns true if is registered in container OR - /// its open generic definition is registered in container. - /// The additional implementation factory may be specified to narrow the search. - public static bool IsRegistered(this IRegistrator registrator, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.IsRegistered(typeof(TService), serviceKey, factoryType, condition); - - /// Removes specified registration from container. - /// It also tries to remove the cached resolutions for the removed registration, But it may not work depending on context. - /// Check the docs for more info: https://bitbucket.org/dadhi/dryioc/wiki/UnregisterAndResolutionCache - public static void Unregister(this IRegistrator registrator, Type serviceType, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.Unregister(serviceType, serviceKey, factoryType, condition); - - /// Removes specified registration from container. - /// It also tries to remove the cached resolutions for the removed registration, But it may not work depending on context. - /// Check the docs for more info: https://bitbucket.org/dadhi/dryioc/wiki/UnregisterAndResolutionCache - public static void Unregister(this IRegistrator registrator, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.Unregister(typeof(TService), serviceKey, factoryType, condition); - - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Registrator New service type. - /// Existing registered service type. - /// (optional) (optional) - /// (optional) By default is - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IRegistrator registrator, Type serviceType, Type registeredServiceType, - object serviceKey = null, object registeredServiceKey = null, FactoryType factoryType = FactoryType.Service) - { - var factories = registrator.GetRegisteredFactories(registeredServiceType, registeredServiceKey, factoryType); - - if (factories.IsNullOrEmpty()) - Throw.It(Error.RegisterMappingNotFoundRegisteredService, registeredServiceType, registeredServiceKey); - - if (factories.Length > 1) - Throw.It(Error.RegisterMappingUnableToSelectFromMultipleFactories, serviceType, serviceKey, factories); - - registrator.Register(factories[0], serviceType, serviceKey, IfAlreadyRegistered.Keep, false); - } - - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Registrator - /// New service type. - /// Existing registered service type. - /// (optional) (optional) - /// (optional) By default is - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IRegistrator registrator, - object serviceKey = null, object registeredServiceKey = null, FactoryType factoryType = FactoryType.Service) => - registrator.RegisterMapping(typeof(TService), typeof(TRegisteredService), serviceKey, registeredServiceKey); - - /// Register a service without implementation which can be provided later in terms - /// of normal registration with IfAlreadyRegistered.Replace parameter. - /// When the implementation is still not provided when the placeholder service is accessed, - /// then the exception will be thrown. - /// This feature allows you to postpone decision on implementation until it is later known. - /// Internally the empty factory is registered with the setup asResolutionCall set to true. - /// That means, instead of placing service instance into graph expression we put here redirecting call to - /// container Resolve. - public static void RegisterPlaceholder(this IRegistrator registrator, Type serviceType, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.Register(FactoryPlaceholder.Default, serviceType, serviceKey, ifAlreadyRegistered, true); - } - - /// Extension methods for . - public static class Resolver - { - internal static readonly MethodInfo ResolveFastMethod = - typeof(IResolver).Method(nameof(IResolver.Resolve), typeof(Type), typeof(IfUnresolved)); - - internal static readonly MethodInfo ResolveMethod = - typeof(IResolver).Method(nameof(IResolver.Resolve), typeof(Type), typeof(object), - typeof(IfUnresolved), typeof(Type), typeof(Request), typeof(object[])); - - internal static readonly MethodInfo ResolveManyMethod = - typeof(IResolver).GetTypeInfo().GetDeclaredMethod(nameof(IResolver.ResolveMany)); - - /// Resolves instance of service type from container. Throws exception if unable to resolve. - public static object Resolve(this IResolver resolver, Type serviceType) => - resolver.Resolve(serviceType, IfUnresolved.Throw); - - /// Resolves instance of service type from container. - public static object Resolve(this IResolver resolver, Type serviceType, IfUnresolved ifUnresolved) => - resolver.Resolve(serviceType, ifUnresolved); - - /// Resolves instance of type TService from container. - public static TService Resolve(this IResolver resolver, - IfUnresolved ifUnresolved = IfUnresolved.Throw) => - (TService)resolver.Resolve(typeof(TService), ifUnresolved); - - /// Tries to resolve instance of service type from container. - public static object Resolve(this IResolver resolver, Type serviceType, bool ifUnresolvedReturnDefault) => - resolver.Resolve(serviceType, ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw); - - /// Tries to resolve instance of TService from container. - public static object Resolve(this IResolver resolver, bool ifUnresolvedReturnDefault) => - resolver.Resolve(typeof(TService), ifUnresolvedReturnDefault); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc. - /// allow you to specify wrapped service type. - /// (); - /// var services = container.Resolve(typeof(IEnumerable), typeof(IService)); - /// ]]> - public static object Resolve(this IResolver resolver, Type serviceType, Type requiredServiceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object[] args = null, object serviceKey = null) => - resolver.Resolve(serviceType, serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc. - /// allow you to specify wrapped service type. - /// (); - /// var services = container.Resolve>(typeof(IService)); - /// ]]> - public static TService Resolve(this IResolver resolver, Type requiredServiceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object[] args = null, object serviceKey = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc. - /// allow you to specify wrapped service type. - /// (); - /// var services = container.Resolve, IService>(); - /// ]]> - public static TService Resolve(this IResolver resolver, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object[] args = null, object serviceKey = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, typeof(TRequiredService), Request.Empty, args); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc., - /// could specify wrapped service type. - /// Using implicitly support Covariance for generic wrappers even in .Net 3.5. - /// (); - /// var services = container.Resolve(typeof(Lazy), "someKey", requiredServiceType: typeof(IService)); - /// ]]> - public static object Resolve(this IResolver resolver, Type serviceType, object serviceKey, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object[] args = null) => - resolver.Resolve(serviceType, serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns instance of type. - /// The type of the requested service. - /// The requested service instance. - /// Using implicitly support Covariance for generic wrappers even in .Net 3.5. - public static TService Resolve(this IResolver resolver, object serviceKey, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object[] args = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Resolves the service supplying all or some of its dependencies - /// (including nested) with the . The rest of dependencies is injected from - /// container. - public static object Resolve(this IResolver resolver, Type serviceType, object[] args, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object serviceKey = null) => - resolver.Resolve(serviceType, serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Resolves the service supplying all or some of its dependencies - /// (including nested) with the . The rest of dependencies is injected from - /// container. - public static TService Resolve(this IResolver resolver, object[] args, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object serviceKey = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns all registered services instances including all keyed and default registrations. - /// Use to return either all registered services at the moment of resolve (dynamic fresh view) or - /// the same services that were returned with first call (fixed view). - /// Return collection item type. - /// It denotes registered service type if is not specified. - /// The same result could be achieved by directly calling: - /// >(); // for dynamic result - default behavior - /// container.Resolve(); // for fixed array - /// container.Resolve>(); // same as fixed array - /// ]]> - /// - public static IEnumerable ResolveMany(this IResolver resolver, - Type requiredServiceType = null, ResolveManyBehavior behavior = ResolveManyBehavior.AsLazyEnumerable, - object[] args = null, object serviceKey = null) => - behavior == ResolveManyBehavior.AsLazyEnumerable - ? resolver.ResolveMany(typeof(TService), serviceKey, requiredServiceType, Request.Empty, args).Cast() - : resolver.Resolve>(serviceKey, IfUnresolved.Throw, requiredServiceType, args); - - /// Returns all registered services as objects, including all keyed and default registrations. - public static IEnumerable ResolveMany(this IResolver resolver, Type serviceType, - ResolveManyBehavior behavior = ResolveManyBehavior.AsLazyEnumerable, - object[] args = null, object serviceKey = null) => - resolver.ResolveMany(serviceType, behavior, args, serviceKey); - - internal static readonly ConstructorInfo ResolutionScopeNameCtor = - typeof(ResolutionScopeName).GetTypeInfo().DeclaredConstructors.First(); - - internal static Expression CreateResolutionExpression(Request request, bool opensResolutionScope = false) - { - if (request.Rules.DependencyResolutionCallExprs != null && - request.Factory != null && !request.Factory.HasRuntimeState) - PopulateDependencyResolutionCallExpressions(request); - - var container = request.Container; - var serviceType = request.ServiceType; - - var serviceTypeExpr = Constant(serviceType, typeof(Type)); - var ifUnresolvedExpr = Constant(request.IfUnresolved, typeof(IfUnresolved)); - var requiredServiceTypeExpr = Constant(request.RequiredServiceType, typeof(Type)); - var serviceKeyExpr = container.GetConstantExpression(request.ServiceKey, typeof(object)); - - var resolverExpr = ResolverContext.GetRootOrSelfExpr(request); - - if (opensResolutionScope) - { - // Generates the code below. That means the service opening the scope is scoped to this scope. - // - // r => r.OpenScope(new ResolutionScopeName(serviceType, serviceKey), trackInParent: true) - // .Resolve(serviceType, serviceKey) - // - var actualServiceTypeExpr = Constant(request.GetActualServiceType(), typeof(Type)); - var scopeNameExpr = New(ResolutionScopeNameCtor, actualServiceTypeExpr, serviceKeyExpr); - var trackInParent = Constant(true); - - resolverExpr = Call(ResolverContext.OpenScopeMethod, - FactoryDelegateCompiler.ResolverContextParamExpr, scopeNameExpr, trackInParent); - } - - var parentFlags = default(RequestFlags); - if (opensResolutionScope) - parentFlags |= RequestFlags.OpensResolutionScope; - if ((request.Flags & RequestFlags.StopRecursiveDependencyCheck) != 0) - parentFlags |= RequestFlags.StopRecursiveDependencyCheck; - - // Only parent is converted to be passed to Resolve. - // The current request is formed by rest of Resolve parameters. - var preResolveParentExpr = container.GetRequestExpression(request.DirectParent, parentFlags); - - var resolveCallExpr = Call(resolverExpr, ResolveMethod, serviceTypeExpr, serviceKeyExpr, - ifUnresolvedExpr, requiredServiceTypeExpr, preResolveParentExpr, request.GetInputArgsExpr()); - - if (serviceType == typeof(object)) - return resolveCallExpr; - - return Convert(resolveCallExpr, serviceType); - } - - private static void PopulateDependencyResolutionCallExpressions(Request request) - { - // Actually calls nested Resolve and stores produced expression in collection inside the container Rules. - // Stops on recursive dependency, e.g. - // `new A(new Lazy(r => r.Resolve())` and `new B(new A())` - for (var p = request.DirectParent; !p.IsEmpty; p = p.DirectParent) - if (p.FactoryID == request.FactoryID) - return; - - var factory = request.Container.ResolveFactory(request); - if (factory == null || factory is FactoryPlaceholder) - return; - - // Prevents infinite recursion when generating the resolution dependency #579 - if ((request.Flags & RequestFlags.IsGeneratedResolutionDependencyExpression) != 0) - return; - - request.Flags |= RequestFlags.IsGeneratedResolutionDependencyExpression; - - var factoryExpr = factory.GetExpressionOrDefault(request)?.NormalizeExpression(); - if (factoryExpr == null) - return; - - request.Container.Rules.DependencyResolutionCallExprs.Swap(request, factoryExpr, - (x, req, facExpr) => x.AddOrUpdate(req, facExpr -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToExpression() -#endif - )); - } - } - - /// Specifies result of : either dynamic(lazy) or fixed view. - public enum ResolveManyBehavior - { - /// Lazy/dynamic item resolve. - AsLazyEnumerable, - /// Fixed array of item at time of resolve, newly registered/removed services won't be listed. - AsFixedArray - } - - /// Provides information required for service resolution: service type - /// and optional - public interface IServiceInfo - { - /// The required piece of info: service type. - Type ServiceType { get; } - - /// Additional optional details: service key, if-unresolved policy, required service type. - ServiceDetails Details { get; } - - /// Creates info from service type and details. - IServiceInfo Create(Type serviceType, ServiceDetails details); - } - - /// Provides optional service resolution details: service key, required service type, what return when service is unresolved, - /// default value if service is unresolved, custom service value. - public class ServiceDetails - { - /// Default details if not specified, use default setting values, e.g. - public static readonly ServiceDetails Default = - new ServiceDetails(null, IfUnresolved.Throw, null, null, null, null, false); - - /// Default details with option. - public static readonly ServiceDetails IfUnresolvedReturnDefault = - new ServiceDetails(null, IfUnresolved.ReturnDefault, null, null, null, null, false); - - /// Default details with option. - public static readonly ServiceDetails IfUnresolvedReturnDefaultIfNotRegistered = - new ServiceDetails(null, IfUnresolved.ReturnDefaultIfNotRegistered, null, null, null, null, false); - - /// Creates new details out of provided settings, or returns default if all settings have default value. - public static ServiceDetails Of(Type requiredServiceType = null, - object serviceKey = null, IfUnresolved ifUnresolved = IfUnresolved.Throw, - object defaultValue = null, string metadataKey = null, object metadata = null) - { - if (defaultValue != null) - { - // IfUnresolved.Throw does not make sense when default value is provided, so normalizing it to ReturnDefault - if (ifUnresolved == IfUnresolved.Throw) - ifUnresolved = IfUnresolved.ReturnDefault; - } - else if (requiredServiceType == null && serviceKey == null && - metadataKey == null && metadata == null) - { - if (ifUnresolved == IfUnresolved.Throw) - return Default; - if (ifUnresolved == IfUnresolved.ReturnDefault) - return IfUnresolvedReturnDefault; - if (ifUnresolved == IfUnresolved.ReturnDefaultIfNotRegistered) - return IfUnresolvedReturnDefaultIfNotRegistered; - } - - return new ServiceDetails(requiredServiceType, ifUnresolved, - serviceKey, metadataKey, metadata, defaultValue, hasCustomValue: false); - } - - /// Sets custom value for service. This setting is orthogonal to the rest. - /// Using default value with invalid ifUnresolved.Throw option to indicate custom value. - public static ServiceDetails Of(object value) => - new ServiceDetails(null, IfUnresolved.Throw, null, null, null, value, hasCustomValue: true); - - /// Service type to search in registry. Should be assignable to user requested service type. - public readonly Type RequiredServiceType; - - /// Service key provided with registration. - public readonly object ServiceKey; - - /// Metadata key to find in metadata dictionary in resolved service. - public readonly string MetadataKey; - - /// Metadata value to find in resolved service. - public readonly object Metadata; - - /// Policy to deal with unresolved request. - public readonly IfUnresolved IfUnresolved; - - /// Indicates that the custom value is specified. - public readonly bool HasCustomValue; - - /// Either default or custom value depending on setting. - private readonly object _value; - - /// Value to use in case is set to not Throw. - public object DefaultValue => IfUnresolved != IfUnresolved.Throw ? _value : null; - - /// Custom value specified for dependency. The IfUnresolved.Throw is the marker of custom value comparing to default value. - public object CustomValue => IfUnresolved == IfUnresolved.Throw ? _value : null; - - /// Pretty prints service details to string for debugging and errors. Details string. - public override string ToString() - { - var s = new StringBuilder(); - - if (HasCustomValue) - return s.Append("{CustomValue=").Print(CustomValue ?? "null").Append("}").ToString(); - - if (RequiredServiceType != null) - s.Append("RequiredServiceType=").Print(RequiredServiceType); - if (ServiceKey != null) - (s.Length == 0 ? s.Append('{') : s.Append(", ")).Append("ServiceKey=").Print(ServiceKey); - if (MetadataKey != null || Metadata != null) - (s.Length == 0 ? s.Append('{') : s.Append(", ")).Append("Metadata=").Append(MetadataKey.Pair(Metadata)); - if (IfUnresolved != IfUnresolved.Throw) - { - s = (s.Length == 0 ? s.Append('{') : s.Append(", ")).Print(IfUnresolved); - s = _value == null ? s : s.Append(", DefaultValue=").Print(_value); - } - - return (s.Length == 0 ? s : s.Append('}')).ToString(); - } - - private ServiceDetails(Type requiredServiceType, IfUnresolved ifUnresolved, - object serviceKey, string metadataKey, object metadata, - object value, bool hasCustomValue) - { - RequiredServiceType = requiredServiceType; - IfUnresolved = ifUnresolved; - ServiceKey = serviceKey; - MetadataKey = metadataKey; - Metadata = metadata; - _value = value; - HasCustomValue = hasCustomValue; - } - } - - /// Contains tools for combining or propagating of independent of its concrete implementations. - public static class ServiceInfoTools - { - /// Creates service info with new type but keeping the details. - public static IServiceInfo With(this IServiceInfo source, Type serviceType) => - source.Create(serviceType, source.Details); - - /// Creates new info with new IfUnresolved behavior or returns the original info if behavior is not different, - /// or the passed info is not a . - public static IServiceInfo WithIfUnresolved(this IServiceInfo source, IfUnresolved ifUnresolved) - { - var details = source.Details; - if (details.IfUnresolved == ifUnresolved || details.HasCustomValue) - return source; - - if (details == ServiceDetails.Default) - details = ifUnresolved == IfUnresolved.ReturnDefault - ? ServiceDetails.IfUnresolvedReturnDefault - : ServiceDetails.IfUnresolvedReturnDefaultIfNotRegistered; - else - details = ServiceDetails.Of(details.RequiredServiceType, details.ServiceKey, - ifUnresolved, details.DefaultValue, details.MetadataKey, details.Metadata); - - return source.Create(source.ServiceType, details); - } - - // todo: Should be renamed or better to be removed, the whole operation should be hidden behind abstraction - /// Combines service info with details. The main goal is to combine service and required service type. - public static T WithDetails(this T serviceInfo, ServiceDetails details) - where T : IServiceInfo - { - details = details ?? ServiceDetails.Default; - var sourceDetails = serviceInfo.Details; - if (!details.HasCustomValue && - sourceDetails != ServiceDetails.Default && - sourceDetails != details) - { - var serviceKey = details.ServiceKey ?? sourceDetails.ServiceKey; - var metadataKey = details.MetadataKey ?? sourceDetails.MetadataKey; - var metadata = metadataKey == details.MetadataKey ? details.Metadata : sourceDetails.Metadata; - var defaultValue = details.DefaultValue ?? sourceDetails.DefaultValue; - - details = ServiceDetails.Of(details.RequiredServiceType, serviceKey, - details.IfUnresolved, defaultValue, metadataKey, metadata); - } - - var serviceType = serviceInfo.ServiceType; - var requiredServiceType = details.RequiredServiceType; - - if (requiredServiceType != null && requiredServiceType == serviceType) - details = ServiceDetails.Of(null, - details.ServiceKey, details.IfUnresolved, details.DefaultValue, - details.MetadataKey, details.Metadata); - - // if service type unchanged and details absent, or details are the same return original info, otherwise create new one - return serviceType == serviceInfo.ServiceType - && (details == null || details == serviceInfo.Details) - ? serviceInfo - : (T)serviceInfo.Create(serviceType, details); - } - - // todo: May operate directly on ServiceType and ServiceDetails instead of IServiceInfo interface - /// Enables propagation/inheritance of info between dependency and its owner: - /// for instance for wrappers. - public static IServiceInfo InheritInfoFromDependencyOwner(this IServiceInfo dependency, - IServiceInfo owner, IContainer container, FactoryType ownerType = FactoryType.Service) - { - var ownerDetails = owner.Details; - if (ownerDetails == null || ownerDetails == ServiceDetails.Default) - return dependency; - - var dependencyDetails = dependency.Details; - - var ownerIfUnresolved = ownerDetails.IfUnresolved; - var ifUnresolved = dependencyDetails.IfUnresolved; - if (ownerIfUnresolved == IfUnresolved.ReturnDefault) // ReturnDefault is always inherited - ifUnresolved = ownerIfUnresolved; - - var serviceType = dependency.ServiceType; - var requiredServiceType = dependencyDetails.RequiredServiceType; - var ownerRequiredServiceType = ownerDetails.RequiredServiceType; - - var serviceKey = dependencyDetails.ServiceKey; - var metadataKey = dependencyDetails.MetadataKey; - var metadata = dependencyDetails.Metadata; - - // Inherit some things through wrappers and decorators - if (ownerType == FactoryType.Wrapper || - ownerType == FactoryType.Decorator && - container.GetWrappedType(serviceType, requiredServiceType).IsAssignableTo(owner.ServiceType)) - { - if (ownerIfUnresolved == IfUnresolved.ReturnDefaultIfNotRegistered) - ifUnresolved = ownerIfUnresolved; - - if (serviceKey == null) - serviceKey = ownerDetails.ServiceKey; - - if (metadataKey == null && metadata == null) - { - metadataKey = ownerDetails.MetadataKey; - metadata = ownerDetails.Metadata; - } - } - - if (ownerType != FactoryType.Service && ownerRequiredServiceType != null && - requiredServiceType == null) // if only dependency does not have its own - requiredServiceType = ownerRequiredServiceType; - - if (serviceKey == dependencyDetails.ServiceKey && - metadataKey == dependencyDetails.MetadataKey && metadata == dependencyDetails.Metadata && - ifUnresolved == dependencyDetails.IfUnresolved && requiredServiceType == dependencyDetails.RequiredServiceType) - return dependency; - - if (serviceType == requiredServiceType) - requiredServiceType = null; - - var serviceDetails = ServiceDetails.Of(requiredServiceType, - serviceKey, ifUnresolved, dependencyDetails.DefaultValue, - metadataKey, metadata); - - return dependency.Create(serviceType, serviceDetails); - } - - /// Returns required service type if it is specified and assignable to service type, - /// otherwise returns service type. - public static Type GetActualServiceType(this IServiceInfo info) - { - var requiredServiceType = info.Details.RequiredServiceType; - return requiredServiceType != null && - info.ServiceType.GetTypeInfo().IsAssignableFrom(requiredServiceType.GetTypeInfo()) - ? requiredServiceType : info.ServiceType; - } - - /// Appends info string representation into provided builder. - public static StringBuilder Print(this StringBuilder s, IServiceInfo info) - { - s.Print(info.ServiceType); - var details = info.Details.ToString(); - return details == string.Empty ? s : s.Append(' ').Append(details); - } - } - - /// Represents custom or resolution root service info, there is separate representation for parameter, - /// property and field dependencies. - public class ServiceInfo : IServiceInfo - { - /// Empty service info for convenience. - public static readonly IServiceInfo Empty = new ServiceInfo(null); - - /// Creates info out of provided settings - public static ServiceInfo Of(Type serviceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null) => - Of(serviceType, null, ifUnresolved, serviceKey); - - /// Creates info out of provided settings - public static ServiceInfo Of(Type serviceType, Type requiredServiceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null, - string metadataKey = null, object metadata = null) - { - (serviceType ?? requiredServiceType).ThrowIfNull(); - - // remove unnecessary details if service and required type are the same - if (serviceType == requiredServiceType) - requiredServiceType = null; - - return serviceKey == null && requiredServiceType == null - && metadataKey == null && metadata == null - ? (ifUnresolved == IfUnresolved.Throw ? new ServiceInfo(serviceType) - : ifUnresolved == IfUnresolved.ReturnDefault ? new WithDetails(serviceType, ServiceDetails.IfUnresolvedReturnDefault) - : new WithDetails(serviceType, ServiceDetails.IfUnresolvedReturnDefaultIfNotRegistered)) - : new WithDetails(serviceType, - ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, null, metadataKey, metadata)); - } - - /// Creates service info using typed . - public static Typed Of(IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null) => - serviceKey == null && ifUnresolved == IfUnresolved.Throw - ? new Typed() - : new TypedWithDetails(ServiceDetails.Of(null, serviceKey, ifUnresolved)); - - /// Strongly-typed version of Service Info. Service type. - public class Typed : ServiceInfo - { - /// Creates service info object. - public Typed() : base(typeof(TService)) { } - } - - /// Type of service to create. Indicates registered service in registry. - public Type ServiceType { get; } - - /// Shortcut access to service key - public object ServiceKey => Details.ServiceKey; - - /// Additional settings. If not specified uses . - public virtual ServiceDetails Details => ServiceDetails.Default; - - /// Creates info from service type and details. - public IServiceInfo Create(Type serviceType, ServiceDetails details) => - details == ServiceDetails.Default ? new ServiceInfo(serviceType) : new WithDetails(serviceType, details); - - /// Prints info to string using . Printed string. - public override string ToString() => - new StringBuilder().Print(this).ToString(); - -#region Implementation - - private ServiceInfo(Type serviceType) { ServiceType = serviceType; } - - private class WithDetails : ServiceInfo - { - public override ServiceDetails Details => _details; - public WithDetails(Type serviceType, ServiceDetails details) : base(serviceType) { _details = details; } - private readonly ServiceDetails _details; - } - - private class TypedWithDetails : Typed - { - public override ServiceDetails Details => _details; - public TypedWithDetails(ServiceDetails details) { _details = details; } - private readonly ServiceDetails _details; - } - -#endregion - } - - /// Provides for parameter, - /// by default using parameter name as . - /// For parameter default setting is . - public class ParameterServiceInfo : IServiceInfo - { - /// Creates service info from parameter alone, setting service type to parameter type, - /// and setting resolution policy to if parameter is optional. - /// Parameter to create info for. - /// Parameter service info. - [MethodImpl((MethodImplOptions)256)] - public static ParameterServiceInfo Of(ParameterInfo parameter) - { - if (!parameter.IsOptional) - return new ParameterServiceInfo(parameter); - return new WithDetails(parameter, parameter.DefaultValue == null - ? ServiceDetails.IfUnresolvedReturnDefault - : ServiceDetails.Of(ifUnresolved: IfUnresolved.ReturnDefault, defaultValue: parameter.DefaultValue)); - } - - /// Service type specified by . - public virtual Type ServiceType => Parameter.ParameterType; - - /// Optional service details. - public virtual ServiceDetails Details => ServiceDetails.Default; - - /// Creates info from service type and details. - public IServiceInfo Create(Type serviceType, ServiceDetails details) => - serviceType == ServiceType ? new WithDetails(Parameter, details) : new TypeWithDetails(Parameter, serviceType, details); - - /// Parameter info. - public readonly ParameterInfo Parameter; - - /// Prints info to string using . Printed string. - public override string ToString() => - new StringBuilder().Print(this).Append(" as parameter ").Print(Parameter.Name).ToString(); - -#region Implementation - - private ParameterServiceInfo(ParameterInfo parameter) { Parameter = parameter; } - - private class WithDetails : ParameterServiceInfo - { - public override ServiceDetails Details { get { return _details; } } - public WithDetails(ParameterInfo parameter, ServiceDetails details) - : base(parameter) - { _details = details; } - private readonly ServiceDetails _details; - } - - private sealed class TypeWithDetails : WithDetails - { - public override Type ServiceType { get { return _serviceType; } } - public TypeWithDetails(ParameterInfo parameter, Type serviceType, ServiceDetails details) - : base(parameter, details) - { _serviceType = serviceType; } - private readonly Type _serviceType; - } - -#endregion - } - - /// Base class for property and field dependency info. - public abstract class PropertyOrFieldServiceInfo : IServiceInfo - { - /// Create member info out of provide property or field. - /// Member is either property or field. Created info. - public static PropertyOrFieldServiceInfo Of(MemberInfo member) => - member.ThrowIfNull() is PropertyInfo - ? (PropertyOrFieldServiceInfo)new Property((PropertyInfo)member) - : new Field((FieldInfo)member); - - /// The required service type. It will be either or . - public abstract Type ServiceType { get; } - - /// Optional details: service key, if-unresolved policy, required service type. - public virtual ServiceDetails Details => ServiceDetails.IfUnresolvedReturnDefaultIfNotRegistered; - - /// Creates info from service type and details. - /// Required service type. Optional details. Create info. - public abstract IServiceInfo Create(Type serviceType, ServiceDetails details); - - /// Either or . - public abstract MemberInfo Member { get; } - - /// Sets property or field value on provided holder object. - /// Holder of property or field. Value to set. - public abstract void SetValue(object holder, object value); - -#region Implementation - - private class Property : PropertyOrFieldServiceInfo - { - public override Type ServiceType => _property.PropertyType; - - public override IServiceInfo Create(Type serviceType, ServiceDetails details) => - serviceType == ServiceType ? new WithDetails(_property, details) : new TypeWithDetails(_property, serviceType, details); - - public override MemberInfo Member => _property; - - public override void SetValue(object holder, object value) => _property.SetValue(holder, value, null); - - public override string ToString() => - new StringBuilder().Print(this).Append(" as property ").Print(_property.Name).ToString(); - - private readonly PropertyInfo _property; - public Property(PropertyInfo property) { _property = property; } - - private class WithDetails : Property - { - public override ServiceDetails Details { get; } - - public WithDetails(PropertyInfo property, ServiceDetails details) : base(property) { Details = details; } - } - - private sealed class TypeWithDetails : WithDetails - { - public override Type ServiceType { get; } - - public TypeWithDetails(PropertyInfo property, Type serviceType, ServiceDetails details) - : base(property, details) { ServiceType = serviceType; } - } - } - - private class Field : PropertyOrFieldServiceInfo - { - public override Type ServiceType => _field.FieldType; - - public override IServiceInfo Create(Type serviceType, ServiceDetails details) => - serviceType == null ? new WithDetails(_field, details) : new TypeWithDetails(_field, serviceType, details); - - public override MemberInfo Member => _field; - - public override void SetValue(object holder, object value) => _field.SetValue(holder, value); - - public override string ToString() => - new StringBuilder().Print(this).Append(" as field ").Print(_field.Name).ToString(); - - private readonly FieldInfo _field; - public Field(FieldInfo field) { _field = field; } - - private class WithDetails : Field - { - public override ServiceDetails Details { get; } - - public WithDetails(FieldInfo field, ServiceDetails details) : base(field) { Details = details; } - } - - private sealed class TypeWithDetails : WithDetails - { - public override Type ServiceType { get; } - - public TypeWithDetails(FieldInfo field, Type serviceType, ServiceDetails details) : base(field, details) - { ServiceType = serviceType; } - } - } - -#endregion - } - - /// Stored check results of two kinds: inherited down dependency chain and not. - [Flags] - public enum RequestFlags - { - /// Not inherited - TracksTransientDisposable = 1 << 1, - - /// Inherited - IsSingletonOrDependencyOfSingleton = 1 << 3, - - /// Inherited - IsWrappedInFunc = 1 << 4, - - /// Indicates that the request is the one from Resolve call. - IsResolutionCall = 1 << 5, - - /// Non inherited - OpensResolutionScope = 1 << 6, - - /// Non inherited - StopRecursiveDependencyCheck = 1 << 7, - - /// Non inherited. Marks the expression to be added to generated resolutions to prevent infinite recursion - IsGeneratedResolutionDependencyExpression = 1 << 8, - - /// Non inherited. Indicates the root service inside the function. - IsDirectlyWrappedInFunc = 1 << 9 - } - - /// Helper extension methods to use on the bunch of factories instead of lambdas to minimize allocations - internal static class RequestTools - { - public static bool MatchFactoryConditionAndMetadata(this Request request, Factory factory) - { - if (!factory.CheckCondition(request)) - return false; - - var metadataKey = request.MetadataKey; - var metadata = request.Metadata; - return (metadataKey == null && metadata == null) || factory.Setup.MatchesMetadata(metadataKey, metadata); - } - - public static bool MatchFactoryConditionAndMetadata(this Request r, KV f) => - r.MatchFactoryConditionAndMetadata(f.Value); - - public static bool MatchFactoryReuse(this Request r, KV f) => - f.Value.Reuse?.CanApply(r) ?? true; - - public static bool MatchGeneratedFactory(this Request r, KV f) => - f.Value.FactoryGenerator == null || f.Value.FactoryGenerator.GetGeneratedFactory(r, ifErrorReturnDefault: true) != null; - } - - internal sealed class RequestStack - { - public static RequestStack Get(int index = 0) - { - var capacity = 4; - while (index >= capacity) - capacity <<= 1; - return new RequestStack(capacity); - } - - public Request[] Items; - private RequestStack(int capacity) => Items = new Request[capacity]; - - public ref Request GetOrPushRef(int index) - { - if (index < Items.Length) - return ref Items[index]; - - Items = Expand(Items, index); - return ref Items[index]; - } - - private static Request[] Expand(Request[] items, int index) - { - var count = items.Length; - var newCount = count << 1; - - // ensure that the index is always in range - while (index >= newCount) - newCount <<= 1; - - var newItems = new Request[newCount]; // count x 2 - Array.Copy(items, 0, newItems, 0, count); - return newItems; - } - } - - /// Tracks the requested service and resolved factory details in a chain of nested dependencies. - public sealed class Request : IEnumerable - { - internal static readonly RequestFlags InheritedFlags - = RequestFlags.IsSingletonOrDependencyOfSingleton - | RequestFlags.IsWrappedInFunc; - - private const RequestFlags DefaultFlags = default; - - /// Empty terminal request. - public static readonly Request Empty = - new Request(null, null, 0, null, DefaultFlags, ServiceInfo.Empty, null); - - internal static readonly Expression EmptyRequestExpr = - Field(null, typeof(Request).Field(nameof(Empty))); - - /// Empty request which opens resolution scope. - public static readonly Request EmptyOpensResolutionScope = - new Request(null, null, 0, null, DefaultFlags | RequestFlags.OpensResolutionScope | RequestFlags.IsResolutionCall, - ServiceInfo.Empty, null); - - internal static readonly Expression EmptyOpensResolutionScopeRequestExpr = - Field(null, typeof(Request).Field(nameof(EmptyOpensResolutionScope))); - - /// Creates the Resolve request. The container initiated the Resolve is stored with request. - public static Request Create(IContainer container, IServiceInfo serviceInfo, - Request preResolveParent = null, RequestFlags flags = DefaultFlags, object[] inputArgs = null) - { - var serviceType = serviceInfo.ThrowIfNull().ServiceType; - if (serviceType != null && serviceType.IsOpenGeneric()) - Throw.It(Error.ResolvingOpenGenericServiceTypeIsNotPossible, serviceType); - - flags |= RequestFlags.IsResolutionCall; - - // inherit some flags and service details from parent (if any) - preResolveParent = preResolveParent ?? Empty; - if (!preResolveParent.IsEmpty) - { - serviceInfo = serviceInfo.InheritInfoFromDependencyOwner( - preResolveParent._serviceInfo, container, preResolveParent.FactoryType); - - flags |= preResolveParent.Flags & InheritedFlags; - } - - var inputArgExprs = inputArgs?.Map(a => Constant(a)); - - var stack = RequestStack.Get(); - ref var req = ref stack.GetOrPushRef(0); - - // we are re-starting the dependency depth count from `1` - if (req == null) - req = new Request(container, preResolveParent, 1, stack, flags, serviceInfo, inputArgExprs); - else - req.SetServiceInfo(container, preResolveParent, 1, stack, flags, serviceInfo, inputArgExprs); - return req; - } - - /// Creates the Resolve request. The container initiated the Resolve is stored with request. - public static Request Create(IContainer container, Type serviceType, - object serviceKey = null, IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - Request preResolveParent = null, RequestFlags flags = DefaultFlags, object[] inputArgs = null) => - Create(container, ServiceInfo.Of(serviceType, requiredServiceType, ifUnresolved, serviceKey), - preResolveParent, flags, inputArgs); - - // todo: Make a property in v5.0 - /// Available in runtime only, provides access to container initiated the request. - public IContainer Container; - - /// Request immediate parent. - public Request DirectParent; - - internal RequestStack RequestStack; - internal int IndexInStack => DependencyDepth - 1; - - // note: mutable because of RequestFlags.AddedToResolutionExpressions - /// Persisted request conditions - public RequestFlags Flags; - - /// mutable, so that the ServiceKey or IfUnresolved can be changed in place. - internal IServiceInfo _serviceInfo; - - /// Service details part of service info - public ServiceDetails ServiceDetails => _serviceInfo.Details; - - //internal IServiceInfo _serviceDetails; // todo: use this as much as possible instead of `_serviceInfo` to avoid virtual calls - - /// Input arguments provided with `Resolve` - internal Expression[] InputArgExprs; - - /// Runtime known resolve factory, otherwise is null - internal Factory Factory; - - /// Resolved factory ID, used to identify applied decorator. - public int FactoryID { get; private set; } - - // based on FactoryID - private int _hashCode; - - /// Type of factory: Service, Wrapper, or Decorator. - public FactoryType FactoryType { get; private set; } - - /// Service implementation type if known. - public Type ImplementationType => _factoryImplType ?? Factory?.ImplementationType; - private Type _factoryImplType; - - /// Service reuse. - public IReuse Reuse { get; private set; } - - /// ID of decorated factory in case of decorator factory type - public int DecoratedFactoryID { get; private set; } - - /// Number of nested dependencies. Set with each new Push. - public int DependencyDepth; - - /// Indicates that request is empty initial request. - public bool IsEmpty => DirectParent == null; - - /// Returns true if request is First in First Resolve call. - public bool IsResolutionRoot => DirectParent != null && DirectParent.DirectParent == null; - - /// Returns true if request is First in Resolve call. - public bool IsResolutionCall => DirectParent != null && (Flags & RequestFlags.IsResolutionCall) != 0; - - /// Not the root resolution call. - public bool IsNestedResolutionCall => IsResolutionCall && DirectParent.DirectParent != null; - - /// Returns true if request is First in First Resolve call. - public bool OpensResolutionScope => DirectParent != null && (DirectParent.Flags & RequestFlags.OpensResolutionScope) != 0; - - /// Checks if the request Or its parent is wrapped in Func. - /// Use for the direct Func wrapper. - public bool IsWrappedInFunc() => (Flags & RequestFlags.IsWrappedInFunc) != 0; - - /// Checks if the request is directly wrapped in Func - public bool IsDirectlyWrappedInFunc() => (Flags & RequestFlags.IsDirectlyWrappedInFunc) != 0; - - /// Checks if request has parent with service type of Func with arguments. - public bool IsWrappedInFuncWithArgs() => InputArgExprs != null; - - /// Returns expression for func arguments. - public Expression GetInputArgsExpr() => - InputArgExprs == null ? Constant(null, typeof(object[])) - : (Expression)NewArrayInit(typeof(object), InputArgExprs.Map(x => x.Type.IsValueType() ? Convert(x, typeof(object)) : x)); - - /// Indicates that requested service is transient disposable that should be tracked. - public bool TracksTransientDisposable => (Flags & RequestFlags.TracksTransientDisposable) != 0; - - /// Indicates the request is singleton or has singleton upper in dependency chain. - public bool IsSingletonOrDependencyOfSingleton => (Flags & RequestFlags.IsSingletonOrDependencyOfSingleton) != 0; - - /// [Obsolete("Unused - hides more than abstracts")] - public bool ShouldSplitObjectGraph() => - FactoryType == FactoryType.Service && - DependencyDepth > Rules.DependencyDepthToSplitObjectGraph; - - /// Current scope - public IScope CurrentScope => Container.CurrentScope; - - /// Singletons - public IScope SingletonScope => Container.SingletonScope; - - /// Shortcut to issued container rules. - public Rules Rules => Container.Rules; - - /// (optional) Made spec used for resolving request. - public Made Made => Factory?.Made; - - /// Returns service parent skipping wrapper if any. To get direct parent use . - public Request Parent - { - get - { - var p = DirectParent; - if (p != null) - while (p.DirectParent != null && p.FactoryType == FactoryType.Wrapper) - p = p.DirectParent; - return p; - } - } - - /// Requested service type. - public Type ServiceType => _serviceInfo.ServiceType; - - /// Compatible required or service type. - public Type GetActualServiceType() => _actualServiceType; - private Type _actualServiceType; - - /// Optional service key to identify service of the same type. - public object ServiceKey => _serviceInfo.Details.ServiceKey; - - /// Metadata key to find in metadata dictionary in resolved service. - public string MetadataKey => _serviceInfo.Details.MetadataKey; - - /// Metadata or the value (if key specified) to find in resolved service. - public object Metadata => _serviceInfo.Details.Metadata; - - /// Policy to deal with unresolved service. - public IfUnresolved IfUnresolved => _serviceInfo.Details.IfUnresolved; - - /// Required service type if specified. - public Type RequiredServiceType => _serviceInfo.Details.RequiredServiceType; - - /// Relative number representing reuse lifespan. - public int ReuseLifespan => Reuse?.Lifespan ?? 0; - - /// Known implementation, or otherwise actual service type. - public Type GetKnownImplementationOrServiceType() => ImplementationType ?? GetActualServiceType(); - - /// Creates new request with provided info, and links current request as a parent. - /// Allows to set some additional flags. Existing/parent request should be resolved to - /// factory via `WithResolvedFactory` before pushing info into it. - public Request Push(IServiceInfo info, RequestFlags additionalFlags = DefaultFlags) - { - if (FactoryID == 0) - Throw.It(Error.PushingToRequestWithoutFactory, info, this); - - var flags = Flags & InheritedFlags | additionalFlags; - var serviceInfo = info.ThrowIfNull().InheritInfoFromDependencyOwner(_serviceInfo, Container, FactoryType); - - var stack = RequestStack; - var indexInStack = IndexInStack + 1; - if (stack == null) - { - stack = RequestStack.Get(indexInStack); - - // traverse all the requests up including the resolution root and set the new stack to them - Request parent = null; - do - { - parent = parent == null ? this : parent.DirectParent; - parent.RequestStack = stack; - } - while ((parent.Flags & RequestFlags.IsResolutionCall) == 0 && !parent.DirectParent.IsEmpty); - } - - ref var req = ref stack.GetOrPushRef(indexInStack); - if (req == null) - req = new Request(Container, this, DependencyDepth + 1, RequestStack, flags, serviceInfo, InputArgExprs); - else - req.SetServiceInfo(Container, this, DependencyDepth + 1, RequestStack, flags, serviceInfo, InputArgExprs); - return req; - } - - /// Composes service description into and Pushes the new request. - public Request Push(Type serviceType, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, RequestFlags flags = DefaultFlags) => - Push(ServiceInfo.Of(serviceType.ThrowIfNull().ThrowIf(serviceType.IsOpenGeneric(), Error.ResolvingOpenGenericServiceTypeIsNotPossible), - requiredServiceType, ifUnresolved, serviceKey), flags); - -#region Used in generated expression - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, int factoryID, Type implementationType, IReuse reuse) => - Push(serviceType, null, null, null, null, IfUnresolved.Throw, - factoryID, FactoryType.Service, implementationType, reuse, DefaultFlags, 0); - - internal static readonly Lazy PushMethodWith4Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), typeof(Type), typeof(int), typeof(Type), typeof(IReuse))); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, Type requiredServiceType, object serviceKey, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags) => - Push(serviceType, requiredServiceType, serviceKey, null, null, IfUnresolved.Throw, - factoryID, factoryType, implementationType, reuse, flags, 0); - - internal static readonly Lazy PushMethodWith8Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), typeof(Type), typeof(Type), typeof(object), - typeof(int), typeof(FactoryType), typeof(Type), typeof(IReuse), typeof(RequestFlags))); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, Type requiredServiceType, object serviceKey, IfUnresolved ifUnresolved, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags, - int decoratedFactoryID) => - Push(serviceType, requiredServiceType, serviceKey, null, null, ifUnresolved, - factoryID, factoryType, implementationType, reuse, flags, decoratedFactoryID); - - internal static readonly Lazy PushMethodWith10Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), - typeof(Type), typeof(Type), typeof(object), typeof(IfUnresolved), - typeof(int), typeof(FactoryType), typeof(Type), typeof(IReuse), typeof(RequestFlags), typeof(int))); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push( - Type serviceType, Type requiredServiceType, object serviceKey, string metadataKey, object metadata, IfUnresolved ifUnresolved, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags, int decoratedFactoryID) - { - return new Request(Container, this, DependencyDepth + 1, null, flags, - ServiceInfo.Of(serviceType, requiredServiceType, ifUnresolved, serviceKey, metadataKey, metadata), - InputArgExprs, implementationType, null, // factory cannot be supplied in generated code - factoryID, factoryType, reuse, decoratedFactoryID); - } - - internal static readonly Lazy PushMethodWith12Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), - typeof(Type), typeof(Type), typeof(object), typeof(string), typeof(object), typeof(IfUnresolved), - typeof(int), typeof(FactoryType), typeof(Type), typeof(IReuse), typeof(RequestFlags), typeof(int))); - -#endregion - - /// Allow to switch current service info to the new one, e.g. in decorators. - /// If info did not change then return the same this request. - public Request WithChangedServiceInfo(Func getInfo) - { - var newServiceInfo = getInfo(_serviceInfo); - return newServiceInfo == _serviceInfo ? this - : new Request(Container, - DirectParent, DependencyDepth, RequestStack, Flags, newServiceInfo, InputArgExprs, - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - } - - /// Produces the new request with the changed `ifUnresolved` or returns original request otherwise - public Request WithIfUnresolved(IfUnresolved ifUnresolved) => - IfUnresolved == ifUnresolved ? this - : new Request(Container, - DirectParent, DependencyDepth, RequestStack, Flags, - _serviceInfo.WithIfUnresolved(ifUnresolved), InputArgExprs, - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - - // todo: in place mutation? - /// Updates the flags - public Request WithFlags(RequestFlags newFlags) => - new Request(Container, - DirectParent, DependencyDepth, RequestStack, newFlags, _serviceInfo, InputArgExprs, - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - - // note: Mutates the request, required for proper caching - /// Sets service key to passed value. Required for multiple default services to change null key to - /// actual - public void ChangeServiceKey(object serviceKey) - { - var info = _serviceInfo; - var details = info.Details; - _serviceInfo = info.Create(info.ServiceType, - ServiceDetails.Of(details.RequiredServiceType, serviceKey, details.IfUnresolved, details.DefaultValue)); - } - - /// Prepends input arguments to existing arguments in request. It is done because the - /// nested Func/Action input argument has a priority over outer argument. - /// The arguments are provided by Func and Action wrappers, or by `args` parameter in Resolve call. - public Request WithInputArgs(Expression[] inputArgs) => - new Request(Container, - DirectParent, DependencyDepth, RequestStack, Flags, _serviceInfo, inputArgs.Append(InputArgExprs), - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - - /// Returns new request with set implementation details. - /// Factory to which request is resolved. - /// (optional) does not check for recursive dependency. - /// Use with caution. Make sense for Resolution expression. - /// (optional) allows to skip reuse mismatch aka captive dependency check. - /// Make a defensive copy of request. - /// New request with set factory. - public Request WithResolvedFactory(Factory factory, - bool skipRecursiveDependencyCheck = false, bool skipCaptiveDependencyCheck = false, bool copyRequest = false) - { - var factoryId = factory.FactoryID; - var factoryType = factory.FactoryType; - var decoratedFactoryID = 0; - if (Factory != null) // resolving the factory for the second time, usually happens in decorators - { - if (Factory.FactoryID == factoryId) - return this; // stop resolving to the same factory twice - if (factoryType == FactoryType.Decorator && Factory.FactoryType != FactoryType.Decorator) - decoratedFactoryID = FactoryID; - } - - // It is required to nullify the transient disposable tracking when factory is resolved multiple times, - // e.g. for the decorator - var flags = Flags & ~RequestFlags.TracksTransientDisposable; - if (skipRecursiveDependencyCheck) - flags |= RequestFlags.StopRecursiveDependencyCheck; - - var reuse = IsWrappedInFuncWithArgs() && Rules.IgnoringReuseForFuncWithArgs - ? DryIoc.Reuse.Transient - : factory.Reuse ?? CalculateDefaultReuse(factory); - - var reuseLifespan = reuse.Lifespan; - skipCaptiveDependencyCheck = skipCaptiveDependencyCheck - || reuseLifespan == 0 || !Rules.ThrowIfDependencyHasShorterReuseLifespan || factory.Setup.OpenResolutionScope; - - skipRecursiveDependencyCheck = skipRecursiveDependencyCheck - || factoryType != FactoryType.Service; - - // For the dependency we nned to check the recursive and the captive dependency - for (var parent = DirectParent; - !parent.IsEmpty && (!skipCaptiveDependencyCheck || !skipRecursiveDependencyCheck); - parent = parent.DirectParent) - { - if (!skipCaptiveDependencyCheck) - { - if (parent.OpensResolutionScope || - // ignores the ScopedOrSingleton inside Scoped or Singleton - (reuse as CurrentScopeReuse)?.ScopedOrSingleton == true && parent.Reuse is SingletonReuse || - parent.FactoryType == FactoryType.Wrapper && parent.GetActualServiceType().IsFunc()) - skipCaptiveDependencyCheck = true; - else if ( - parent.FactoryType == FactoryType.Service && - parent.ReuseLifespan > reuseLifespan) - Throw.It(Error.DependencyHasShorterReuseLifespan, PrintCurrent(), reuse, parent); - } - - if (!skipRecursiveDependencyCheck) - { - if ((parent.Flags & RequestFlags.StopRecursiveDependencyCheck) != 0) - skipRecursiveDependencyCheck = true; // stops further upward checking - else if (parent.FactoryID == factoryId) - Throw.It(Error.RecursiveDependencyDetected, Print(factoryId)); - } - } - - if (reuse == DryIoc.Reuse.Singleton) - { - flags |= RequestFlags.IsSingletonOrDependencyOfSingleton; - } - else if (reuse == DryIoc.Reuse.Transient) // check for disposable transient - { - reuse = GetTransientDisposableTrackingReuse(factory); - if (reuse != DryIoc.Reuse.Transient) - flags |= RequestFlags.TracksTransientDisposable; - } - - if (copyRequest) - { - IsolateRequestChain(); - return new Request(Container, DirectParent, DependencyDepth, null, flags, _serviceInfo, - InputArgExprs, null, factory, factoryId, factoryType, reuse, decoratedFactoryID); - } - - Flags = flags; - SetResolvedFactory(null, factory, factoryId, factoryType, reuse, decoratedFactoryID); - return this; - } - - /// Check for the parents. - public bool HasRecursiveParent(int factoryID) - { - for (var parent = DirectParent; !parent.IsEmpty; parent = parent.DirectParent) - { - if ((parent.Flags & RequestFlags.StopRecursiveDependencyCheck) != 0) - break; // stops further upward checking - if (parent.FactoryID == factoryID) - return true; - } - return false; - } - - private IReuse CalculateDefaultReuse(Factory factory) - { - if (factory.Setup.UseParentReuse) - return GetFirstParentNonTransientReuseUntilFunc(); - - if (factory.FactoryType == FactoryType.Decorator) - { - if (factory.Setup.To().UseDecorateeReuse || - Rules.UseDecorateeReuseForDecorators) - return Reuse; // use reuse of resolved service factory for decorator - } - - return factory.FactoryType == FactoryType.Wrapper - ? DryIoc.Reuse.Transient : Rules.DefaultReuse; - } - - private IReuse GetTransientDisposableTrackingReuse(Factory factory) - { - // Track transient disposable in parent scope (if any), or open scope (if any) - var setup = factory.Setup; - var tracksTransientDisposable = - !setup.PreventDisposal && - (setup.TrackDisposableTransient || !setup.AllowDisposableTransient && Rules.TrackingDisposableTransients) && - (factory.ImplementationType ?? GetActualServiceType()).IsAssignableTo(); - - if (!tracksTransientDisposable) - return DryIoc.Reuse.Transient; - - var parentReuse = GetFirstParentNonTransientReuseUntilFunc(); - if (parentReuse != DryIoc.Reuse.Transient) - return parentReuse; - - if (IsWrappedInFunc()) - return DryIoc.Reuse.Transient; - - // If no parent with reuse found, then track in current open scope or in singletons scope - return DryIoc.Reuse.ScopedOrSingleton; - } - - private IReuse GetFirstParentNonTransientReuseUntilFunc() - { - for (var parent = DirectParent; parent.DirectParent != null; parent = parent.DirectParent) - { - if (parent.FactoryType == FactoryType.Wrapper && parent.GetActualServiceType().IsFunc()) - break; - if (parent.FactoryType != FactoryType.Wrapper && parent.Reuse != DryIoc.Reuse.Transient) - return parent.Reuse; - } - - return DryIoc.Reuse.Transient; - } - - /// If request corresponds to dependency injected into parameter, - /// then method calls handling and returns its result. - /// If request corresponds to property or field, then method calls respective handler. - /// If request does not correspond to dependency, then calls handler. - public TResult Is( - Func root = null, - Func parameter = null, - Func property = null, - Func field = null) - { - var info = _serviceInfo; - if (info is ParameterServiceInfo) - { - if (parameter != null) - return parameter(((ParameterServiceInfo)info).Parameter); - } - else if (info is PropertyOrFieldServiceInfo) - { - var propertyOrFieldServiceInfo = (PropertyOrFieldServiceInfo)info; - var propertyInfo = propertyOrFieldServiceInfo.Member as PropertyInfo; - if (propertyInfo != null) - { - if (property != null) - return property(propertyInfo); - } - else if (field != null) - return field((FieldInfo)propertyOrFieldServiceInfo.Member); - } - else if (root != null) - return root(); - - return default(TResult); - } - - /// Obsolete: now request is directly implements the . - public IEnumerable Enumerate() => this; - - /// Enumerates self and all request stack parents. - public IEnumerator GetEnumerator() - { - for (var r = this; !r.IsEmpty; r = r.DirectParent) - yield return r; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// Prints current request info only (no parents printed) to provided builder. - public StringBuilder PrintCurrent(StringBuilder s = null) - { - s = s ?? new StringBuilder(); - - if (IsEmpty) - return s.Append(""); - - if (IsNestedResolutionCall) - s.Append("Resolution call dependency "); - else if (IsResolutionRoot) - s.Append("Resolution root "); - - if (FactoryID != 0) // request is with resolved factory - { - if (Reuse != DryIoc.Reuse.Transient) - s.Append(Reuse is SingletonReuse ? "singleton" : "scoped").Append(' '); - - if (FactoryType != FactoryType.Service) - s.Append(FactoryType.ToString().ToLower()).Append(' '); - - var implType = ImplementationType; - if (implType != null && implType != ServiceType) - s.Print(implType).Append(": "); - } - - s.Append(_serviceInfo); - - if (Factory != null && Factory is ReflectionFactory == false) - s.Append(' ').Append(Factory.GetType().Name).Append(' '); - - if (FactoryID != 0) - s.Append(" FactoryId=").Append(FactoryID); - - if (DecoratedFactoryID != 0) - s.Append(" decorating FactoryId=").Append(DecoratedFactoryID); - - if (!InputArgExprs.IsNullOrEmpty()) - s.AppendFormat(" with passed arguments [{0}]", InputArgExprs); - - // todo: exclude IsResolutionCall cause it is printed by above - if (Flags != default(RequestFlags)) - s.Append(" (").Append(Flags).Append(')'); - - return s; - } - - /// Prints full stack of requests starting from current one using . - public StringBuilder Print(int recursiveFactoryID = 0) - { - if (IsEmpty) - return new StringBuilder(""); - - var s = PrintCurrent(new StringBuilder()); - - s = recursiveFactoryID == 0 ? s : s.Append(" <--recursive"); - foreach (var parent in DirectParent) - { - s = parent.PrintCurrent(s.AppendLine().Append(" in ")); - if (parent.FactoryID == recursiveFactoryID) - s = s.Append(" <--recursive"); - } - - if (Container != null) - s.AppendLine().Append(" from ").Append(Container); - - return s; - } - - /// Prints whole request chain. - public override string ToString() => Print().ToString(); - - /// Returns true if request info and passed object are equal, and their parents recursively are equal. - public override bool Equals(object obj) => Equals(obj as Request); - - /// Returns true if request info and passed info are equal, and their parents recursively are equal. - public bool Equals(Request other) => - other != null && EqualsWithoutParent(other) - && (DirectParent == null && other.DirectParent == null - || (DirectParent != null && DirectParent.EqualsWithoutParent(other.DirectParent))); - - // todo: Should we include InputArgs and DecoratedFactoryID and what about flags? - // todo: Should we add and rely on Equals of ServiceInfo and Reuse? - // todo: The equals calculated differently comparing to HashCode, may be we can use FactoryID for Equals as well? - /// Compares self properties but not the parents. - public bool EqualsWithoutParent(Request other) => - other.ServiceType == ServiceType - && other.RequiredServiceType == RequiredServiceType - && other.IfUnresolved == IfUnresolved - - && other.FactoryType == FactoryType - && other.ImplementationType == ImplementationType - - && Equals(other.ServiceKey, ServiceKey) - && Equals(other.MetadataKey, MetadataKey) - && Equals(other.Metadata, Metadata) - - // todo: Move to Reuse? - && other.Reuse?.GetType() == Reuse?.GetType() - && other.Reuse?.Lifespan == Reuse?.Lifespan - && Equals(other.Reuse?.Name, Reuse?.Name); - - /// Calculates the combined hash code based on factory IDs. - public override int GetHashCode() => _hashCode; - - // Initial request without factory info yet - private Request(IContainer container, Request parent, - int dependencyDepth, RequestStack stack, RequestFlags flags, IServiceInfo serviceInfo, Expression[] inputArgExprs) - { - DirectParent = parent; - DependencyDepth = dependencyDepth; - RequestStack = stack; - - _serviceInfo = serviceInfo; - _actualServiceType = serviceInfo.GetActualServiceType(); - - Flags = flags; - - // runtime state - InputArgExprs = inputArgExprs; - Container = container; - } - - // Request with resolved factory state - private Request(IContainer container, - Request parent, int dependencyDepth, RequestStack stack, - RequestFlags flags, IServiceInfo serviceInfo, Expression[] inputArgExprs, - Type factoryImplType, Factory factory, int factoryID, FactoryType factoryType, IReuse reuse, int decoratedFactoryID) - : this(container, parent, dependencyDepth, stack, flags, serviceInfo, inputArgExprs) - { - SetResolvedFactory(factoryImplType, factory , factoryID, factoryType, reuse, decoratedFactoryID); - } - - /// Severe the connection with the request pool up to the parent so that noone can change the Request state - internal Request IsolateRequestChain() - { - Request r = null; - do - { - r = r == null ? this : r.DirectParent; - if (r.RequestStack != null) - { - // severe the requests links with the stack - r.RequestStack.Items[r.IndexInStack] = null; - r.RequestStack = null; - } - - } while ((r.Flags & RequestFlags.IsResolutionCall) == 0 && !r.DirectParent.IsEmpty); - - return this; - } - - private void SetServiceInfo(IContainer container, Request parent, - int dependencyDepth, RequestStack stack, RequestFlags flags, IServiceInfo serviceInfo, Expression[] inputArgExprs) - { - DirectParent = parent; - DependencyDepth = dependencyDepth; - RequestStack = stack; - - _serviceInfo = serviceInfo; - _actualServiceType = serviceInfo.GetActualServiceType(); - - Flags = flags; - - // runtime state - InputArgExprs = inputArgExprs; - Container = container; - - // reset factory info - SetResolvedFactory(null, null, 0, FactoryType.Service, null, 0); - } - - private void SetResolvedFactory(Type factoryImplType, - Factory factory, int factoryID, FactoryType factoryType, IReuse reuse, int decoratedFactoryID) - { - FactoryID = factoryID; - FactoryType = factoryType; - Reuse = reuse; - DecoratedFactoryID = decoratedFactoryID; - _hashCode = Hasher.Combine(DirectParent?._hashCode ?? 0, FactoryID); - - _factoryImplType = factoryImplType; // should be set from the runtime known `Factory` object - Factory = factory; // runtime state - } - } - - /// Type of services supported by Container. - public enum FactoryType - { - /// (default) Defines normal service factory - Service, - /// Defines decorator factory - Decorator, - /// Defines wrapper factory. - Wrapper - }; - - /// Base class to store optional settings. - public abstract class Setup - { - /// Factory type is required to be specified by concrete setups as in - /// , , . - public abstract FactoryType FactoryType { get; } - - /// Predicate to check if factory could be used for resolved request. - public Func Condition { get; } - - /// Relative disposal order when defined. Greater number, later dispose. - public int DisposalOrder { get; } - - /// Arbitrary metadata object associated with Factory/Implementation, may be a dictionary of key-values. - public virtual object Metadata => null; - - /// Returns true if passed meta key and value match the setup metadata. - public bool MatchesMetadata(string metadataKey, object metadata) - { - if (metadataKey == null) - return Equals(metadata, Metadata); - - object metaValue; - var metaDict = Metadata as IDictionary; - return metaDict != null - && metaDict.TryGetValue(metadataKey, out metaValue) - && Equals(metadata, metaValue); - } - - /// Indicates that injected expression should be: - /// (...)]]> - /// instead of: - public bool AsResolutionCall => (_settings & Settings.AsResolutionCall) != 0; - - /// Setup with the only setting: `AsResolutionCall` - internal static readonly Setup AsResolutionCallSetup = - new ServiceSetup { _settings = Settings.AsResolutionCall }; - - internal Setup WithAsResolutionCall() - { - if (AsResolutionCall) - return this; - - if (this == Default) - return AsResolutionCallSetup; - - var setupClone = (Setup)MemberwiseClone(); - setupClone._settings |= Settings.AsResolutionCall; - return setupClone; - } - - /// Works as `AsResolutionCall` but only with `Rules.UsedForExpressionGeneration` - public bool AsResolutionCallForExpressionGeneration => (_settings & Settings.AsResolutionCallForExpressionGeneration) != 0; - - /// Specifies to use `asResolutionCall` but only in expression generation context, e.g. for compile-time generation - internal static readonly Setup AsResolutionCallForGeneratedExpressionSetup = - new ServiceSetup { _settings = Settings.AsResolutionCallForExpressionGeneration }; - - internal Setup WithAsResolutionCallForGeneratedExpression() - { - if (AsResolutionCallForExpressionGeneration) - return this; - - if (this == Default) - return AsResolutionCallForGeneratedExpressionSetup; - - var setupClone = (Setup)MemberwiseClone(); - setupClone._settings |= Settings.AsResolutionCallForExpressionGeneration; - return setupClone; - } - - /// Marks service (not a wrapper or decorator) registration that is expected to be resolved via Resolve call. - public bool AsResolutionRoot => (_settings & Settings.AsResolutionRoot) != 0; - - /// Opens scope, also implies . - public bool OpenResolutionScope => (_settings & Settings.OpenResolutionScope) != 0; - - /// Stores reused instance as WeakReference. - public bool WeaklyReferenced => (_settings & Settings.WeaklyReferenced) != 0; - - /// Allows registering transient disposable. - public bool AllowDisposableTransient => (_settings & Settings.AllowDisposableTransient) != 0; - - /// Turns On tracking of disposable transient dependency in parent scope or in open scope if resolved directly. - public bool TrackDisposableTransient => (_settings & Settings.TrackDisposableTransient) != 0; - - /// Instructs to use parent reuse. Applied only if is not specified. - public bool UseParentReuse => (_settings & Settings.UseParentReuse) != 0; - - /// Prevents disposal of reused instance if it is disposable. - public bool PreventDisposal => (_settings & Settings.PreventDisposal) != 0; - - /// When single service is resolved, but multiple candidates found, this options will be used to prefer this one. - public bool PreferInSingleServiceResolve => (_settings & Settings.PreferInSingleServiceResolve) != 0; - - private Setup() { } - - private Setup(Func condition, - bool openResolutionScope, bool asResolutionCall, bool asResolutionRoot, bool preventDisposal, bool weaklyReferenced, - bool allowDisposableTransient, bool trackDisposableTransient, bool useParentReuse, int disposalOrder, - bool preferOverMultipleResolved = false, bool asResolutionCallForExpressionGeneration = false) - { - Condition = condition; - DisposalOrder = disposalOrder; - - if (asResolutionCall) - _settings |= Settings.AsResolutionCall; - if (openResolutionScope) - { - _settings |= Settings.OpenResolutionScope; - _settings |= Settings.AsResolutionCall; - } - if (preventDisposal) - _settings |= Settings.PreventDisposal; - if (weaklyReferenced) - _settings |= Settings.WeaklyReferenced; - if (allowDisposableTransient) - _settings |= Settings.AllowDisposableTransient; - if (trackDisposableTransient) - { - _settings |= Settings.TrackDisposableTransient; - _settings |= Settings.AllowDisposableTransient; - } - if (asResolutionRoot) - _settings |= Settings.AsResolutionRoot; - if (useParentReuse) - _settings |= Settings.UseParentReuse; - if (preferOverMultipleResolved) - _settings |= Settings.PreferInSingleServiceResolve; - if (asResolutionCallForExpressionGeneration) - _settings |= Settings.AsResolutionCallForExpressionGeneration; - } - - [Flags] - private enum Settings - { - AsResolutionCall = 1 << 1, - OpenResolutionScope = 1 << 2, - PreventDisposal = 1 << 3, - WeaklyReferenced = 1 << 4, - AllowDisposableTransient = 1 << 5, - TrackDisposableTransient = 1 << 6, - AsResolutionRoot = 1 << 7, - UseParentReuse = 1 << 8, - PreferInSingleServiceResolve = 1 << 9, - AsResolutionCallForExpressionGeneration = 1 << 10 - } - - private Settings _settings; // note: mutable because of setting the AsResolutionCall - - /// Default setup for service factories. - public static readonly Setup Default = new ServiceSetup(); - - /// Constructs setup object out of specified settings. - /// If all settings are default then setup will be returned. - /// is metadata object or Func returning metadata object. - public static Setup With( - object metadataOrFuncOfMetadata = null, Func condition = null, - bool openResolutionScope = false, bool asResolutionCall = false, bool asResolutionRoot = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - bool useParentReuse = false, int disposalOrder = 0, bool preferInSingleServiceResolve = false) - { - if (metadataOrFuncOfMetadata == null && condition == null && - !openResolutionScope && !asResolutionRoot && - !preventDisposal && !weaklyReferenced && !allowDisposableTransient && !trackDisposableTransient && - !useParentReuse && disposalOrder == 0 && !preferInSingleServiceResolve) - return !asResolutionCall ? Default : AsResolutionCallSetup; - - return new ServiceSetup(condition, - metadataOrFuncOfMetadata, openResolutionScope, asResolutionCall, asResolutionRoot, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, - useParentReuse, disposalOrder, preferInSingleServiceResolve); - } - - /// Default setup which will look for wrapped service type as single generic parameter. - public static readonly Setup Wrapper = new WrapperSetup(); - - // todo: rename to WrapperOf - /// Returns generic wrapper setup. - /// Default for is -1 for generic wrapper with single type argument. - /// Index need to be set for multiple type arguments. need to be set - /// when generic wrapper type arguments should be ignored. - public static Setup WrapperWith(int wrappedServiceTypeArgIndex = -1, - bool alwaysWrapsRequiredServiceType = false, Func unwrap = null, - bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - bool useParentReuse = false, Func condition = null, int disposalOrder = 0) => - wrappedServiceTypeArgIndex == -1 && !alwaysWrapsRequiredServiceType && unwrap == null && - !openResolutionScope && !asResolutionCall && !preventDisposal && !weaklyReferenced && - !allowDisposableTransient && !trackDisposableTransient && condition == null && disposalOrder == 0 - ? Wrapper - : new WrapperSetup(wrappedServiceTypeArgIndex, alwaysWrapsRequiredServiceType, unwrap, - condition, openResolutionScope, asResolutionCall, preventDisposal, weaklyReferenced, - allowDisposableTransient, trackDisposableTransient, useParentReuse, disposalOrder); - - /// Default decorator setup: decorator is applied to service type it registered with. - public static readonly Setup Decorator = new DecoratorSetup(); - - // todo: Make decorateeReuse a default? - /// Creates setup with optional condition. - /// The specifies relative decorator position in decorators chain. - /// Greater number means further from decoratee - specify negative number to stay closer. - /// Decorators without order (Order is 0) or with equal order are applied in registration order - /// - first registered are closer decoratee. - public static Setup DecoratorWith( - Func condition = null, int order = 0, bool useDecorateeReuse = false, - bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - int disposalOrder = 0) => - condition == null && order == 0 && !useDecorateeReuse && - !openResolutionScope && !asResolutionCall && - !preventDisposal && !weaklyReferenced && !allowDisposableTransient && !trackDisposableTransient && - disposalOrder == 0 - ? Decorator - : new DecoratorSetup(condition, order, useDecorateeReuse, openResolutionScope, asResolutionCall, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, disposalOrder); - - /// Creates a condition for both , and additional condition - public static Func GetDecorateeCondition(Type decorateeType, - object decorateeServiceKey = null, Func condition = null) - { - if (decorateeType == null && decorateeServiceKey == null) - return condition; - - Func decorateeCondition; - if (decorateeServiceKey == null) - decorateeCondition = r => r.GetKnownImplementationOrServiceType().IsAssignableTo(decorateeType); - else if (decorateeType == null) - decorateeCondition = r => decorateeServiceKey.Equals(r.ServiceKey); - else - decorateeCondition = r => decorateeServiceKey.Equals(r.ServiceKey) && - r.GetKnownImplementationOrServiceType().IsAssignableTo(decorateeType); - return condition == null ? decorateeCondition : r => decorateeCondition(r) && condition(r); - } - - /// Setup for decorator of type . - public static Setup DecoratorOf(Type decorateeType = null, - int order = 0, bool useDecorateeReuse = false, bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, bool allowDisposableTransient = false, - bool trackDisposableTransient = false, int disposalOrder = 0, object decorateeServiceKey = null) => - DecoratorWith(GetDecorateeCondition(decorateeType, decorateeServiceKey), order, useDecorateeReuse, openResolutionScope, asResolutionCall, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, disposalOrder); - - /// Setup for decorator of type . - public static Setup DecoratorOf( - int order = 0, bool useDecorateeReuse = false, bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, bool allowDisposableTransient = false, - bool trackDisposableTransient = false, int disposalOrder = 0, object decorateeServiceKey = null) => - DecoratorOf(typeof(TDecoratee), order, useDecorateeReuse, openResolutionScope, asResolutionCall, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, - disposalOrder, decorateeServiceKey); - - /// Service setup. - internal sealed class ServiceSetup : Setup - { - /// - public override FactoryType FactoryType => FactoryType.Service; - - /// Evaluates metadata if it specified as Func of object, and replaces Func with its result!. - /// Otherwise just returns metadata object. - /// Invocation of Func metadata is Not thread-safe. Please take care of that inside the Func. - public override object Metadata => - _metadataOrFuncOfMetadata is Func metaFactory - ? (_metadataOrFuncOfMetadata = metaFactory()) - : _metadataOrFuncOfMetadata; - - /// All settings are set to defaults. - public ServiceSetup() { } - - /// Specify all the individual settings. - public ServiceSetup(Func condition = null, object metadataOrFuncOfMetadata = null, - bool openResolutionScope = false, bool asResolutionCall = false, bool asResolutionRoot = false, - bool preventDisposal = false, bool weaklyReferenced = false, bool allowDisposableTransient = false, - bool trackDisposableTransient = false, bool useParentReuse = false, int disposalOrder = 0, - bool preferOverMultipleResolved = false, bool asResolutionCallForExpressionGeneration = false) - : base(condition, openResolutionScope, asResolutionCall, asResolutionRoot, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, - useParentReuse, disposalOrder, preferOverMultipleResolved, asResolutionCallForExpressionGeneration) - { - _metadataOrFuncOfMetadata = metadataOrFuncOfMetadata; - } - - private object _metadataOrFuncOfMetadata; - } - - /// Setup applied for wrappers. - internal sealed class WrapperSetup : Setup - { - /// Returns type. - public override FactoryType FactoryType => FactoryType.Wrapper; - - /// Delegate to get wrapped type from provided wrapper type. - /// If wrapper is generic, then wrapped type is usually a generic parameter. - public readonly int WrappedServiceTypeArgIndex; - - /// Per name. - public readonly bool AlwaysWrapsRequiredServiceType; - - /// Delegate returning wrapped type from wrapper type. Overwrites other options. - public readonly Func Unwrap; - - /// Default setup - /// Default is -1 for generic wrapper with single type argument. - /// Need to be set for multiple type arguments. - public WrapperSetup(int wrappedServiceTypeArgIndex = -1) - { - WrappedServiceTypeArgIndex = wrappedServiceTypeArgIndex; - } - - /// Returns generic wrapper setup. - /// Default for is -1 for generic wrapper with single type argument. - /// Index need to be set for multiple type arguments. need to be set - /// when generic wrapper type arguments should be ignored. - public WrapperSetup(int wrappedServiceTypeArgIndex, bool alwaysWrapsRequiredServiceType, Func unwrap, - Func condition, - bool openResolutionScope, bool asResolutionCall, - bool preventDisposal, bool weaklyReferenced, bool allowDisposableTransient, bool trackDisposableTransient, - bool useParentReuse, int disposalOrder) - : base(condition, openResolutionScope, asResolutionCall, false, preventDisposal, weaklyReferenced, - allowDisposableTransient, trackDisposableTransient, useParentReuse, disposalOrder) - { - WrappedServiceTypeArgIndex = wrappedServiceTypeArgIndex; - AlwaysWrapsRequiredServiceType = alwaysWrapsRequiredServiceType; - Unwrap = unwrap; - } - - internal void ThrowIfInvalidRegistration(Type serviceType) - { - if (AlwaysWrapsRequiredServiceType || Unwrap != null || !serviceType.IsGeneric()) - return; - - var typeArgCount = serviceType.GetGenericParamsAndArgs().Length; - var typeArgIndex = WrappedServiceTypeArgIndex; - Throw.If(typeArgCount > 1 && typeArgIndex == -1, - Error.GenericWrapperWithMultipleTypeArgsShouldSpecifyArgIndex, serviceType); - - var index = typeArgIndex != -1 ? typeArgIndex : 0; - Throw.If(index > typeArgCount - 1, - Error.GenericWrapperTypeArgIndexOutOfBounds, serviceType, index); - } - - /// Unwraps service type or returns the as-is. - public Type GetWrappedTypeOrNullIfWrapsRequired(Type serviceType) - { - if (Unwrap != null) - return Unwrap(serviceType); - - if (AlwaysWrapsRequiredServiceType || !serviceType.IsGeneric()) - return null; - - var typeArgs = serviceType.GetGenericParamsAndArgs(); - var typeArgIndex = WrappedServiceTypeArgIndex; - serviceType.ThrowIf(typeArgs.Length > 1 && typeArgIndex == -1, - Error.GenericWrapperWithMultipleTypeArgsShouldSpecifyArgIndex); - - typeArgIndex = typeArgIndex != -1 ? typeArgIndex : 0; - serviceType.ThrowIf(typeArgIndex > typeArgs.Length - 1, - Error.GenericWrapperTypeArgIndexOutOfBounds, typeArgIndex); - - return typeArgs[typeArgIndex]; - } - } - - /// Setup applied to decorators. - internal sealed class DecoratorSetup : Setup - { - /// Returns Decorator factory type. - public override FactoryType FactoryType => FactoryType.Decorator; - - /// If provided specifies relative decorator position in decorators chain. - /// Greater number means further from decoratee - specify negative number to stay closer. - /// Decorators without order (Order is 0) or with equal order are applied in registration order - /// - first registered are closer decoratee. - public readonly int Order; - - // todo: It does not consider the keys of decorated services, - // therefore it will be shared between all services in collection - /// Instructs to use decorated service reuse. Decorated service may be decorator itself. - public readonly bool UseDecorateeReuse; - - /// Default setup. - public DecoratorSetup() { } - - /// Creates decorator setup with optional condition. applied to - /// decorated service to find that service is the decorator target. specifies - /// relative decorator position in decorators chain. Greater number means further from decoratee - - /// specify negative number to stay closer. Decorators without order (Order is 0) or with equal order - /// are applied in registration order - first registered are closer decoratee. - public DecoratorSetup(Func condition, int order, bool useDecorateeReuse, - bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - int disposalOrder = 0) - : base(condition, openResolutionScope, asResolutionCall, false, preventDisposal, weaklyReferenced, - allowDisposableTransient, trackDisposableTransient, false, disposalOrder) - { - Order = order; - UseDecorateeReuse = useDecorateeReuse; - } - } - } - - /// Facility for creating concrete factories from some template/prototype. Example: - /// creating closed-generic type reflection factory from registered open-generic prototype factory. - public interface IConcreteFactoryGenerator - { - /// Generated factories so far, identified by the service type and key pair. - ImHashMap, ReflectionFactory> GeneratedFactories { get; } - - /// Returns factory per request. May track already generated factories and return one without regenerating. - Factory GetGeneratedFactory(Request request, bool ifErrorReturnDefault = false); - } - - /// Instructs how to deal with factory result expression: - public enum FactoryCaching - { /// Is up to DryIoc to decide, - Default = 0, - /// Prevents DryIoc to set `DoNotCache`. - PleaseDontSetDoNotCache, - /// If set, the expression won't be cached - DoNotCache - } - - /// Base class for different ways to instantiate service: - /// - /// Through reflection - - /// Using custom delegate - - /// Using custom expression - - /// A placeholder for future actual implementation - - /// - /// For all of the types Factory should provide result as and . - /// Factories are supposed to be immutable and stateless. - /// Each created factory has an unique ID set in . - public abstract class Factory - { - /// Get next factory ID in a atomic way.The ID. - public static int GetNextID() => Interlocked.Increment(ref _lastFactoryID); - - /// Unique factory id generated from static seed. - public int FactoryID { get; internal set; } - - /// Reuse policy for created services. - public virtual IReuse Reuse => _reuse; - - /// Setup may contain different/non-default factory settings. - public virtual Setup Setup - { - get => _setup; - internal set { _setup = value ?? Setup.Default; } - } - - /// Checks that condition is met for request or there is no condition setup. - public bool CheckCondition(Request request) => Setup.Condition?.Invoke(request) != false; - - /// Shortcut for . - public FactoryType FactoryType => Setup.FactoryType; - - /// Non-abstract closed implementation type. May be null if not known beforehand, e.g. in . - public virtual Type ImplementationType => null; - - /// Allow inheritors to define lazy implementation type - public virtual bool CanAccessImplementationType => true; - - /// Indicates that Factory is factory provider and - /// consumer should call to get concrete factory. - public virtual IConcreteFactoryGenerator FactoryGenerator => null; - - /// Registration order. - public virtual int RegistrationOrder => FactoryID; - - /// Settings (if any) to select Constructor/FactoryMethod, Parameters, Properties and Fields. - public virtual Made Made => Made.Default; - - /// The factory inserts the runtime-state into result expression, e.g. delegate or pre-created instance. - public virtual bool HasRuntimeState => false; - - /// Indicates how to deal with the result expression - public FactoryCaching Caching { get; set; } - - /// Instructs to skip caching the factory unless it really wants to do so via `PleaseDontSetDoNotCache` - public Factory DoNotCache() - { - if (Caching != FactoryCaching.PleaseDontSetDoNotCache) - Caching = FactoryCaching.DoNotCache; - return this; - } - - /// Initializes reuse and setup. Sets the - /// (optional) (optional) - protected Factory(IReuse reuse = null, Setup setup = null) - { - FactoryID = GetNextID(); - _reuse = reuse; - _setup = setup ?? Setup.Default; - } - - /// The main factory method to create service expression, e.g. "new Client(new Service())". - /// If has specified, they could be used in expression. - /// Service request. - /// Created expression. - public abstract Expression CreateExpressionOrDefault(Request request); - - /// Returns service expression: either by creating it with or taking expression from cache. - /// Before returning method may transform the expression by applying , or/and decorators if found any. - public virtual Expression GetExpressionOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - - // First look for decorators if it is not already a decorator - var container = request.Container; - if (FactoryType != FactoryType.Decorator) - { - var decoratorExpr = container.GetDecoratorExpressionOrDefault(request); - if (decoratorExpr != null) - return decoratorExpr; - } - - // Then optimize for already resolved singleton object, otherwise goes normal ApplyReuse route - var setup = Setup; - var rules = container.Rules; - if (rules.EagerCachingSingletonForFasterAccess && - request.Reuse is SingletonReuse && !setup.PreventDisposal && !setup.WeaklyReferenced) - { - var itemRef = ((Scope)container.SingletonScope)._maps[FactoryID & Scope.MAP_COUNT_SUFFIX_MASK].GetEntryOrDefault(FactoryID); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return Constant(itemRef.Value); - } - - if ((request.Flags & RequestFlags.IsGeneratedResolutionDependencyExpression) == 0 && - !request.OpensResolutionScope && ( - setup.OpenResolutionScope || - !request.IsResolutionCall && ( - setup.AsResolutionCall || - setup.AsResolutionCallForExpressionGeneration && rules.UsedForExpressionGeneration || - setup.UseParentReuse || - request.FactoryType == FactoryType.Service && request.DependencyDepth > rules.DependencyDepthToSplitObjectGraph - ) && - request.GetActualServiceType() != typeof(void)) - ) - return Resolver.CreateResolutionExpression(request, setup.OpenResolutionScope); - - var mayCache = Caching != FactoryCaching.DoNotCache && - FactoryType == FactoryType.Service && - !request.IsResolutionRoot && - (!request.IsSingletonOrDependencyOfSingleton || rules.UsedForValidation) && - !request.IsDirectlyWrappedInFunc() && - !request.IsWrappedInFuncWithArgs() && - !setup.UseParentReuse && - !Made.IsConditional; - - ImMapEntry cacheEntry = null; - if (mayCache) - { - var cachedExpr = ((Container)container).GetCachedFactoryExpression(FactoryID, request, out cacheEntry); - if (cachedExpr != null) - return cachedExpr; - } - - // Creates an object graph expression with all of the dependencies created - var serviceExpr = CreateExpressionOrDefault(request); - if (serviceExpr != null) - { - if (request.Reuse != DryIoc.Reuse.Transient && - request.GetActualServiceType() != typeof(void) && - // we don't need the reuse expression when we are validating the object graph - !rules.UsedForValidation) - { - var originalServiceExprType = serviceExpr.Type; - - serviceExpr = ApplyReuse(serviceExpr, request); - - if (serviceExpr.Type != originalServiceExprType) - serviceExpr = Convert(serviceExpr, originalServiceExprType); - } - - if (mayCache) - ((Container)container).CacheFactoryExpression(FactoryID, request, serviceExpr, cacheEntry); - } - else Container.TryThrowUnableToResolve(request); - return serviceExpr; - } - - /// Applies reuse to created expression, by wrapping passed expression into scoped access - /// and producing the result expression. - protected virtual Expression ApplyReuse(Expression serviceExpr, Request request) - { - // Optimization: eagerly creates a singleton during the construction of object graph - if (request.Reuse is SingletonReuse && - request.Rules.EagerCachingSingletonForFasterAccess && - !request.TracksTransientDisposable && - !request.IsWrappedInFunc()) - { - var container = request.Container; - var scope = (Scope)container.SingletonScope; - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - var factoryId = FactoryID; - ref var map = ref scope._maps[factoryId & Scope.MAP_COUNT_SUFFIX_MASK]; - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(factoryId, Scope.NoItem), m) != m) - Ref.Swap(ref map, factoryId, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - var itemRef = map.GetEntryOrDefault(factoryId); - if (itemRef.Value == Scope.NoItem) - { - object singleton = null; - lock (itemRef) - { - if (itemRef.Value == Scope.NoItem) - { - var useFec = container.Rules.UseFastExpressionCompiler; - if (!Interpreter.TryInterpretAndUnwrapContainerException(container, serviceExpr, useFec, out singleton)) - singleton = serviceExpr.CompileToFactoryDelegate(useFec, container.Rules.UseInterpretation)(container); - - if (Setup.WeaklyReferenced) - singleton = new WeakReference(singleton); - else if (Setup.PreventDisposal) // todo: we don't need it here because because we just don't need to AddDisposable - singleton = new HiddenDisposable(singleton); - - itemRef.Value = singleton; - } - } - - if (singleton is IDisposable disp && disp != this) - { - if (Setup.DisposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, Setup.DisposalOrder); - } - } - - serviceExpr = Constant(itemRef.Value); - } - else - { - // Wrap service expression in WeakReference or HiddenDisposable - if (Setup.WeaklyReferenced) - serviceExpr = New(typeof(WeakReference).Constructor(typeof(object)), serviceExpr); - else if (Setup.PreventDisposal) - serviceExpr = New(HiddenDisposable.Ctor, serviceExpr); - - serviceExpr = request.Reuse.Apply(request, serviceExpr); - } - - // Unwrap WeakReference or HiddenDisposable - if (Setup.WeaklyReferenced) - { - serviceExpr = Call( - typeof(ThrowInGeneratedCode).GetTypeInfo().GetDeclaredMethod(nameof(ThrowInGeneratedCode.WeakRefReuseWrapperGCed)), - Property(Convert(serviceExpr, typeof(WeakReference)), - typeof(WeakReference).Property(nameof(WeakReference.Target)))); - } - else if (Setup.PreventDisposal) - { - serviceExpr = Field(Convert(serviceExpr, typeof(HiddenDisposable)), HiddenDisposable.ValueField); - } - - return serviceExpr; - } - - // todo: remove this - /// [Obsolete("Not need to control on the factory level, the remaining UseInstanceFactory will be removed")] - public virtual bool UseInterpretation(Request request) => request.Rules.UseInterpretationForTheFirstResolution; - - /// Creates factory delegate from service expression and returns it. - public virtual FactoryDelegate GetDelegateOrDefault(Request request) => - GetExpressionOrDefault(request) - ?.CompileToFactoryDelegate(request.Rules.UseFastExpressionCompiler, request.Rules.UseInterpretation); - - internal virtual bool ValidateAndNormalizeRegistration(Type serviceType, object serviceKey, bool isStaticallyChecked, Rules rules) - { - if (!isStaticallyChecked) - serviceType.ThrowIfNull(); - - var setup = Setup; - if (setup.FactoryType == FactoryType.Service) - { - // Warn about registering disposable transient - var reuse = Reuse ?? rules.DefaultReuse; - if (reuse != DryIoc.Reuse.Transient) - return true; - - if (setup.AllowDisposableTransient || - !rules.ThrowOnRegisteringDisposableTransient) - return true; - - if (setup.UseParentReuse || - setup.FactoryType == FactoryType.Decorator && ((Setup.DecoratorSetup)setup).UseDecorateeReuse) - return true; - - var knownImplOrServiceType = CanAccessImplementationType ? ImplementationType : serviceType; - if (knownImplOrServiceType.IsAssignableTo()) - Throw.It(Error.RegisteredDisposableTransientWontBeDisposedByContainer, - serviceType, serviceKey ?? "{no key}", this); - } - else if (setup.FactoryType == FactoryType.Wrapper) - { - ((Setup.WrapperSetup)setup).ThrowIfInvalidRegistration(serviceType); - } - else if (setup.FactoryType == FactoryType.Decorator) - { - if (serviceKey != null) - Throw.It(Error.DecoratorShouldNotBeRegisteredWithServiceKey, serviceKey); - } - - return true; - } - - /// Returns nice string representation of factory. - public override string ToString() - { - var s = new StringBuilder().Append("{FactoryID=").Append(FactoryID); - if (ImplementationType != null) - s.Append(", ImplType=").Print(ImplementationType); - if (Reuse != null) - s.Append(", Reuse=").Print(Reuse); - if (Setup.FactoryType != Setup.Default.FactoryType) - s.Append(", FactoryType=").Append(Setup.FactoryType); - if (Setup.Metadata != null) - s.Append(", Metadata=").Print(Setup.Metadata); - if (Setup.Condition != null) - s.Append(", HasCondition"); - - if (Setup.OpenResolutionScope) - s.Append(", OpensResolutionScope"); - else if (Setup.AsResolutionCall) - s.Append(", AsResolutionCall"); - - return s.Append("}").ToString(); - } - -#region Implementation - - internal static int _lastFactoryID; - private IReuse _reuse; - private Setup _setup; - -#endregion - } - - /// Declares delegate to get single factory method or constructor for resolved request. - public delegate FactoryMethod FactoryMethodSelector(Request request); - - /// Specifies how to get parameter info for injected parameter and resolved request - public delegate Func ParameterSelector(Request request); - - /// Specifies what properties or fields to inject and how. - public delegate IEnumerable PropertiesAndFieldsSelector(Request request); - - /// DSL for specifying injection rules. - public static class Parameters - { - /// Returns default service info wrapper for each parameter info. - public static ParameterSelector Of = request => ParameterServiceInfo.Of; - - /// Returns service info which considers each parameter as optional. - public static ParameterSelector IfUnresolvedReturnDefault = - request => pi => ParameterServiceInfo.Of(pi).WithDetails(ServiceDetails.IfUnresolvedReturnDefault); - - /// Combines source selector with other. Other is used as fallback when source returns null. - public static ParameterSelector OverrideWith(this ParameterSelector source, ParameterSelector other) => - source == null || source == Of ? other ?? Of - : other == null || other == Of ? source - : req => paramInfo => other(req)?.Invoke(paramInfo) ?? source(req)?.Invoke(paramInfo); - - /// Obsolete: please use - [Obsolete("Replaced with OverrideWith", false)] - public static ParameterSelector And(this ParameterSelector source, ParameterSelector other) => - source.OverrideWith(other); - - /// Overrides source parameter rules with specific parameter details. - /// If it is not your parameter just return null. - /// Original parameters rules - /// Should return specific details or null. - /// New parameters rules. - public static ParameterSelector Details(this ParameterSelector source, Func getDetailsOrNull) - { - getDetailsOrNull.ThrowIfNull(); - return source.OverrideWith(request => p => getDetailsOrNull(request, p)?.To(ParameterServiceInfo.Of(p).WithDetails)); - } - - /// Adds to selector service info for parameter identified by . - /// Original parameters rules. Name to identify parameter. - /// (optional) (optional) - /// (optional) By default throws exception if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// New parameters rules. - public static ParameterSelector Name(this ParameterSelector source, string name, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Details((r, p) => !p.Name.Equals(name) ? null - : ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata)); - - /// Specify parameter by name and set custom value to it. - public static ParameterSelector Name(this ParameterSelector source, - string name, Func getServiceDetails) => - source.Details((r, p) => p.Name.Equals(name) ? getServiceDetails(r, p) : null); - - /// Specify parameter by name and set custom value to it. - public static ParameterSelector Name(this ParameterSelector source, - string name, Func getCustomValue) => - source.Name(name, (r, p) => ServiceDetails.Of(getCustomValue(r))); - - /// Adds to selector service info for parameter identified by type . - /// Source selector. The type of the parameter. - /// (optional) (optional) - /// (optional) By default throws exception if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// Combined selector. - public static ParameterSelector Type(this ParameterSelector source, Type parameterType, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Details((r, p) => !parameterType.IsAssignableTo(p.ParameterType) ? null - : ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata)); - - /// Adds to selector service info for parameter identified by type . - /// Type of parameter. Source selector. - /// (optional) (optional) - /// (optional) By default throws exception if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// Combined selector. - public static ParameterSelector Type(this ParameterSelector source, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Type(typeof(T), requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata); - - /// Specify parameter by type and set its details. - public static ParameterSelector Type(this ParameterSelector source, - Func getServiceDetails) => - source.Details((r, p) => p.ParameterType == typeof(T) ? getServiceDetails(r, p) : null); - - /// Specify parameter by type and set custom value to it. - public static ParameterSelector Type(this ParameterSelector source, Func getCustomValue) => - source.Type((r, p) => ServiceDetails.Of(getCustomValue(r))); - - /// Specify parameter by type and set custom value to it. - /// Original parameters rules. - /// The type of the parameter. - /// Custom value provider. - /// New parameters rules. - public static ParameterSelector Type(this ParameterSelector source, - Type parameterType, Func getCustomValue) => - source.Details((r, p) => p.ParameterType == parameterType ? ServiceDetails.Of(getCustomValue(r)) : null); - } - - /// DSL for specifying injection rules. - public static partial class PropertiesAndFields - { - /// Say to not resolve any properties or fields. - public static PropertiesAndFieldsSelector Of = request => null; - - /// Public assignable instance members of any type except object, string, primitives types, and arrays of those. - public static PropertiesAndFieldsSelector Auto = All(withNonPublic: false, withPrimitive: false); - - /// Public, declared, assignable, non-primitive properties. - public static PropertiesAndFieldsSelector Properties( - bool withNonPublic = false, bool withBase = false, - IfUnresolved ifUnresolved = IfUnresolved.ReturnDefaultIfNotRegistered) => - All(withNonPublic: withNonPublic, withPrimitive: false, withFields: false, withBase: withBase, ifUnresolved: ifUnresolved); - - /// Should return service info for input member (property or field). - public delegate PropertyOrFieldServiceInfo GetServiceInfo(MemberInfo member, Request request); - - /// Generates selector property and field selector with settings specified by parameters. - /// If all parameters are omitted the return all public not primitive members. - public static PropertiesAndFieldsSelector All( - bool withNonPublic = true, - bool withPrimitive = true, - bool withFields = true, - bool withBase = true, - IfUnresolved ifUnresolved = IfUnresolved.ReturnDefaultIfNotRegistered, - GetServiceInfo serviceInfo = null) - { - GetServiceInfo info = (m, r) => - serviceInfo != null ? serviceInfo(m, r) : - PropertyOrFieldServiceInfo.Of(m).WithDetails(ServiceDetails.Of(ifUnresolved: ifUnresolved)); - - return req => - { - var properties = req.ImplementationType.GetMembers(x => x.DeclaredProperties, includeBase: withBase) - .Match(p => p.IsInjectable(withNonPublic, withPrimitive), p => info(p, req)); - - if (!withFields) - return properties; - - var fields = req.ImplementationType - .GetMembers(x => x.DeclaredFields, includeBase: withBase) - .Match(f => f.IsInjectable(withNonPublic, withPrimitive), f => info(f, req)); - - return properties.Append(fields); - }; - } - - /// Combines source properties and fields with other. Other will override the source condition. - /// Source selector. Specific other selector to add. - /// Combined result selector. - public static PropertiesAndFieldsSelector OverrideWith( - this PropertiesAndFieldsSelector source, PropertiesAndFieldsSelector other) - { - return source == null || source == Of ? (other ?? Of) - : other == null || other == Of ? source - : r => - { - var sourceMembers = source(r).ToArrayOrSelf(); - var otherMembers = other(r).ToArrayOrSelf(); - return sourceMembers == null || sourceMembers.Length == 0 ? otherMembers - : otherMembers == null || otherMembers.Length == 0 ? sourceMembers - : otherMembers.Append( - sourceMembers.Match(otherMembers, (om, s) => s != null && om.All(o => o == null || !s.Member.Name.Equals(o.Member.Name)))); - }; - } - - /// Obsolete: please use - [Obsolete("Replaced with OverrideWith", false)] - public static PropertiesAndFieldsSelector And( - this PropertiesAndFieldsSelector source, PropertiesAndFieldsSelector other) => - source.OverrideWith(other); - - /// Specifies service details (key, if-unresolved policy, required type) for property/field with the name. - /// Original member selector. Member name. Details. - /// New selector. - public static PropertiesAndFieldsSelector Details(this PropertiesAndFieldsSelector source, - string name, Func getDetails) - { - name.ThrowIfNull(); - getDetails.ThrowIfNull(); - return source.OverrideWith(req => - { - var implType = req.GetKnownImplementationOrServiceType(); - - var property = implType - .GetMembers(x => x.DeclaredProperties, includeBase: true) - .FindFirst(x => x.Name == name); - if (property != null && property.IsInjectable(true, true)) - return getDetails(req)?.To(PropertyOrFieldServiceInfo.Of(property).WithDetails).One(); - - var field = implType - .GetMembers(x => x.DeclaredFields, includeBase: true) - .FindFirst(x => x.Name == name); - if (field != null && field.IsInjectable(true, true)) - return getDetails(req)?.To(PropertyOrFieldServiceInfo.Of(field).WithDetails).One(); - - return Throw.For>( - Error.NotFoundSpecifiedWritablePropertyOrField, name, req); - }); - } - - /// Adds to selector service info for property/field identified by . - /// Source selector. Name to identify member. - /// (optional) (optional) - /// (optional) By default returns default value if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// Combined selector. - public static PropertiesAndFieldsSelector Name(this PropertiesAndFieldsSelector source, string name, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.ReturnDefault, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Details(name, r => ServiceDetails.Of( - requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata)); - - /// Specifies custom value for property/field with specific name. - public static PropertiesAndFieldsSelector Name(this PropertiesAndFieldsSelector source, - string name, Func getCustomValue) => - source.Details(name, r => ServiceDetails.Of(getCustomValue(r))); - - /// Returns true if property matches flags provided. - /// Property to match - /// Says to include non public properties. - /// Says to include properties of primitive type. - /// True if property is matched and false otherwise. - public static bool IsInjectable(this PropertyInfo property, - bool withNonPublic = false, bool withPrimitive = false) - { - if (!property.CanWrite || property.IsExplicitlyImplemented()) - return false; - - if (property.IsStatic()) - return false; - - return !property.IsIndexer() && - (withNonPublic || property.GetSetMethodOrNull() != null) && - (withPrimitive || !property.PropertyType.IsPrimitive(orArrayOfPrimitives: true)); - } - - /// Returns true if field matches flags provided. - /// Field to match. - /// Says to include non public fields. - /// Says to include fields of primitive type. - /// True if property is matched and false otherwise. - public static bool IsInjectable(this FieldInfo field, - bool withNonPublic = false, bool withPrimitive = false) => - !field.IsInitOnly && !field.IsBackingField() - && (withNonPublic || field.IsPublic) - && (withPrimitive || !field.FieldType.IsPrimitive(orArrayOfPrimitives: true)); - } - - /// Reflects on constructor parameters and members, - /// creates expression for each reflected dependency, and composes result service expression. - public sealed class ReflectionFactory : Factory - { - /// Non-abstract service implementation type. May be open generic. - public override Type ImplementationType - { - get - { - if (_implementationType == null && _implementationTypeProvider != null) - SetKnownImplementationType(_implementationTypeProvider(), Made); - return _implementationType; - } - } - - /// False for lazy implementation type, to prevent its early materialization. - public override bool CanAccessImplementationType => - _implementationType != null || _implementationTypeProvider == null; - - /// Provides closed-generic factory for registered open-generic variant. - public override IConcreteFactoryGenerator FactoryGenerator => _factoryGenerator; - - /// Injection rules set for Constructor/FactoryMethod, Parameters, Properties and Fields. - public override Made Made => _made; - - /// FactoryID of generator (open-generic) factory. - public int GeneratorFactoryID { get; private set; } - - /// Will contain factory ID of generator's factory for generated factory. - public override int RegistrationOrder => GeneratorFactoryID != 0 ? GeneratorFactoryID : FactoryID; - - /// Creates factory providing implementation type, optional reuse and setup. - /// (optional) Optional if Made.FactoryMethod is present Non-abstract close or open generic type. - /// (optional) (optional) (optional) - public ReflectionFactory(Type implementationType = null, IReuse reuse = null, Made made = null, Setup setup = null) - : base(reuse, setup) - { - _made = made ?? Made.Default; - SetKnownImplementationType(implementationType, _made); - } - - /// Creates factory providing implementation type, optional reuse and setup. - /// Provider of non-abstract closed or open-generic type. - /// (optional) (optional) (optional) - public ReflectionFactory(Func implementationTypeProvider, IReuse reuse = null, Made made = null, Setup setup = null) - : base(reuse, setup) - { - _made = made ?? Made.Default; - _implementationTypeProvider = implementationTypeProvider.ThrowIfNull(); - } - - /// Creates service expression. - public override Expression CreateExpressionOrDefault(Request request) - { - var container = request.Container; - var rules = container.Rules; - - FactoryMethod factoryMethod; - var factoryMethodSelector = Made.FactoryMethod ?? rules.FactoryMethod; - if (factoryMethodSelector == null) - { - factoryMethod = new FactoryMethod(_knownSingleCtor ?? request.ImplementationType.SingleConstructor()); - } - else if ((factoryMethod = factoryMethodSelector(request)) == null) - return Throw.For(request.IfUnresolved != IfUnresolved.ReturnDefault, - Error.UnableToSelectCtor, request.ImplementationType, request); - - // If factory method is the method of some registered service, then resolve factory service first. - var factoryExpr = factoryMethod.FactoryExpression; - if (factoryExpr == null && factoryMethod.FactoryServiceInfo != null) - { - var factoryRequest = request.Push(factoryMethod.FactoryServiceInfo); - factoryExpr = container.ResolveFactory(factoryRequest)?.GetExpressionOrDefault(factoryRequest); - if (factoryExpr == null) - return null; - } - - var ctorOrMember = factoryMethod.ConstructorOrMethodOrMember; - var ctorOrMethod = ctorOrMember as MethodBase; - if (ctorOrMethod == null) // return early when factory is Property or Field - { - var memberExpr = ctorOrMember is PropertyInfo - ? Property(factoryExpr, (PropertyInfo)ctorOrMember) - : (Expression)Field(factoryExpr, (FieldInfo)ctorOrMember); - return ConvertExpressionIfNeeded(memberExpr, request, ctorOrMember); - } - - Expression arg0 = null, arg1 = null, arg2 = null, arg3 = null, arg4 = null; - Expression[] paramExprs = Empty(); - var parameters = ctorOrMethod.GetParameters(); - if (parameters.Length != 0) - { - paramExprs = factoryMethod.ResolvedParameterExpressions; - if (paramExprs == null) - { - if (parameters.Length > 5) - paramExprs = new Expression[parameters.Length]; - - var paramSelector = rules.OverrideRegistrationMade - ? Made.Parameters.OverrideWith(rules.Parameters) - : rules.Parameters.OverrideWith(Made.Parameters); - var paramServiceInfoSelector = paramSelector(request); - - var inputArgs = request.InputArgExprs; - var argsUsedMask = 0; - for (var i = 0; i < parameters.Length; i++) - { - var param = parameters[i]; - if (inputArgs != null) - { - var inputArgExpr = TryGetExpressionFromInputArgs(param.ParameterType, inputArgs, ref argsUsedMask); - if (inputArgExpr != null) - { - if (paramExprs != null) - paramExprs[i] = inputArgExpr; - else if (i == 0) - arg0 = inputArgExpr; - else if (i == 1) - arg1 = inputArgExpr; - else if (i == 2) - arg2 = inputArgExpr; - else if (i == 3) - arg3 = inputArgExpr; - else - arg4 = inputArgExpr; - continue; - } - } - - var paramInfo = paramServiceInfoSelector(param) ?? ParameterServiceInfo.Of(param); - var paramRequest = request.Push(paramInfo); - var paramDetails = paramInfo.Details; - var usedOrCustomValExpr = TryGetUsedInstanceOrCustomValueExpression(request, paramRequest, paramDetails); - if (usedOrCustomValExpr != null) - { - if (paramExprs != null) - paramExprs[i] = usedOrCustomValExpr; - else if (i == 0) - arg0 = usedOrCustomValExpr; - else if (i == 1) - arg1 = usedOrCustomValExpr; - else if (i == 2) - arg2 = usedOrCustomValExpr; - else if (i == 3) - arg3 = usedOrCustomValExpr; - else - arg4 = usedOrCustomValExpr; - continue; - } - - var injectedExpr = container.ResolveFactory(paramRequest)?.GetExpressionOrDefault(paramRequest); - if (injectedExpr == null || - // When param is an empty array / collection, then we may use a default value instead (#581) - paramDetails.DefaultValue != null && - injectedExpr.NodeType == System.Linq.Expressions.ExpressionType.NewArrayInit && - ((NewArrayExpression) injectedExpr).Expressions.Count == 0) - { - // Check if parameter dependency itself (without propagated parent details) - // does not allow default, then stop checking the rest of parameters. - if (paramDetails.IfUnresolved == IfUnresolved.Throw) - return null; - injectedExpr = paramDetails.DefaultValue != null - ? container.GetConstantExpression(paramDetails.DefaultValue) - : paramRequest.ServiceType.GetDefaultValueExpression(); - } - - if (paramExprs != null) - paramExprs[i] = injectedExpr; - else if (i == 0) - arg0 = injectedExpr; - else if (i == 1) - arg1 = injectedExpr; - else if (i == 2) - arg2 = injectedExpr; - else if (i == 3) - arg3 = injectedExpr; - else - arg4 = injectedExpr; - } - } - } - - if (rules.UsedForValidation) - return request.GetActualServiceType().GetDefaultValueExpression(); - - var ctor = ctorOrMethod as ConstructorInfo; - Expression serviceExpr; - if (arg0 == null) - serviceExpr = ctor != null ? New(ctor, paramExprs) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, paramExprs); - else if (arg1 == null) - serviceExpr = ctor != null ? New(ctor, arg0) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0); - else if (arg2 == null) - serviceExpr = ctor != null ? New(ctor, arg0, arg1) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1); - else if (arg3 == null) - serviceExpr = ctor != null ? New(ctor, arg0, arg1, arg2) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1, arg2); - else if (arg4 == null) - serviceExpr = ctor != null ? New(ctor, arg0, arg1, arg2, arg3) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1, arg2, arg3); - else - serviceExpr = ctor != null ? New(ctor, arg0, arg1, arg2, arg3, arg4) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1, arg2, arg3, arg4); - - if (ctor == null) - return ConvertExpressionIfNeeded(serviceExpr, request, ctorOrMember); - - if (rules.PropertiesAndFields == null && Made.PropertiesAndFields == null) - return serviceExpr; - - return CreateMemberInitExpression(serviceExpr, request); - } - - private Expression CreateMemberInitExpression(Expression serviceExpr, Request request) - { - var container = request.Container; - var rules = container.Rules; - var propertiesAndFieldsSelector = rules.OverrideRegistrationMade - ? Made.PropertiesAndFields.OverrideWith(rules.PropertiesAndFields) - : rules.PropertiesAndFields.OverrideWith(Made.PropertiesAndFields); - var propertiesAndFields = propertiesAndFieldsSelector.Invoke(request); - if (propertiesAndFields == null) - return serviceExpr; - - var assignments = Empty(); - foreach (var member in propertiesAndFields) - if (member != null) - { - var memberRequest = request.Push(member); - var memberExpr = - TryGetUsedInstanceOrCustomValueExpression(request, memberRequest, member.Details) - ?? container.ResolveFactory(memberRequest)?.GetExpressionOrDefault(memberRequest); - if (memberExpr != null) - assignments = assignments.AppendOrUpdate(Bind(member.Member, memberExpr)); - else if (request.IfUnresolved == IfUnresolved.ReturnDefault) - return null; - } - - return assignments.Length == 0 ? serviceExpr : MemberInit((NewExpression) serviceExpr, assignments); - } - - private static Expression ConvertExpressionIfNeeded(Expression serviceExpr, Request request, MemberInfo ctorOrMember) - { - var actualServiceType = request.GetActualServiceType(); - var serviceExprType = serviceExpr.Type; - if (serviceExprType == typeof(object)) - return Convert(serviceExpr, actualServiceType); - return serviceExprType == actualServiceType || serviceExprType.IsAssignableTo(actualServiceType) ? serviceExpr - : serviceExprType.HasConversionOperatorTo(actualServiceType) ? Convert(serviceExpr, actualServiceType) - : request.IfUnresolved != IfUnresolved.Throw ? null - : Throw.For(Error.ServiceIsNotAssignableFromFactoryMethod, actualServiceType, ctorOrMember, - request); - } - - // Check not yet used arguments provided via `Func` or `Resolve(.., args: new[] { arg })` - internal static Expression TryGetExpressionFromInputArgs(Type paramType, Expression[] inputArgs, ref int argsUsedMask) - { - for (var a = 0; a < inputArgs.Length; ++a) - if ((argsUsedMask & 1 << a) == 0 && inputArgs[a].Type.IsAssignableTo(paramType)) - { - argsUsedMask |= 1 << a; // mark that argument was used - return inputArgs[a]; - } - return null; - } - - internal static Expression TryGetUsedInstanceOrCustomValueExpression(Request request, Request paramRequest, ServiceDetails paramDetails) - { - if (paramDetails == DryIoc.ServiceDetails.Default) - { - // Generate the fast resolve call for used instances - if (request.Container.TryGetUsedInstance(paramRequest.ServiceType, out var instance)) - return Call(ResolverContext.GetRootOrSelfExpr(paramRequest), Resolver.ResolveFastMethod, - Constant(paramRequest.ServiceType, typeof(Type)), Constant(paramRequest.IfUnresolved)); - return null; - } - - if (paramDetails.HasCustomValue) - { - var serviceType = paramRequest.ServiceType; - var hasConversionOperator = false; - var customValue = paramDetails.CustomValue; - if (customValue != null) - { - var customTypeValue = customValue.GetType(); - if (!customTypeValue.IsArray && - !customTypeValue.IsAssignableTo(serviceType) && - !(hasConversionOperator = customTypeValue.HasConversionOperatorTo(serviceType))) - return Throw.For(paramRequest.IfUnresolved != IfUnresolved.ReturnDefault, - Error.InjectedCustomValueIsOfDifferentType, customValue, serviceType, paramRequest); - } - - return hasConversionOperator - ? Convert(request.Container.GetConstantExpression(customValue), serviceType) - : request.Container.GetConstantExpression(customValue, serviceType); - } - - return null; - } - - internal override bool ValidateAndNormalizeRegistration(Type serviceType, object serviceKey, bool isStaticallyChecked, Rules rules) - { - base.ValidateAndNormalizeRegistration(serviceType, serviceKey, isStaticallyChecked, rules); - - if (!CanAccessImplementationType) - return true; - - var implType = ImplementationType; - if (Made.FactoryMethod == null && rules.FactoryMethod == null) - { - var ctors = implType.GetTypeInfo().DeclaredConstructors.ToArrayOrSelf(); - var ctorCount = 0; - for (var i = 0; ctorCount != 2 && i < ctors.Length; i++) - { - var ctor = ctors[i]; - if (ctor.IsPublic && !ctor.IsStatic) - { - ++ctorCount; - _knownSingleCtor = ctor; - } - } - - if (ctorCount == 0) - Throw.It(Error.UnableToSelectSinglePublicConstructorFromNone, implType); - else if (ctorCount > 1) - Throw.It(Error.UnableToSelectSinglePublicConstructorFromMultiple, implType, ctors); - } - - if (isStaticallyChecked || implType == null) - return true; - - var implTypeInfo = implType.GetTypeInfo(); - if (!implTypeInfo.IsGenericTypeDefinition) - { - if (implTypeInfo.IsGenericType && !ReflectionTools.AreAllTypeArgumentsClosed(implTypeInfo.GetGenericParamsAndArgsUnsafe())) - Throw.It(Error.RegisteringNotAGenericTypedefImplType, implType, implType.GetGenericTypeDefinition()); - - else if (implType != serviceType && serviceType != typeof(object)) - { - if (!serviceType.IsGenericDefinition()) - { - if (!implType.IsImplementingServiceType(serviceType)) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - } - else - { - if (implType.GetImplementedTypes().IndexOf(serviceType, (st, t) => t == st || t.GetGenericDefinitionOrNull() == st) == -1) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - } - } - } - else if (implType != serviceType) // implTypeInfo.IsGenericTypeDefinition - { - var serviceTypeInfo = serviceType.GetTypeInfo(); - if (serviceTypeInfo.IsGenericTypeDefinition) - { - var implTypeParams = implTypeInfo.GetGenericParamsAndArgsUnsafe(); - var implementedTypes = implType.GetImplementedTypes(); - - var implementedTypeFound = false; - var containsAllTypeParams = false; - for (var i = 0; !containsAllTypeParams && i < implementedTypes.Length; ++i) - { - var implementedType = implementedTypes[i]; - implementedTypeFound = implementedType.GetGenericDefinitionOrNull() == serviceType; - containsAllTypeParams = implementedTypeFound && implementedType.ContainsAllGenericTypeParameters(implTypeParams); - } - - if (!implementedTypeFound) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - - if (!containsAllTypeParams) - Throw.It(Error.RegisteringOpenGenericServiceWithMissingTypeArgs, - implType, serviceType, implementedTypes.Match(t => t.GetGenericDefinitionOrNull() == serviceType)); - } - - else if (serviceTypeInfo.IsGenericType && !ReflectionTools.AreAllTypeArgumentsClosed(serviceTypeInfo.GetGenericParamsAndArgsUnsafe())) - Throw.It(Error.RegisteringNotAGenericTypedefServiceType, - serviceType, serviceType.GetGenericTypeDefinition()); - - else if (!serviceTypeInfo.IsGenericType) - Throw.It(Error.RegisteringOpenGenericImplWithNonGenericService, implType, serviceType); - - else if (!implType.IsImplementingServiceType(serviceType.GetGenericTypeDefinition())) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - } - - return true; - } - -#region Implementation - - private Type _implementationType; // non-readonly to be set by lazy type provider - private readonly Func _implementationTypeProvider; - private readonly Made _made; - private ClosedGenericFactoryGenerator _factoryGenerator; - private ConstructorInfo _knownSingleCtor; - - private sealed class ClosedGenericFactoryGenerator : IConcreteFactoryGenerator - { - public ImHashMap, ReflectionFactory> GeneratedFactories => _generatedFactories.Value; - - public ClosedGenericFactoryGenerator(ReflectionFactory openGenericFactory) => _openGenericFactory = openGenericFactory; - - public Factory GetGeneratedFactory(Request request, bool ifErrorReturnDefault = false) - { - var openFactory = _openGenericFactory; - var implType = openFactory._implementationType; - var serviceType = request.GetActualServiceType(); - var serviceTypeInfo = serviceType.GetTypeInfo(); - - Type[] closedTypeArgs; - if (implType == null || serviceTypeInfo.IsGenericType && serviceType.GetGenericTypeDefinition() == implType) - closedTypeArgs = serviceTypeInfo.GetGenericParamsAndArgsUnsafe(); - else if (implType.IsGenericParameter) - closedTypeArgs = serviceType.One(); - else - { - var implTypeInfo = implType.GetTypeInfo(); - var implTypeParams = implTypeInfo.GetGenericParamsAndArgsUnsafe(); - - closedTypeArgs = new Type[implTypeParams.Length]; - var implementedTypes = implType.GetImplementedTypes(); - - var serviceTypeArgs = serviceTypeInfo.GetGenericParamsAndArgsUnsafe(); - - var matchFound = false; - for (var i = 0; !matchFound && i < implementedTypes.Length; ++i) - { - var implementedType = implementedTypes[i]; - var implementedTypeInfo = implementedType.GetTypeInfo(); - if (implementedTypeInfo.IsGenericType && - implementedType.GetGenericTypeDefinition() == serviceType.GetGenericTypeDefinition()) - { - var implementedTypeGenericParams = implementedTypeInfo.GetGenericParamsAndArgsUnsafe(); - if (!ReflectionTools.AreAllTypeArgumentsClosed(implementedTypeGenericParams)) - matchFound = MatchServiceWithImplementedTypeParams( - closedTypeArgs, implTypeParams, implementedTypeGenericParams, serviceTypeArgs); - } - } - - if (!matchFound) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null : - Throw.For(Error.NoMatchedImplementedTypesWithServiceType, implType, implementedTypes, request); - - MatchOpenGenericConstraints(implTypeParams, closedTypeArgs); - - for (var i = 0; i < closedTypeArgs.Length; i++) - if (closedTypeArgs[i] == null) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null : - Throw.For(Error.NotFoundOpenGenericImplTypeArgInService, - implType, implTypeParams[i], request); - } - - var made = openFactory.Made; - if (made.FactoryMethod != null) - { - // resolve request with factory to specify the implementation type may be required by FactoryMethod or GetClosed... - request = request.WithResolvedFactory(openFactory, ifErrorReturnDefault, ifErrorReturnDefault, copyRequest: true); - var factoryMethod = made.FactoryMethod(request); - if (factoryMethod == null) - return ifErrorReturnDefault ? null : // todo: should we check ifUnresolved here, - Throw.For(Error.GotNullFactoryWhenResolvingService, request); - - var checkMatchingType = implType != null && implType.IsGenericParameter; - var closedFactoryMethod = GetClosedFactoryMethodOrDefault(factoryMethod, closedTypeArgs, request, checkMatchingType); - if (closedFactoryMethod == null) // may be null only for `IfUnresolved.ReturnDefault` or if the check for matching type is failed - return null; - - made = Made.Of(closedFactoryMethod, made.Parameters, made.PropertiesAndFields); - } - - if (implType != null) - { - implType = implType.IsGenericParameter ? closedTypeArgs[0] : - Throw.IfThrows(() => implType.MakeGenericType(closedTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, implType, request); - if (implType == null) - return null; - } - - var knownImplOrServiceType = implType ?? made.FactoryMethodKnownResultType ?? serviceType; - var serviceKey = request.ServiceKey; - serviceKey = (serviceKey as OpenGenericTypeKey)?.ServiceKey ?? serviceKey; - var generatedFactoryKey = KV.Of(knownImplOrServiceType, serviceKey); - - var generatedFactories = _generatedFactories.Value; - if (!generatedFactories.IsEmpty) - { - var generatedFactory = generatedFactories.GetValueOrDefault(generatedFactoryKey); - if (generatedFactory != null) - return generatedFactory; - } - - var closedGenericFactory = new ReflectionFactory(implType, openFactory.Reuse, made, openFactory.Setup) - { - GeneratorFactoryID = openFactory.FactoryID, - Caching = openFactory.Caching - }; - - // we should use whatever the first factory is registered because it can be used already in decorators and recursive factories check - _generatedFactories.Swap(generatedFactoryKey, closedGenericFactory, - (x, genFacKey, fac) => x.AddOrUpdate(genFacKey, fac, (oldFac, _) => closedGenericFactory = oldFac)); - - return closedGenericFactory; - } - - private readonly ReflectionFactory _openGenericFactory; - private readonly Ref, ReflectionFactory>> - _generatedFactories = Ref.Of(ImHashMap, ReflectionFactory>.Empty); - } - - private void SetKnownImplementationType(Type implType, Made made) - { - var knownImplType = implType; - - var factoryMethodResultType = Made.FactoryMethodKnownResultType; - if (implType == null || - implType == typeof(object) || // required as currently object represents the open-generic type argument T registrations - implType.IsAbstract()) - { - if (made.FactoryMethod == null) - { - if (implType == null) - Throw.It(Error.RegisteringNullImplementationTypeAndNoFactoryMethod); - if (implType == typeof(object)) - Throw.It(Error.RegisteringObjectTypeAsImplementationIsNotSupported); - if (implType.IsAbstract()) - Throw.It(Error.RegisteringAbstractImplementationTypeAndNoFactoryMethod, implType); - } - - knownImplType = null; // Ensure that we do not have abstract implementation type - - // Using non-abstract factory method result type is safe for conditions and diagnostics - if (factoryMethodResultType != null && - factoryMethodResultType != typeof(object) && - !factoryMethodResultType.IsAbstract()) - knownImplType = factoryMethodResultType; - } - else if (factoryMethodResultType != null - && factoryMethodResultType != implType) - { - if (!factoryMethodResultType.IsAssignableTo(implType) && - !factoryMethodResultType.HasConversionOperatorTo(implType)) - Throw.It(Error.RegisteredFactoryMethodResultTypesIsNotAssignableToImplementationType, - implType, factoryMethodResultType); - } - - var openGenericImplType = knownImplType ?? implType; - if (openGenericImplType == typeof(object) || // for open-generic T implementation - openGenericImplType != null && // for open-generic X implementation - (openGenericImplType.IsGenericDefinition() || openGenericImplType.IsGenericParameter) || - made.IsConditionalImplementation) - { - _factoryGenerator = new ClosedGenericFactoryGenerator(this); - } - - _implementationType = knownImplType; - } - - private static void MatchOpenGenericConstraints(Type[] implTypeParams, Type[] implTypeArgs) - { - for (var i = 0; i < implTypeParams.Length; i++) - { - var implTypeArg = implTypeArgs[i]; - if (implTypeArg == null) - continue; // skip yet unknown type arg - - var implTypeParamConstraints = implTypeParams[i].GetGenericParamConstraints(); - if (implTypeParamConstraints.IsNullOrEmpty()) - continue; // skip case with no constraints - - // match type parameters inside constraint - var constraintMatchFound = false; - for (var j = 0; !constraintMatchFound && j < implTypeParamConstraints.Length; ++j) - { - var implTypeParamConstraint = implTypeParamConstraints[j]; - if (implTypeParamConstraint != implTypeArg && implTypeParamConstraint.IsOpenGeneric()) - { - var implTypeArgArgs = implTypeArg.IsGeneric() ? implTypeArg.GetGenericParamsAndArgs() : implTypeArg.One(); - var implTypeParamConstraintParams = implTypeParamConstraint.GetGenericParamsAndArgs(); - - constraintMatchFound = MatchServiceWithImplementedTypeParams( - implTypeArgs, implTypeParams, implTypeParamConstraintParams, implTypeArgArgs); - } - } - } - } - - private static bool MatchServiceWithImplementedTypeParams( - Type[] resultImplArgs, Type[] implParams, Type[] serviceParams, Type[] serviceArgs, - int resultCount = 0) - { - if (serviceArgs.Length != serviceParams.Length) - return false; - - for (var i = 0; i < serviceParams.Length; i++) - { - var serviceArg = serviceArgs[i]; - var implementedParam = serviceParams[i]; - if (implementedParam.IsGenericParameter) - { - var paramIndex = implParams.Length - 1; - while (paramIndex != -1 && !ReferenceEquals(implParams[paramIndex], implementedParam)) - --paramIndex; - if (paramIndex != -1) - { - if (resultImplArgs[paramIndex] == null) - resultImplArgs[paramIndex] = serviceArg; - else if (resultImplArgs[paramIndex] != serviceArg) - return false; // more than one service type arg is matching with single implementation type parameter - } - } - else if (implementedParam != serviceArg) - { - if (!implementedParam.IsOpenGeneric() || - implementedParam.GetGenericDefinitionOrNull() != serviceArg.GetGenericDefinitionOrNull()) - return false; // type parameter and argument are of different types - - if (!MatchServiceWithImplementedTypeParams(resultImplArgs, implParams, - implementedParam.GetGenericParamsAndArgs(), serviceArg.GetGenericParamsAndArgs())) - return false; // nested match failed due one of above reasons. - } - } - - return true; - } - - private static FactoryMethod GetClosedFactoryMethodOrDefault( - FactoryMethod factoryMethod, Type[] serviceTypeArgs, Request request, bool ifErrorReturnDefault = false) - { - var factoryMember = factoryMethod.ConstructorOrMethodOrMember; - var factoryInfo = factoryMethod.FactoryServiceInfo; - - var resultType = factoryMember.GetReturnTypeOrDefault(); - var implTypeParams = resultType.IsGenericParameter ? resultType.One() : resultType.GetGenericParamsAndArgs(); - - // Get method declaring type, and if its open-generic, - // then close it first. It is required to get actual method. - var factoryImplType = factoryMember.DeclaringType.ThrowIfNull(); - if (factoryImplType.IsOpenGeneric()) - { - var factoryImplTypeParams = factoryImplType.GetGenericParamsAndArgs(); - var resultFactoryImplTypeArgs = new Type[factoryImplTypeParams.Length]; - - var isFactoryImplTypeClosed = MatchServiceWithImplementedTypeParams( - resultFactoryImplTypeArgs, factoryImplTypeParams, implTypeParams, serviceTypeArgs); - - if (!isFactoryImplTypeClosed) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null - : Throw.For(Error.NoMatchedFactoryMethodDeclaringTypeWithServiceTypeArgs, - factoryImplType, new StringBuilder().Print(serviceTypeArgs, itemSeparator: ", "), request); - - // For instance factory match its service type from the implementation factory type. - if (factoryInfo != null) - { - // Look for service type equivalent within factory implementation type base classes and interfaces, - // because we need identical type arguments to match. - var factoryServiceType = factoryInfo.ServiceType; - if (factoryServiceType != factoryImplType) - factoryServiceType = factoryImplType.GetImplementedTypes() - .FindFirst(factoryServiceType, (fServiceType, t) => t.IsGeneric() && t.GetGenericTypeDefinition() == fServiceType) - .ThrowIfNull(); - - var factoryServiceTypeParams = factoryServiceType.GetGenericParamsAndArgs(); - var resultFactoryServiceTypeArgs = new Type[factoryServiceTypeParams.Length]; - - var isFactoryServiceTypeClosed = MatchServiceWithImplementedTypeParams( - resultFactoryServiceTypeArgs, factoryServiceTypeParams, factoryImplTypeParams, resultFactoryImplTypeArgs); - - // Replace factory info with close factory service type - if (isFactoryServiceTypeClosed) - { - factoryServiceType = factoryServiceType.GetGenericTypeDefinition().ThrowIfNull(); - var closedFactoryServiceType = Throw.IfThrows( - () => factoryServiceType.MakeGenericType(resultFactoryServiceTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, factoryServiceType, request); - - if (closedFactoryServiceType == null) - return null; - - // Copy factory info with closed factory type - factoryInfo = ServiceInfo.Of(closedFactoryServiceType).WithDetails(factoryInfo.Details); - } - } - - MatchOpenGenericConstraints(factoryImplTypeParams, resultFactoryImplTypeArgs); - - // Close the factory type implementation - // and get factory member to use from it. - var closedFactoryImplType = Throw.IfThrows( - () => factoryImplType.MakeGenericType(resultFactoryImplTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, factoryImplType, request); - - if (closedFactoryImplType == null) - return null; - - // Find corresponding member again, now from closed type - var factoryMethodBase = factoryMember as MethodBase; - if (factoryMethodBase != null) - { - var factoryMethodParameters = factoryMethodBase.GetParameters(); - var targetMethods = closedFactoryImplType.GetMembers(t => t.DeclaredMethods, includeBase: true) - .Match(m => m.Name == factoryMember.Name && m.GetParameters().Length == factoryMethodParameters.Length) - .ToArrayOrSelf(); - - if (targetMethods.Length == 1) - factoryMember = targetMethods[0]; - else // Fallback to MethodHandle only if methods have similar signatures - { - var methodHandleProperty = typeof(MethodBase).GetTypeInfo() - .DeclaredProperties - .FindFirst(it => it.Name == "MethodHandle") - .ThrowIfNull(Error.OpenGenericFactoryMethodDeclaringTypeIsNotSupportedOnThisPlatform, - factoryImplType, closedFactoryImplType, factoryMethodBase.Name); - factoryMember = MethodBase.GetMethodFromHandle( - (RuntimeMethodHandle)methodHandleProperty.GetValue(factoryMethodBase, Empty()), - closedFactoryImplType.TypeHandle); - } - } - else if (factoryMember is FieldInfo) - { - factoryMember = closedFactoryImplType.GetMembers(t => t.DeclaredFields, includeBase: true) - .Single(f => f.Name == factoryMember.Name); - } - else if (factoryMember is PropertyInfo) - { - factoryMember = closedFactoryImplType.GetMembers(t => t.DeclaredProperties, includeBase: true) - .Single(f => f.Name == factoryMember.Name); - } - } - - // If factory method is actual method and still open-generic after closing its declaring type, - // then match remaining method type parameters and make closed method - var openFactoryMethod = factoryMember as MethodInfo; - if (openFactoryMethod != null && openFactoryMethod.ContainsGenericParameters) - { - var methodTypeParams = openFactoryMethod.GetGenericArguments(); - var resultMethodTypeArgs = new Type[methodTypeParams.Length]; - - var isMethodClosed = MatchServiceWithImplementedTypeParams( - resultMethodTypeArgs, methodTypeParams, implTypeParams, serviceTypeArgs); - - if (!isMethodClosed) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null - : Throw.For(Error.NoMatchedFactoryMethodWithServiceTypeArgs, - openFactoryMethod, new StringBuilder().Print(serviceTypeArgs, itemSeparator: ", "), - request); - - MatchOpenGenericConstraints(methodTypeParams, resultMethodTypeArgs); - - factoryMember = Throw.IfThrows( - () => openFactoryMethod.MakeGenericMethod(resultMethodTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, factoryImplType, request); - - if (factoryMember == null) - return null; - } - - return FactoryMethod.Of(factoryMember, factoryInfo); - } - -#endregion - } - - /// Creates service expression using client provided expression factory delegate. - public sealed class ExpressionFactory : Factory - { - /// Wraps provided delegate into factory. - /// Delegate that will be used internally to create service expression. - /// (optional) Reuse. (optional) Setup. - public ExpressionFactory(Func getServiceExpression, IReuse reuse = null, Setup setup = null) - : base(reuse, setup) - { - _getServiceExpression = getServiceExpression.ThrowIfNull(); - } - - /// Creates service expression using wrapped delegate. - /// Request to resolve. Expression returned by stored delegate. - public override Expression CreateExpressionOrDefault(Request request) => - _getServiceExpression(request); - - private readonly Func _getServiceExpression; - } - - /// Wraps the instance in registry - public sealed class RegisteredInstanceFactory : Factory - { - /// The registered pre-created object instance - public readonly object Instance; - - /// Non-abstract closed implementation type. - public override Type ImplementationType { get; } - - /// - public override bool HasRuntimeState => true; - - /// Simplified specially for register instance - internal override bool ValidateAndNormalizeRegistration(Type serviceType, object serviceKey, bool isStaticallyChecked, Rules rules) - { - if (!isStaticallyChecked && (ImplementationType != null && !ImplementationType.IsAssignableTo(serviceType.ThrowIfNull()))) - Throw.It(Error.RegisteringInstanceNotAssignableToServiceType, ImplementationType, serviceType); - return true; - } - - /// Creates factory. - public RegisteredInstanceFactory(object instance, IReuse reuse = null, Setup setup = null) - : base(reuse ?? DryIoc.Reuse.Singleton, - (setup ?? DryIoc.Setup.Default).WithAsResolutionCallForGeneratedExpression()) - { - if (instance != null) // it may be `null` as well - { - ImplementationType = instance.GetType(); - if (Setup.WeaklyReferenced) - Instance = new WeakReference(instance); - else - Instance = instance; - } - } - - /// Wraps the instance in expression constant - public override Expression CreateExpressionOrDefault(Request request) - { - // unpacks the weak-reference - if (Setup.WeaklyReferenced) - return Call( - typeof(ThrowInGeneratedCode).GetTypeInfo() - .GetDeclaredMethod(nameof(ThrowInGeneratedCode.WeakRefReuseWrapperGCed)), - Property( - Constant(Instance, typeof(WeakReference)), - typeof(WeakReference).Property(nameof(WeakReference.Target)))); - - // otherwise just return a constant - var instanceExpr = request.Container.GetConstantExpression(Instance); - var serviceType = request.GetActualServiceType(); - if (ImplementationType.IsAssignableTo(serviceType)) - return instanceExpr; - return Convert(instanceExpr, serviceType); - } - - /// Simplified path for the registered instance - public override Expression GetExpressionOrDefault(Request request) - { - if (// preventing recursion - (request.Flags & RequestFlags.IsGeneratedResolutionDependencyExpression) == 0 && !request.IsResolutionCall && - (Setup.AsResolutionCall || Setup.AsResolutionCallForExpressionGeneration && request.Rules.UsedForExpressionGeneration)) - return Resolver.CreateResolutionExpression(request.WithResolvedFactory(this), Setup.OpenResolutionScope); - - // First look for decorators if it is not already a decorator - var serviceType = request.ServiceType; - var elementType = serviceType.GetArrayElementTypeOrNull(); - if (elementType != null) - serviceType = typeof(IEnumerable<>).MakeGenericType(elementType); - if (!request.Container.GetDecoratorFactoriesOrDefault(serviceType).IsNullOrEmpty()) - { - var decoratorExpr = request.Container.GetDecoratorExpressionOrDefault(request.WithResolvedFactory(this)); - if (decoratorExpr != null) - return decoratorExpr; - } - - return CreateExpressionOrDefault(request); - } - - /// Used at resolution root too simplify getting the actual instance - public override FactoryDelegate GetDelegateOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - - if (request.Container.GetDecoratorExpressionOrDefault(request) != null) - return base.GetDelegateOrDefault(request); - - return Setup.WeaklyReferenced - ? (FactoryDelegate)UnpackWeakRefFactory - : InstanceFactory; - } - - private object InstanceFactory(IResolverContext _) => Instance; - private object UnpackWeakRefFactory(IResolverContext _) => (Instance as WeakReference)?.Target.WeakRefReuseWrapperGCed(); - } - - /// This factory is the thin wrapper for user provided delegate - /// and where possible it uses delegate directly: without converting it to expression. - public sealed class DelegateFactory : Factory - { - /// Non-abstract closed implementation type. - public override Type ImplementationType { get; } - - /// - public override bool HasRuntimeState => true; - - /// Creates factory. - public DelegateFactory(FactoryDelegate factoryDelegate, - IReuse reuse = null, Setup setup = null, Type knownImplementationType = null) - : base(reuse, (setup ?? Setup.Default).WithAsResolutionCallForGeneratedExpression()) - { - _factoryDelegate = factoryDelegate.ThrowIfNull(); - ImplementationType = knownImplementationType; - } - - /// Create expression by wrapping call to stored delegate with provided request. - public override Expression CreateExpressionOrDefault(Request request) - { - // GetConstant here is needed to check the state - var delegateExpr = request.Container.GetConstantExpression(_factoryDelegate); - var resolverExpr = ResolverContext.GetRootOrSelfExpr(request); - return Convert(Invoke(delegateExpr, resolverExpr), request.GetActualServiceType()); - } - - /// If possible returns delegate directly, without creating expression trees, just wrapped in . - /// If decorator found for request then factory fall-backs to expression creation. - /// Request to resolve. - /// Factory delegate directly calling wrapped delegate, or invoking expression if decorated. - public override FactoryDelegate GetDelegateOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - - // Wrap the delegate in respective expression for non-simple use - if (request.Reuse != DryIoc.Reuse.Transient || - FactoryType == FactoryType.Service && - request.Container.GetDecoratorExpressionOrDefault(request) != null) - return base.GetDelegateOrDefault(request); - - // Otherwise just use delegate as-is - return _factoryDelegate; - } - - private readonly FactoryDelegate _factoryDelegate; - } - - internal sealed class FactoryPlaceholder : Factory - { - public static readonly Factory Default = new FactoryPlaceholder(); - - // Always resolved asResolutionCall, to create a hole in object graph to be filled in later - public override Setup Setup => _setup; - private static readonly Setup _setup = Setup.With(asResolutionCall: true); - - public override Expression CreateExpressionOrDefault(Request request) => - Throw.For(Error.NoImplementationForPlaceholder, request); - } - - /// Should return value stored in scope - public delegate object CreateScopedValue(); - - /// Lazy object storage that will create object with provided factory on first access, - /// then will be returning the same object for subsequent access. - public interface IScope : IEnumerable, IDisposable - { - /// Parent scope in scope stack. Null for root scope. - IScope Parent { get; } - - /// Optional name object associated with scope. - object Name { get; } - - /// True if scope is disposed. - bool IsDisposed { get; } - - /// Looks up for stored item by id. - bool TryGet(out object item, int id); - - /// Creates, stores, and returns created item - object GetOrAdd(int id, CreateScopedValue createValue, int disposalOrder = 0); - - /// Create the value via `FactoryDelegate` passing the `IResolverContext` - object GetOrAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder = 0); - - /// Creates, stores, and returns created item - object TryGetOrAddWithoutClosure(int id, - IResolverContext resolveContext, Expression expr, bool useFec, - Func createValue, int disposalOrder = 0); - - /// Tracked item will be disposed with the scope. - /// Smaller will be disposed first. - object TrackDisposable(object item, int disposalOrder = 0); - - ///[Obsolete("Removing because it is used only by obsolete `UseInstance` feature")] - void SetOrAdd(int id, object item); - - ///[Obsolete("Removing because it is not used")] - object GetOrTryAdd(int id, object item, int disposalOrder); - - /// Sets (replaces) the factory for specified type. - void SetUsedInstance(Type type, FactoryDelegate factory); - - /// Looks up for stored item by type. - bool TryGetUsedInstance(IResolverContext r, Type type, out object instance); - - /// Clones the scope. - IScope Clone(); - } - - /// Scope is container to hold the shared per scope items and dispose items. - /// Scope uses Locking to ensure that the object factory called only once. - public sealed class Scope : IScope - { - /// Parent scope in scope stack. Null for the root scope. - public IScope Parent { get; } - - /// Optional name associated with scope. - public object Name { get; } - - /// True if scope is disposed. - public bool IsDisposed => _disposed == 1; - private int _disposed; - - private ImHashMap _factories; - private ImList _unorderedDisposables; - private ImMap _disposables; - - internal const int MAP_COUNT = 16; - internal const int MAP_COUNT_SUFFIX_MASK = MAP_COUNT - 1; - internal ImMap[] _maps; - - internal static readonly object NoItem = new object(); - - private static StackPool[]> _mapsPool = new StackPool[]>(); - - private static ImMap[] _emptySlots = CreateEmptyMaps(); - - private static ImMap[] CreateEmptyMaps() - { - var empty = ImMap.Empty; - var slots = new ImMap[MAP_COUNT]; - for (var i = 0; i < MAP_COUNT; ++i) - slots[i] = empty; - return slots; - } - - /// Creates scope with optional parent and name. - public Scope(IScope parent = null, object name = null) - : this(parent, name, /*_mapsPool.RentOrDefault() ??*/ CreateEmptyMaps(), ImHashMap.Empty, - ImList.Empty, ImMap.Empty) - { } - - private Scope(IScope parent, object name, ImMap[] maps, ImHashMap instances, - ImList unorderedDisposables, ImMap disposables) - { - Parent = parent; - Name = name; - _unorderedDisposables = unorderedDisposables; - _disposables = disposables; - _factories = ImHashMap.Empty; - _maps = maps; - } - - /// - public IScope Clone() - { - var slotsCopy = new ImMap[MAP_COUNT]; - - // todo: we need a way to copy all Refs in the slot - do we? - for (var i = 0; i < MAP_COUNT; i++) - slotsCopy[i] = _maps[i]; - - return new Scope(Parent, Name, slotsCopy, _factories, _unorderedDisposables, _disposables); - } - - /// - [MethodImpl((MethodImplOptions)256)] - public object GetOrAdd(int id, CreateScopedValue createValue, int disposalOrder = 0) - { - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != NoItem) - return itemRef.Value; - return TryGetOrAdd(ref map, id, createValue, disposalOrder); - } - - private object TryGetOrAdd(ref ImMap map, int id, CreateScopedValue createValue, int disposalOrder = 0) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef.Value; - - // lock on the ref itself to set its `Item` field - lock (itemRef) - { - // double-check if the item was changed in between (double check locking) - if (itemRef.Value != NoItem) - return itemRef.Value; - itemRef.Value = createValue(); - } - - if (itemRef.Value is IDisposable disp && disp != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disp); - else - AddDisposable(disp, disposalOrder); - - return itemRef.Value; - } - - /// - [MethodImpl((MethodImplOptions)256)] - public object GetOrAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder = 0) - { - var itemRef = _maps[id & MAP_COUNT_SUFFIX_MASK].GetEntryOrDefault(id); - return itemRef != null && itemRef.Value != NoItem - ? itemRef.Value - : TryGetOrAddViaFactoryDelegate(id, createValue, r, disposalOrder); - } - - internal static readonly MethodInfo GetOrAddViaFactoryDelegateMethod = - typeof(IScope).GetTypeInfo().GetDeclaredMethod(nameof(IScope.GetOrAddViaFactoryDelegate)); - - internal ImMapEntry TryAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef; - - lock (itemRef) - { - if (itemRef.Value != NoItem) - return itemRef; - itemRef.Value = createValue(r); - } - - if (itemRef.Value is IDisposable disp && disp != this) - AddDisposable(disp, disposalOrder); - - return itemRef; - } - - internal object TryGetOrAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder = 0) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef.Value; - - lock (itemRef) - { - if (itemRef.Value != NoItem) - return itemRef.Value; - itemRef.Value = createValue(r); - } - - if (itemRef.Value is IDisposable disp && disp != this) - { - if (disposalOrder == 0) - AddUnorderedDisposable(disp); - else - AddDisposable(disp, disposalOrder); - } - - return itemRef.Value; - } - - /// - public object TryGetOrAddWithoutClosure(int id, - IResolverContext resolveContext, Expression expr, bool useFec, - Func createValue, int disposalOrder = 0) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef.Value; - - lock (itemRef) - { - if (itemRef.Value != NoItem) - return itemRef.Value; - itemRef.Value = createValue(resolveContext, expr, useFec); - } - - if (itemRef.Value is IDisposable disposable && disposable != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disposable); - else - AddDisposable(disposable, disposalOrder); - - return itemRef.Value; - } - - ///[Obsolete("Removing because it is used only by obsolete `UseInstance` feature")] - public void SetOrAdd(int id, object item) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, x => x.AddOrKeep(id, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return; - - // lock on the ref itself to set its `Item` field - lock (itemRef) - { - // double-check if the item was changed in between (double check locking) - if (itemRef.Value != NoItem) - return; - // we can simple assign because we are under the lock - itemRef.Value = item; - } - - if (item is IDisposable disp && disp != this) - AddUnorderedDisposable(disp); - } - - ///[Obsolete("Removing because it is not used")] - public object GetOrTryAdd(int id, object newItem, int disposalOrder) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != NoItem) - return itemRef.Value; - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, x => x.AddOrUpdate(id, NoItem)); - - itemRef = map.GetEntryOrDefault(id); - - // lock on the ref itself to set its `Item` field - lock (itemRef) - { - // double-check if the item was changed in between (double check locking) - if (itemRef.Value != NoItem) - return itemRef.Value; - - // we can simple assign because we are under the lock - itemRef.Value = newItem; - } - - if (itemRef.Value is IDisposable disp && disp != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disp); - else - AddDisposable(disp, disposalOrder); - - return itemRef.Value; - } - - internal void AddDisposable(IDisposable disposable, int disposalOrder) - { - var d = _disposables; - if (Interlocked.CompareExchange(ref _disposables, d.AddOrUpdate(disposalOrder, disposable), d) != d) - Ref.Swap(ref _disposables, disposalOrder, disposable, (x, dispOrder, disp) => x.AddOrUpdate(dispOrder, disp)); - } - - [MethodImpl((MethodImplOptions)256)] - internal void AddUnorderedDisposable(IDisposable disposable) - { - var copy = _unorderedDisposables; - if (Interlocked.CompareExchange(ref _unorderedDisposables, copy.Push(disposable), copy) != copy) - Ref.Swap(ref _unorderedDisposables, disposable, (x, d) => x.Push(d)); - } - - /// - [MethodImpl((MethodImplOptions)256)] - public bool TryGet(out object item, int id) - { - var itemRef = _maps[id & MAP_COUNT_SUFFIX_MASK].GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != NoItem) - { - item = itemRef.Value; - return true; - } - - item = null; - return false; - } - - // todo: consider adding the overload without `disposalOrder` - /// Can be used to manually add service for disposal - public object TrackDisposable(object item, int disposalOrder = 0) - { - if (item is IDisposable disposable && disposable != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disposable); - else - AddDisposable(disposable, disposalOrder); - return item; - } - - internal static readonly MethodInfo TrackDisposableMethod = - typeof(IScope).GetTypeInfo().GetDeclaredMethod(nameof(IScope.TrackDisposable)); - - /// Add instance to the small registry via factory - public void SetUsedInstance(Type type, FactoryDelegate factory) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - var f = _factories; - var hash = RuntimeHelpers.GetHashCode(type); - if (Interlocked.CompareExchange(ref _factories, f.AddOrUpdate(hash, type, factory), f) != f) - Ref.Swap(ref _factories, hash, type, factory, (x, h, t, fac) => x.AddOrUpdate(h, t, fac)); - } - - /// Try retrieve instance from the small registry. - public bool TryGetUsedInstance(IResolverContext r, Type type, out object instance) - { - instance = null; - if (_disposed == 1) - return false; - - if (!_factories.IsEmpty) - { - var factory = _factories.GetValueOrDefault(RuntimeHelpers.GetHashCode(type), type); - if (factory != null) - { - instance = factory(r); - return true; - } - } - - return Parent?.TryGetUsedInstance(r, type, out instance) ?? false; - } - - /// Enumerates all the parent scopes upwards starting from this one. - public IEnumerator GetEnumerator() - { - for (IScope scope = this; scope != null; scope = scope.Parent) - yield return scope; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// Disposes all stored objects and empties item storage. - /// The disposal happens in REVERSE resolution / injection order, consumer first, dependency next. - /// It will allow consumer to do something with its dependency before it is disposed. - /// All disposal exceptions are swallowed except the ContainerException, - /// which may indicate container misconfiguration. - public void Dispose() - { - if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1) - return; - - if (!_disposables.IsEmpty) - SafelyDisposeOrderedDisposables(_disposables); - - for (var unordDisp = _unorderedDisposables; !unordDisp.IsEmpty; unordDisp = unordDisp.Tail) - unordDisp.Head.Dispose(); - - _unorderedDisposables = ImList.Empty; - _disposables = ImMap.Empty; - _factories = ImHashMap.Empty; - - var maps = Interlocked.Exchange(ref _maps, _emptySlots); - var empty = ImMap.Empty; - //for (int i = 0; i < MAP_COUNT; i++) maps[i] = empty; - //_mapsPool.Return(maps); - } - - private static void SafelyDisposeOrderedDisposables(ImMap disposables) - { - disposables.Visit(d => { - try - { - // Ignoring disposing exception, as it is not important to proceed the disposal of other items - d.Value.Dispose(); - } - catch (ContainerException) - { - throw; - } - catch (Exception) - { - } - }); - } - - /// Prints scope info (name and parent) to string for debug purposes. - public override string ToString() => - "{" + (IsDisposed ? "IsDisposed=true, " : "") - + (Name != null ? "Name=" + Name : "Name=null") - + (Parent != null ? ", Parent=" + Parent : "") - + "}"; - } - - /// Delegate to get new scope from old/existing current scope. - /// Old/existing scope to change. - /// New scope or old if do not want to change current scope. - public delegate IScope SetCurrentScopeHandler(IScope oldScope); - - /// Provides ambient current scope and optionally scope storage for container, - /// examples are HttpContext storage, Execution context, Thread local. - public interface IScopeContext : IDisposable - { - /// Returns current scope or null if no ambient scope available at the moment. - /// Current scope or null. - IScope GetCurrentOrDefault(); - - /// Changes current scope using provided delegate. Delegate receives current scope as input and - /// should return new current scope. - /// Delegate to change the scope. - /// Important: may be called multiple times in concurrent environment. - /// Make it predictable by removing any side effects. - /// New current scope. So it is convenient to use method in "using (var newScope = ctx.SetCurrent(...))". - IScope SetCurrent(SetCurrentScopeHandler setCurrentScope); - } - -#if NET35 || NET40 || NET403 || PCL || PCL328 || PCL259 - - /// Tracks one current scope per thread, so the current scope in different tread would be different or null, - /// if not yet tracked. Context actually stores scope references internally, so it should be disposed to free them. - public sealed class ThreadScopeContext : IScopeContext - { - /// Provides static name for context. It is OK because its constant. - public static readonly string ScopeContextName = "ThreadScopeContext"; - - /// Returns current scope in calling Thread or null, if no scope tracked. - public IScope GetCurrentOrDefault() => - _scopes.GetValueOrDefault(Portable.GetCurrentManagedThreadID()) as IScope; - - /// Change current scope for the calling Thread. - public IScope SetCurrent(SetCurrentScopeHandler setCurrentScope) - { - var threadId = Portable.GetCurrentManagedThreadID(); - IScope newScope = null; - Ref.Swap(ref _scopes, s => s.AddOrUpdate(threadId, - newScope = setCurrentScope(s.GetValueOrDefault(threadId) as IScope))); - return newScope; - } - - /// Disposes the scopes and empties internal scope storage. - public void Dispose() - { - if (!_scopes.IsEmpty) - _scopes.Visit(d => d.Value?.Dispose()); - _scopes = ImMap.Empty; - } - - /// Collection of scoped by their managed thread id - private ImMap _scopes = ImMap.Empty; - } - -#else - /// Tracks one current scope per thread, so the current scope in different tread would be different or null, - /// if not yet tracked. Context actually stores scope references internally, so it should be disposed to free them. - public sealed class ThreadScopeContext : IScopeContext - { - /// Provides static name for context. It is OK because its constant. - public static readonly string ScopeContextName = "ThreadScopeContext"; - - private ThreadLocal _scope = new ThreadLocal(true); - - /// Returns current scope in calling Thread or null, if no scope tracked. - public IScope GetCurrentOrDefault() => - _scope.Value; - - /// Change current scope for the calling Thread. - public IScope SetCurrent(SetCurrentScopeHandler setCurrentScope) => - _scope.Value = setCurrentScope(GetCurrentOrDefault()); - - /// Disposes the scopes and empties internal scope storage. - public void Dispose() - { - var scopes = _scope.Values; - foreach (var scope in scopes) - { - var s = scope; - while (s != null) - { - var x = s; - s = s.Parent; - x.Dispose(); - } - } - } - } - -#endif - - /// Simplified scope agnostic reuse abstraction. More easy to implement, - /// and more powerful as can be based on other storage beside reuse. - public interface IReuse : IConvertibleToExpression - { - /// Relative to other reuses lifespan value. - int Lifespan { get; } - - /// Optional name. Use to find matching scope by the name. - /// It also may be interpreted as object[] Names for matching with multiple scopes - object Name { get; } - - /// Returns true if reuse can be applied: may check if scope or other reused item storage is present. - bool CanApply(Request request); - - /// Returns composed expression. - Expression Apply(Request request, Expression serviceFactoryExpr); - } - - /// Returns container bound scope for storing singleton objects. - public sealed class SingletonReuse : IReuse - { - /// Big lifespan. - public const int DefaultLifespan = 1000; - - /// Relative to other reuses lifespan value. - public int Lifespan => DefaultLifespan; - - /// - public object Name => null; - - /// Returns true because singleton is always available. - public bool CanApply(Request request) => true; - - /// Returns expression call to GetOrAddItem. - public Expression Apply(Request request, Expression serviceFactoryExpr) - { - // this is required because we cannot use ValueType for the object - if (serviceFactoryExpr.Type.IsValueType()) - serviceFactoryExpr = Convert(serviceFactoryExpr, typeof(object)); - - if (request.TracksTransientDisposable) - return Call(ResolverContext.SingletonScopeExpr, Scope.TrackDisposableMethod, - serviceFactoryExpr, Constant(request.Factory.Setup.DisposalOrder)); - - return Call(ResolverContext.SingletonScopeExpr, Scope.GetOrAddViaFactoryDelegateMethod, - Constant(request.FactoryID), Lambda(serviceFactoryExpr, - FactoryDelegateCompiler.FactoryDelegateParamExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , typeof(object) -#endif - ), - FactoryDelegateCompiler.ResolverContextParamExpr, - Constant(request.Factory.Setup.DisposalOrder)); - } - - private static readonly Lazy _singletonReuseExpr = Lazy.Of(() => - Field(null, typeof(Reuse).Field(nameof(Reuse.Singleton)))); - - /// - public Expression ToExpression(Func fallbackConverter) => _singletonReuseExpr.Value; - - /// Pretty prints reuse name and lifespan - public override string ToString() => "Singleton {Lifespan=" + Lifespan + "}"; - } - - /// Specifies that instances are created, stored and disposed together with some scope. - public sealed class CurrentScopeReuse : IReuse - { - /// Less than Singleton's - public const int DefaultLifespan = 100; - - /// Relative to other reuses lifespan value. - public int Lifespan { get; } - - /// - public object Name { get; } - - /// Returns true if scope is open and the name is matching with reuse . - public bool CanApply(Request request) => - ScopedOrSingleton || - (Name == null ? request.Container.CurrentScope != null : request.Container.GetNamedScope(Name, false) != null); - - /// Creates scoped item creation and access expression. - public Expression Apply(Request request, Expression serviceFactoryExpr) - { - // strip the conversion as we are operating with object anyway - if (serviceFactoryExpr.NodeType == ExprType.Convert) - serviceFactoryExpr = ((UnaryExpression)serviceFactoryExpr).Operand; - - // this is required because we cannot use ValueType for the object - if (serviceFactoryExpr.Type.IsValueType()) - serviceFactoryExpr = Convert(serviceFactoryExpr, typeof(object)); - - var resolverContextParamExpr = FactoryDelegateCompiler.ResolverContextParamExpr; - - if (request.TracksTransientDisposable) - { - if (ScopedOrSingleton) - return Call(TrackScopedOrSingletonMethod, new[] { resolverContextParamExpr, serviceFactoryExpr }); - - var ifNoScopeThrowExpr = Constant(request.IfUnresolved == IfUnresolved.Throw); - if (Name == null) - return Call(TrackScopedMethod, new[] { resolverContextParamExpr, ifNoScopeThrowExpr, serviceFactoryExpr }); - - var nameExpr = request.Container.GetConstantExpression(Name, typeof(object)); - return Call(TrackNameScopedMethod, new[] { resolverContextParamExpr, nameExpr, ifNoScopeThrowExpr, serviceFactoryExpr }); - } - else - { - var idExpr = Constant(request.FactoryID); - - Expression factoryDelegateExpr; - if (serviceFactoryExpr is InvocationExpression ie && - ie.Expression is ConstantExpression registeredDelegateExpr && - registeredDelegateExpr.Type == typeof(FactoryDelegate)) - { - // optimization for the registered delegate - factoryDelegateExpr = registeredDelegateExpr; - } - else - { - factoryDelegateExpr = Lambda(serviceFactoryExpr, - FactoryDelegateCompiler.FactoryDelegateParamExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , typeof(object) -#endif - ); - } - - var disposalIndex = request.Factory.Setup.DisposalOrder; - - if (ScopedOrSingleton) - return Call(GetScopedOrSingletonViaFactoryDelegateMethod, - resolverContextParamExpr, idExpr, factoryDelegateExpr, Constant(disposalIndex)); - - var ifNoScopeThrowExpr = Constant(request.IfUnresolved == IfUnresolved.Throw); - - if (Name == null) - { - if (disposalIndex == 0) - return Call(GetScopedViaFactoryDelegateNoDisposalIndexMethod, - resolverContextParamExpr, ifNoScopeThrowExpr, idExpr, factoryDelegateExpr); - - return Call(GetScopedViaFactoryDelegateMethod, - resolverContextParamExpr, ifNoScopeThrowExpr, idExpr, factoryDelegateExpr, Constant(disposalIndex)); - } - - return Call(GetNameScopedViaFactoryDelegateMethod, resolverContextParamExpr, - request.Container.GetConstantExpression(Name, typeof(object)), - ifNoScopeThrowExpr, idExpr, factoryDelegateExpr, Constant(disposalIndex)); - } - } - - /// - public Expression ToExpression(Func fallbackConverter) => - Name == null && !ScopedOrSingleton - ? Field(null, typeof(Reuse).GetTypeInfo().GetDeclaredField(nameof(Reuse.Scoped))) - : ScopedOrSingleton - ? (Expression)Field(null, typeof(Reuse).GetTypeInfo().GetDeclaredField(nameof(Reuse.ScopedOrSingleton))) - : Call(typeof(Reuse).Method(nameof(Reuse.ScopedTo), typeof(object)), fallbackConverter(Name)); - - /// Pretty prints reuse to string. Reuse string. - public override string ToString() - { - var s = new StringBuilder(ScopedOrSingleton ? "ScopedOrSingleton {" : "Scoped {"); - if (Name != null) - s.Append("Name=").Print(Name).Append(", "); - if (Lifespan != DefaultLifespan) - s.Append("NON DEFAULT LIFESPAN=").Append(Lifespan); - else - s.Append("Lifespan=").Append(Lifespan); - - return s.Append("}").ToString(); - } - - /// Creates the reuse. - public CurrentScopeReuse(object name, bool scopedOrSingleton, int lifespan) - { - Name = name; - ScopedOrSingleton = scopedOrSingleton; - Lifespan = lifespan; - } - - /// Creates the reuse optionally specifying its name. - public CurrentScopeReuse(object name = null, bool scopedOrSingleton = false) - : this(name, scopedOrSingleton, DefaultLifespan) - { - } - - /// Flag indicating that it is a scope or singleton. - public readonly bool ScopedOrSingleton; - - /// [Obsolete("Replaced by `GetScopedOrSingletonViaFactoryDelegate`")] - public static object GetScopedOrSingleton(IResolverContext r, - int id, CreateScopedValue createValue, int disposalIndex) => - (r.CurrentScope ?? r.SingletonScope).GetOrAdd(id, createValue, disposalIndex); - - /// Subject - public static object GetScopedOrSingletonViaFactoryDelegate(IResolverContext r, - int id, FactoryDelegate createValue, int disposalIndex) => - (r.CurrentScope ?? r.SingletonScope).GetOrAddViaFactoryDelegate(id, createValue, r, disposalIndex); - - internal static readonly MethodInfo GetScopedOrSingletonViaFactoryDelegateMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetScopedOrSingletonViaFactoryDelegate)); - - /// Subject - public static object TrackScopedOrSingleton(IResolverContext r, object item) => - (r.CurrentScope ?? r.SingletonScope).TrackDisposable(item); - - internal static readonly MethodInfo TrackScopedOrSingletonMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(TrackScopedOrSingleton)); - - /// [Obsolete("Replaced by `GetScopedViaFactoryDelegate`")] - public static object GetScoped(IResolverContext r, - bool throwIfNoScope, int id, CreateScopedValue createValue, int disposalIndex) => - r.GetCurrentScope(throwIfNoScope)?.GetOrAdd(id, createValue, disposalIndex); - - /// Subject - public static object GetScopedViaFactoryDelegateNoDisposalIndex(IResolverContext r, - bool throwIfNoScope, int id, FactoryDelegate createValue) => - r.GetCurrentScope(throwIfNoScope)?.GetOrAddViaFactoryDelegate(id, createValue, r); - - internal static readonly MethodInfo GetScopedViaFactoryDelegateNoDisposalIndexMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetScopedViaFactoryDelegateNoDisposalIndex)); - - /// Subject - public static object GetScopedViaFactoryDelegate(IResolverContext r, - bool throwIfNoScope, int id, FactoryDelegate createValue, int disposalIndex) => - r.GetCurrentScope(throwIfNoScope)?.GetOrAddViaFactoryDelegate(id, createValue, r, disposalIndex); - - internal static readonly MethodInfo GetScopedViaFactoryDelegateMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetScopedViaFactoryDelegate)); - - /// [Obsolete("Replaced by `GetNameScopedViaFactoryDelegate`")] - public static object GetNameScoped(IResolverContext r, - object scopeName, bool throwIfNoScope, int id, CreateScopedValue createValue, int disposalIndex) => - r.GetNamedScope(scopeName, throwIfNoScope)?.GetOrAdd(id, createValue, disposalIndex); - - /// Subject - public static object GetNameScopedViaFactoryDelegate(IResolverContext r, - object scopeName, bool throwIfNoScope, int id, FactoryDelegate createValue, int disposalIndex) => - r.GetNamedScope(scopeName, throwIfNoScope)?.GetOrAddViaFactoryDelegate(id, createValue, r, disposalIndex); - - internal static readonly MethodInfo GetNameScopedViaFactoryDelegateMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetNameScopedViaFactoryDelegate)); - - /// Subject - public static object TrackScoped(IResolverContext r, bool throwIfNoScope, object item) => - r.GetCurrentScope(throwIfNoScope)?.TrackDisposable(item); - - internal static readonly MethodInfo TrackScopedMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(TrackScoped)); - - /// Subject - public static object TrackNameScoped(IResolverContext r, object scopeName, bool throwIfNoScope, object item) => - r.GetNamedScope(scopeName, throwIfNoScope)?.TrackDisposable(item); - - internal static readonly MethodInfo TrackNameScopedMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(TrackNameScoped)); - } - - /// Abstracts way to match reuse and scope names - public interface IScopeName - { - /// Does the job. - bool Match(object scopeName); - } - - /// Represents multiple names - public sealed class CompositeScopeName : IScopeName - { - /// Wraps multiple names - public static CompositeScopeName Of(object[] names) => new CompositeScopeName(names); - - /// Matches all the name in a loop until first match is found, otherwise returns false. - public bool Match(object scopeName) - { - var names = _names; - for (var i = 0; i < names.Length; i++) - { - var name = names[i]; - if (name == scopeName) - return true; - var aScopeName = name as IScopeName; - if (aScopeName != null && aScopeName.Match(scopeName)) - return true; - if (scopeName != null && scopeName.Equals(name)) - return true; - } - - return false; - } - - private CompositeScopeName(object[] names) { _names = names; } - private readonly object[] _names; - } - - /// Holds the name for the resolution scope. - public sealed class ResolutionScopeName : IScopeName - { - /// Creates scope with specified service type and key - public static ResolutionScopeName Of(Type serviceType = null, object serviceKey = null) => - new ResolutionScopeName(serviceType, serviceKey); - - /// Creates scope with specified service type and key. - public static ResolutionScopeName Of(object serviceKey = null) => - new ResolutionScopeName(typeof(TService), serviceKey); - - /// Type of service opening the scope. - public readonly Type ServiceType; - - /// Optional service key of service opening the scope. - public readonly object ServiceKey; - - private ResolutionScopeName(Type serviceType, object serviceKey) - { - ServiceType = serviceType; - ServiceKey = serviceKey; - } - - /// - public bool Match(object scopeName) - { - var name = scopeName as ResolutionScopeName; - return name != null && - (ServiceType == null || - name.ServiceType.IsAssignableTo(ServiceType) || - ServiceType.IsOpenGeneric() && - name.ServiceType.GetGenericDefinitionOrNull().IsAssignableTo(ServiceType)) && - (ServiceKey == null || ServiceKey.Equals(name.ServiceKey)); - } - - /// String representation for easy debugging and understood error messages. - public override string ToString() - { - var s = new StringBuilder().Append("{ServiceType=").Print(ServiceType); - if (ServiceKey != null) - s.Append(", ServiceKey=").Print(ServiceKey); - return s.Append('}').ToString(); - } - } - - /// Specifies pre-defined reuse behaviors supported by container: - /// used when registering services into container with methods. - public static class Reuse - { - /// Synonym for absence of reuse. - public static readonly IReuse Transient = new TransientReuse(); - - /// Specifies to store single service instance per . - public static readonly IReuse Singleton = new SingletonReuse(); - - /// Same as InCurrentScope. From now on will be the default name. - public static readonly IReuse Scoped = new CurrentScopeReuse(); - - /// Same as InCurrentNamedScope. From now on will be the default name. - public static IReuse ScopedTo(object name) => new CurrentScopeReuse(name); - - /// Specifies all the scope details - public static IReuse ScopedTo(object name, bool scopedOrSingleton, int lifespan) => - new CurrentScopeReuse(name, scopedOrSingleton, lifespan); - - // todo: Should be renamed to `ScopedToMany` to prevent overload ambiguity - /// Scoped to multiple names. - public static IReuse ScopedTo(params object[] names) => - names.IsNullOrEmpty() ? Scoped - : names.Length == 1 ? ScopedTo(names[0]) - : new CurrentScopeReuse(CompositeScopeName.Of(names)); - - // todo: Consider changing the name to remove the ambiguity - /// Same as InResolutionScopeOf. From now on will be the default name. - public static IReuse ScopedTo(Type serviceType = null, object serviceKey = null) => - serviceType == null && serviceKey == null ? Scoped - : new CurrentScopeReuse(ResolutionScopeName.Of(serviceType, serviceKey)); - - /// Same as InResolutionScopeOf. From now on will be the default name. - public static IReuse ScopedTo(object serviceKey = null) => - ScopedTo(typeof(TService), serviceKey); - - /// The same as but if no open scope available will fallback to - /// The is applied the same way as for reuse. - public static readonly IReuse ScopedOrSingleton = new CurrentScopeReuse(scopedOrSingleton: true); - - /// Obsolete: same as . - [Obsolete("The same as Reuse.Scoped, please prefer to use Reuse.Scoped or the Reuse.ScopedTo to specify a bound service.")] - public static readonly IReuse InResolutionScope = Scoped; - - /// Obsolete: same as . - public static readonly IReuse InCurrentScope = Scoped; - - /// Returns current scope reuse with specific name to match with scope. - /// If name is not specified then function returns . - /// (optional) Name to match with scope. - /// Created current scope reuse. - public static IReuse InCurrentNamedScope(object name = null) => ScopedTo(name); - - /// Obsolete: please use ScopedTo instead. - public static IReuse InResolutionScopeOf(Type assignableFromServiceType = null, object serviceKey = null) => - ScopedTo(assignableFromServiceType, serviceKey); - - /// Obsolete: please use ScopedTo instead. - public static IReuse InResolutionScopeOf(object serviceKey = null) => - ScopedTo(serviceKey); - - /// Same as Scoped but requires . - public static readonly IReuse InThread = Scoped; - - // todo: Minimize usage of name for scopes, it will be more performant. e.g. ASP.NET Core does not use one. - /// Special name that by convention recognized by . - public static string WebRequestScopeName = "~WebRequestScopeName"; - - /// Obsolete: please prefer using instead. - /// The named scope has performance drawback comparing to just a scope. - /// If you need to distinguish nested scope, give names to them instead of naming the top web request scope. - public static readonly IReuse InWebRequest = ScopedTo(WebRequestScopeName); - -#region Implementation - - private sealed class TransientReuse : IReuse - { - public int Lifespan => 0; - - public object Name => null; - - public Expression Apply(Request _, Expression serviceFactoryExpr) => serviceFactoryExpr; - - public bool CanApply(Request request) => true; - - private readonly Lazy _transientReuseExpr = Lazy.Of(() => - Field(null, typeof(Reuse).Field(nameof(Transient)))); - - public Expression ToExpression(Func fallbackConverter) => - _transientReuseExpr.Value; - - public override string ToString() => "TransientReuse"; - } - -#endregion - } - - /// Policy to handle unresolved service. - public enum IfUnresolved - { - /// If service is unresolved for whatever means, it will throw the respective exception. - Throw, - /// If service is unresolved for whatever means, it will return default(serviceType) value. - ReturnDefault, - /// If service is not registered, then it will return default, for other errors it will throw. - ReturnDefaultIfNotRegistered, - } - - /// Declares minimal API for service resolution. - /// Resolve default and keyed is separated because of optimization for faster resolution of the former. - public interface IResolver -#if SUPPORTS_ISERVICE_PROVIDER - : IServiceProvider -#endif - - { - /// Resolves default (non-keyed) service from container and returns created service object. - /// Service type to search and to return. - /// Says what to do if service is unresolved. - /// Created service object or default based on provided. - object Resolve(Type serviceType, IfUnresolved ifUnresolved); - - /// Resolves service instance from container. - /// Service type to search and to return. - /// (optional) service key used for registering service. - /// (optional) Says what to do if service is unresolved. - /// (optional) Registered or wrapped service type to use instead of , - /// or wrapped type for generic wrappers. The type should be assignable to return . - /// (optional) Dependency chain info. - /// (optional) To specify the dependency objects to use instead of resolving them from container. - /// Created service object or default based on parameter. - object Resolve(Type serviceType, object serviceKey, - IfUnresolved ifUnresolved, Type requiredServiceType, Request preResolveParent, object[] args); - - /// Resolves all services registered for specified , or if not found returns - /// empty enumerable. If specified then returns only (single) service registered with this type. - /// Return type of an service item. - /// (optional) Resolve only single service registered with the key. - /// (optional) Actual registered service to search for. - /// Dependency resolution path info. - /// (optional) To specify the dependency objects to use instead of resolving them from container. - /// Enumerable of found services or empty. Does Not throw if no service found. - IEnumerable ResolveMany(Type serviceType, object serviceKey, - Type requiredServiceType, Request preResolveParent, object[] args); - } - - /// Specifies options to handle situation when registered service is already present in the registry. - public enum IfAlreadyRegistered - { - /// Appends new default registration or throws registration with the same key. - AppendNotKeyed, - /// Throws if default or registration with the same key is already exist. - Throw, - /// Keeps old default or keyed registration ignoring new registration: ensures Register-Once semantics. - Keep, - /// Replaces old registration with new one. - Replace, - /// Adds the new implementation or null (Made.Of), - /// otherwise keeps the previous registration of the same implementation type. - AppendNewImplementation - } - - /// Existing registration info. - public struct ServiceRegistrationInfo : IComparable - { - /// Registered factory. - public Factory Factory; - - /// Required service type. - public Type ServiceType; - - /// May be null for single default service, or for multiple default services. - public object OptionalServiceKey; - - /// Provides registration order across all factory registrations in container. - /// May be the same for factory registered with multiple services - /// OR for closed-generic factories produced from the single open-generic registration. - public int FactoryRegistrationOrder => Factory.RegistrationOrder; - - /// Implementation type if available. - public Type ImplementationType => Factory.CanAccessImplementationType ? Factory.ImplementationType : null; - - /// Shortcut to property, useful to find all roots - public bool AsResolutionRoot => Factory.Setup.AsResolutionRoot; - - /// Shortcut to service info. - public ServiceInfo ToServiceInfo() => ServiceInfo.Of(ServiceType, serviceKey: OptionalServiceKey); - - /// Overrides the service type and pushes the original service type to required service type - public ServiceInfo ToServiceInfo(Type serviceType) => - ServiceInfo.Of(serviceType, ServiceType, IfUnresolved.Throw, OptionalServiceKey); - - /// Overrides the service type and pushes the original service type to required service type - public ServiceInfo ToServiceInfo() => ToServiceInfo(typeof(TService)); - - /// Creates info. Registration order is figured out automatically based on Factory. - public ServiceRegistrationInfo(Factory factory, Type serviceType, object optionalServiceKey) - { - Factory = factory; - ServiceType = serviceType; - OptionalServiceKey = optionalServiceKey; - } - - /// Orders by registration - public int CompareTo(ServiceRegistrationInfo other) => Factory.FactoryID - other.Factory.FactoryID; - - /// Pretty-prints info to string. - public override string ToString() - { - var s = new StringBuilder().Print(ServiceType); - if (OptionalServiceKey != null) - s.Append(" with ServiceKey=").Print(OptionalServiceKey); - return s.Append(" with factory ").Append(Factory).ToString(); - } - } - - /// Defines operations that for changing registry, and checking if something exist in registry. - public interface IRegistrator - { - /// Registers factory in registry with specified service type and key for lookup. - /// Returns true if factory was added to registry, false otherwise. False may be in case of - /// setting and already existing factory - /// To register. - /// Service type as unique key in registry for lookup. - /// Service key as complementary lookup for the same service type. - /// Policy how to deal with already registered factory with same service type and key. - /// [performance] Confirms that service and implementation types are statically checked by compiler. - /// True if factory was added to registry, false otherwise. - /// False may be in case of setting and already existing factory. - void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked); - - /// Returns true if expected factory is registered with specified service key and type. - /// Not provided or null means to check the - /// alone with any service key. - bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, Func condition); - - /// Removes factory with specified service type and key from registry and cache. - /// BUT consuming services may still hold on the resolved service instance. - /// The cache of consuming services may also hold on the unregistered service. Use `IContainer.ClearCache` to clear all cache. - void Unregister(Type serviceType, object serviceKey, FactoryType factoryType, Func condition); - - /// Returns all registered service factories with their Type and optional Key. - /// Decorator and Wrapper types are not included. - IEnumerable GetServiceRegistrations(); - - /// Searches for registered factories by type, and key (if specified), - /// and by factory type (by default uses ). - /// May return empty, 1 or multiple factories. - Factory[] GetRegisteredFactories(Type serviceType, object serviceKey, FactoryType factoryType); - - /// Puts instance into the current scope or singletons. - void UseInstance(Type serviceType, object instance, IfAlreadyRegistered IfAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey); - - /// Puts instance created via the passed factory on demand into the current or singleton scope - void Use(Type serviceType, FactoryDelegate factory); - } - - /// What to do with registrations when creating the new container from the existent one. - public enum RegistrySharing - { - /// Shares both registrations and resolution cache if any - Share = 0, - /// Clones the registrations but preserves the resolution cache - CloneButKeepCache, - /// Clones the registrations and drops the cache -- full reset! - CloneAndDropCache - } - - /// Combines registrator and resolver roles, plus rules and scope management. - public interface IContainer : IRegistrator, IResolverContext - { - /// Rules for defining resolution/registration behavior throughout container. - Rules Rules { get; } - - /// Represents scope bound to container itself, and not an ambient (context) thingy. - IScope OwnCurrentScope { get; } - - // todo: replace with the below overload with more parameters - /// Creates new container from the current one by specifying the listed parameters. - /// If the null or default values are provided then the default or new values will be applied. - /// Nothing will be inherited from the current container. - /// If you want to inherit something you need to provide it as parameter. - IContainer With(Rules rules, IScopeContext scopeContext, RegistrySharing registrySharing, IScope singletonScope); - - /// Creates new container from the current one by specifying the listed parameters. - /// If the null or default values are provided then the default or new values will be applied. - /// Nothing will be inherited from the current container. - /// If you want to inherit something you need to provide it as parameter. - IContainer With(IResolverContext parent, Rules rules, IScopeContext scopeContext, - RegistrySharing registrySharing, IScope singletonScope, IScope currentScope); - - /// Produces new container which prevents any further registrations. - /// (optional)Controls what to do with registrations: ignore or throw exception. - /// Throws exception by default. - /// New container preserving all current container state but disallowing registrations. - IContainer WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow = false); - - /// Searches for requested factory in registry, and then using . - /// Factory request. - /// Found factory, otherwise null if is set to . - Factory ResolveFactory(Request request); - - /// Searches for registered service factory and returns it, or null if not found. - /// Will use if specified. - /// Factory request. - /// Found factory or null. - Factory GetServiceFactoryOrDefault(Request request); - - /// Finds all registered default and keyed service factories and returns them. - /// It skips decorators and wrappers. - /// Service type to look for, may be open-generic type too. - /// (optional) For generic serviceType instructs to look for - /// both closed and open-generic registrations. - /// Enumerable of found pairs. - /// Returned Key item should not be null - it should be . - IEnumerable> GetAllServiceFactories(Type serviceType, bool bothClosedAndOpenGenerics = false); - - /// Searches for registered wrapper factory and returns it, or null if not found. - /// Service type to look for. Found wrapper factory or null. - Factory GetWrapperFactoryOrDefault(Type serviceType); - - /// Returns all decorators registered for the service type. Decorator factories. - Factory[] GetDecoratorFactoriesOrDefault(Type serviceType); - - /// Creates decorator expression: it could be either Func{TService,TService}, - /// or service expression for replacing decorators. - /// Decorated service request. - /// Decorator expression. - Expression GetDecoratorExpressionOrDefault(Request request); - - /// If is generic type then this method checks if the type registered as generic wrapper, - /// and recursively unwraps and returns its type argument. This type argument is the actual service type we want to find. - /// Otherwise, method returns the input . - /// Type to unwrap. Method will return early if type is not generic. - /// Required service type or null if don't care. - /// Unwrapped service type in case it corresponds to registered generic wrapper, or input type in all other cases. - Type GetWrappedType(Type serviceType, Type requiredServiceType); - - /// Converts known items into custom expression or wraps in a constant expression. - /// Item to convert. - /// (optional) Type of item, otherwise item . - /// (optional) Throws for non-primitive and not-recognized items, - /// identifying that result expression require run-time state. For compiled expression it means closure in lambda delegate. - /// Returns constant or state access expression for added items. - Expression GetConstantExpression(object item, Type itemType = null, bool throwIfStateRequired = false); - - /// Clears cache for specified service(s). But does not clear instances of already resolved/created singletons and scoped services! - /// Target service type. - /// (optional) If not specified, clears cache for all . - /// (optional) If omitted, the cache will be cleared for all registrations of . - /// True if target service was found, false - otherwise. - bool ClearCache(Type serviceType, FactoryType? factoryType, object serviceKey); - - /// Puts instance created via the passed factory on demand into the current or singleton scope - new void Use(Type serviceType, FactoryDelegate factory); - - /// [Obsolete("Replaced by `Use` to put runtime data into container scopes and with `RegisterInstance` as a sugar for `RegisterDelegate(_ => instance)`")] - new void UseInstance(Type serviceType, object instance, IfAlreadyRegistered IfAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey); - } - - /// Resolves all registered services of type on demand, - /// when enumerator called. If service type is not found, empty returned. - /// Service type to resolve. - public sealed class LazyEnumerable : IEnumerable - { - /// Exposes internal items enumerable. - public readonly IEnumerable Items; - - /// Wraps lazy resolved items. Lazy resolved items. - public LazyEnumerable(IEnumerable items) - { - Items = items.ThrowIfNull(); - } - - /// Return items enumerator. - public IEnumerator GetEnumerator() => Items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - /// Wrapper type to box service with associated arbitrary metadata object. - /// Service type. - /// Arbitrary metadata object type. - public sealed class Meta - { - /// Value or object with associated metadata. - public readonly T Value; - - /// Associated metadata object. Could be anything. - public readonly TMetadata Metadata; - - /// Boxes value and its associated metadata together. - public Meta(T value, TMetadata metadata) - { - Value = value; - Metadata = metadata; - } - } - - /// Exception that container throws in case of error. Dedicated exception type simplifies - /// filtering or catching container relevant exceptions from client code. -#if SUPPORTS_SERIALIZABLE - [Serializable] -#endif - [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "Not available in PCL.")] - public class ContainerException : InvalidOperationException - { - /// Error code of exception, possible values are listed in class. - public readonly int Error; - - /// Simplifies the access to the error name. - public string ErrorName => DryIoc.Error.NameOf(Error); - - /// Creates exception by wrapping and its message, - /// optionally with exception. - public static ContainerException Of(ErrorCheck errorCheck, int errorCode, - object arg0, object arg1 = null, object arg2 = null, object arg3 = null, Exception innerException = null) => - new ContainerException(errorCode, - string.Format(GetMessage(errorCheck, errorCode), Print(arg0), Print(arg1), Print(arg2), Print(arg3)), - innerException); - - /// Gets error message based on provided args. - protected static string GetMessage(ErrorCheck errorCheck, int errorCode) => - errorCode == -1 ? Throw.GetDefaultMessage(errorCheck) : DryIoc.Error.Messages[errorCode]; - - /// Prints argument for formatted message. To print. Printed string. - protected static string Print(object arg) => - arg == null ? string.Empty : new StringBuilder().Print(arg).ToString(); - - /// Creates exception with message describing cause and context of error, - /// and leading/system exception causing it. - public ContainerException(int errorCode, string message, Exception innerException) - : base($"code: {DryIoc.Error.NameOf(errorCode)}; message: {message}", innerException) - { - Error = errorCode; - } - - /// Creates exception with message describing cause and context of error. - public ContainerException(int error, string message) - : this(error, message, null) { } - -#if SUPPORTS_SERIALIZABLE - /// - protected ContainerException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - : base(info, context) {} -#endif - } - - /// Defines error codes and error messages for all DryIoc exceptions (DryIoc extensions may define their own.) - public static class Error - { - private static int _errorIndex = -1; - - /// List of error messages indexed with code. - public static readonly string[] Messages = new string[100]; - -#pragma warning disable 1591 // "Missing XML-comment" - public static readonly int - UnableToResolveUnknownService = Of( - "Unable to resolve {0}" + NewLine + - "Where no service registrations found" + NewLine + - " and no dynamic registrations found in {1} of Rules.DynamicServiceProviders" + NewLine + - " and nothing found in {2} of Rules.UnknownServiceResolvers"), - - UnableToResolveFromRegisteredServices = Of( - "Unable to resolve {0}" + NewLine + - " with normal and dynamic registrations:" + NewLine + "{1}"), - - ExpectedSingleDefaultFactory = Of( - "Expecting single default registration but found many:" + NewLine + "{0}" + NewLine + - "when resolving {1}." + NewLine + - "Please identify service with key, or metadata, or use Rules.WithFactorySelector to specify single registered factory."), - - RegisteringImplementationNotAssignableToServiceType = Of( - "Registering implementation type {0} is not assignable to service type {1}."), - RegisteredFactoryMethodResultTypesIsNotAssignableToImplementationType = Of( - "Registered factory method return type {1} should be assignable Or castable to implementation type {0} but it is not."), - RegisteringOpenGenericRequiresFactoryProvider = Of( - "Unable to register delegate factory for open-generic service {0}." + NewLine + - "You need to specify concrete (closed) service type returned by delegate."), - RegisteringOpenGenericImplWithNonGenericService = Of( - "Unable to register open-generic implementation {0} with non-generic service {1}."), - RegisteringOpenGenericServiceWithMissingTypeArgs = Of( - "Unable to register open-generic implementation {0} because service {1} should specify all type arguments, but specifies only {2}."), - RegisteringNotAGenericTypedefImplType = Of( - "Unsupported registration of implementation {0} which is not a generic type definition but contains generic parameters." + NewLine + - "Consider to register generic type definition {1} instead."), - RegisteringNotAGenericTypedefServiceType = Of( - "Unsupported registration of service {0} which is not a generic type definition but contains generic parameters." + NewLine + - "Consider to register generic type definition {1} instead."), - RegisteringNullImplementationTypeAndNoFactoryMethod = Of( - "Registering without implementation type and without FactoryMethod to use instead."), - RegisteringObjectTypeAsImplementationIsNotSupported = Of( - "Registering `System.Object` type as implementation without a factory method is not supported."), - RegisteringAbstractImplementationTypeAndNoFactoryMethod = Of( - "Registering abstract implementation type {0} when it is should be concrete. Also there is not FactoryMethod to use instead."), - UnableToSelectSinglePublicConstructorFromMultiple = Of( - "Unable to select single public constructor from implementation type {0}:" + NewLine + - "{1}"), - UnableToSelectSinglePublicConstructorFromNone = Of( - "Unable to select single public constructor from implementation type {0} because it does not have one."), - NoMatchedImplementedTypesWithServiceType = Of( - "Unable to match service with open-generic {0} implementing {1} when resolving {2}."), - NoMatchedFactoryMethodDeclaringTypeWithServiceTypeArgs = Of( - "Unable to match open-generic factory method Declaring type {0} with requested service type arguments <{1}> when resolving {2}."), - NoMatchedFactoryMethodWithServiceTypeArgs = Of( - "Unable to match open-generic factory method {0} with requested service type arguments <{1}> when resolving {2}."), - OpenGenericFactoryMethodDeclaringTypeIsNotSupportedOnThisPlatform = Of( - "[Specific to this .NET version] Unable to match method or constructor {0} from open-generic declaring type {1} to closed-generic type {2}, " + - NewLine + - "Please give the method an unique name to distinguish it from other overloads."), - ResolvingOpenGenericServiceTypeIsNotPossible = Of( - "Resolving open-generic service type is not possible for type: {0}."), - RecursiveDependencyDetected = Of( - "Recursive dependency is detected when resolving" + NewLine + "{0}."), - ScopeIsDisposed = Of( - "Scope {0} is disposed and scoped instances are disposed and no longer available."), - NotFoundOpenGenericImplTypeArgInService = Of( - "Unable to find for open-generic implementation {0} the type argument {1} when resolving {2}."), - UnableToSelectCtor = Of( - "Unable to get constructor of {0} using provided constructor selector when resolving {1}."), - UnableToFindCtorWithAllResolvableArgs = Of( - "Unable to find most resolvable constructor also including passed input arguments `{0}` " + - NewLine + " when resolving: {1}."), - RegisteredDelegateResultIsNotOfServiceType = Of( - "Registered factory delegate returns service {0} is not assignable to desired service {1}."), - NotFoundSpecifiedWritablePropertyOrField = Of( - "Unable to find writable property or field {0} when resolving: {1}."), - PushingToRequestWithoutFactory = Of( - "Pushing the next request {0} into parent request not yet resolved to factory: {1}"), - NoMatchedGenericParamConstraints = Of( - "Open-generic service does not match with registered open-generic implementation constraints {0} when resolving: {1}."), - GenericWrapperWithMultipleTypeArgsShouldSpecifyArgIndex = Of( - "Generic wrapper type {0} should specify what type argument is wrapped, but it does not."), - GenericWrapperTypeArgIndexOutOfBounds = Of( - "Registered generic wrapper {0} specified type argument index {1} is out of type argument list."), - DependencyHasShorterReuseLifespan = Of( - "Dependency {0} with reuse {1} has shorter lifespan than its parent's {2}" + NewLine + - "If you know what you're doing you may disable this error with the rule `new Container(rules => rules.WithoutThrowIfDependencyHasShorterReuseLifespan())`."), - WeakRefReuseWrapperGCed = Of( - "Reused service wrapped in WeakReference is Garbage Collected and no longer available."), - ServiceIsNotAssignableFromFactoryMethod = Of( - "Service of {0} is not assignable from factory method {1} when resolving: {2}."), - GotNullConstructorFromFactoryMethod = Of( - "Got null constructor when resolving {0}"), - UnableToRegisterDuplicateDefault = Of( - "The default service {0} without key {1} is already registered as {2}."), - UnableToRegisterDuplicateKey = Of( - "Unable to register service with duplicate key '{0}': {1}" + NewLine + - " There is already registered service with the same key {2}."), - NoCurrentScope = Of( - "No current scope is available: probably you are registering to, or resolving from outside of the scope. " + NewLine + - "Current resolver context is: {0}."), - ContainerIsDisposed = Of( - "Container is disposed and should not be used: {0}"), - NoMatchedScopeFound = Of( - "Unable to find matching scope with name {0} starting from the current scope {1}."), - NotSupportedMadeOfExpression = Of( - "Expected expression of method call, property getter, or new statement (with optional property initializer), " + - "but found this Made.Of expression: {0}"), - UnexpectedFactoryMemberExpressionInMadeOf = Of( - "Expected property getter, but found not supported `{0}` " + NewLine + - "in Made.Of expression: `{1}`"), - UnexpectedExpressionInsteadOfArgMethodInMadeOf = Of( - "Expected `Arg.Of` method call to specify parameter, property or field, but found `{0}` " + NewLine + - "in Made.Of expression: `{1}`"), - UnexpectedExpressionInsteadOfConstantInMadeOf = Of( - "Expected `ConstantExpression` for value of parameter, property, or field, but found `{0}` " + NewLine + - "in Made.Of expression: `{1}`"), - InjectedCustomValueIsOfDifferentType = Of( - "Injected value {0} is not assignable to {1} when resolving: {2}"), - NoConversionOperatorFoundWhenInterpretingTheConvertExpression = Of( - "There is no explicit or implicit conversion operator found when interpreting {0} to {1} in expression: {2}"), - StateIsRequiredToUseItem = Of( - "Runtime state is required to inject (or use) the: {0}. " + NewLine + - "The reason is using RegisterDelegate, Use (or UseInstance), RegisterInitializer/Disposer, or registering with non-primitive service key, or metadata." + NewLine + - "You can convert run-time value to expression via container.With(rules => rules.WithItemToExpressionConverter(YOUR_ITEM_TO_EXPRESSION_CONVERTER))."), - ArgValueIndexIsProvidedButNoArgValues = Of( - "`Arg.Index` is provided but no values are passed in Made.Of expression: " + NewLine + - "{0}"), - ArgValueIndexIsOutOfProvidedArgValues = Of( - "`Arg.Index` {0} is outside of provided values [{1}] in Made.Of expression: " + NewLine + - "{2}"), - ResolutionNeedsRequiredServiceType = Of( - "Expecting required service type but it is not specified when resolving: {0}"), - RegisterMappingNotFoundRegisteredService = Of( - "When registering mapping, Container is unable to find factory of registered service type {0} and key {1}."), - RegisterMappingUnableToSelectFromMultipleFactories = Of( - "RegisterMapping selected more than 1 factory with provided type {0} and key {1}: {2}"), - RegisteringInstanceNotAssignableToServiceType = Of( - "Registered instance of type {0} is not assignable to serviceType {1}."), - NoMoreRegistrationsAllowed = Of( - "Container does not allow further registrations." + NewLine + - "Attempting to register {0}{1} with implementation factory {2}."), - NoMoreUnregistrationsAllowed = Of( - "Container does not allow further registry modification." + NewLine + - "Attempting to Unregister {0}{1} with factory type {2}."), - GotNullFactoryWhenResolvingService = Of( - "Got null factory method when resolving {0}"), - RegisteredDisposableTransientWontBeDisposedByContainer = Of( - "Registered Disposable Transient service {0} with key {1} registered as {2} won't be disposed by container." + - " DryIoc does not hold reference to resolved transients, and therefore does not control their dispose." + - " To silence this exception Register(setup: Setup.With(allowDisposableTransient: true)) " + - " or set the rule Container(rules => rules.WithoutThrowOnRegisteringDisposableTransient())." + - " To enable tracking use Register(setup: Setup.With(trackDisposableTransient: true)) " + - " or set the rule Container(rules => rules.WithTrackingDisposableTransients())"), - NotFoundMetaCtorWithTwoArgs = Of( - "Expecting Meta wrapper public constructor with two arguments {0} but not found when resolving: {1}"), - UnableToSelectFromManyRegistrationsWithMatchingMetadata = Of( - "Unable to select from multiple registrations matching the Metadata type {0}:" + NewLine + - "{1}" + NewLine + - "When resolving: {2}"), - ImplTypeIsNotSpecifiedForAutoCtorSelection = Of( - "Implementation type is not specified when using automatic constructor selection: {0}"), - NoImplementationForPlaceholder = Of( - "There is no real implementation, only a placeholder for the service {0}." + NewLine + - "Please Register the implementation with the ifAlreadyRegistered.Replace parameter to fill the placeholder."), - UnableToFindSingletonInstance = Of( - "Expecting the instance to be stored in singleton scope, but unable to find anything here." + NewLine + - "Likely, you've called UseInstance from the scoped container, but resolving from another container or injecting into a singleton."), - DecoratorShouldNotBeRegisteredWithServiceKey = Of( - "Registering Decorator {0} with service key {1} is not supported," + NewLine + - "because instead of decorator with the key you actually want a decorator for service registered with the key." + NewLine + - "To apply decorator for service with the key, please use `Setup.DecoratorOf(decorateeServiceKey: \"a service key\")`"), - PassedCtorOrMemberIsNull = Of( - "The constructor of member info passed to `Made.Of` or `FactoryMethod.Of` is null"), - PassedMemberIsNotStaticButInstanceFactoryIsNull = Of( - "The member info {0} passed to `Made.Of` or `FactoryMethod.Of` is NOT static, but instance factory is not provided or null"), - PassedMemberIsStaticButInstanceFactoryIsNotNull = Of( - "You are passing constructor or STATIC member info {0} to `Made.Of` or `FactoryMethod.Of`, but then why are you passing factory INSTANCE: {1}"), - UndefinedMethodWhenGettingTheSingleMethod = Of( - "Undefined Method '{0}' in Type {1} (including non-public={2})"), - UndefinedMethodWhenGettingMethodWithSpecifiedParameters = Of( - "Undefined Method '{0}' in Type {1} with parameters {2}."), - UndefinedPropertyWhenGettingProperty = Of("Undefined property {0} in type {1}"), - UndefinedFieldWhenGettingField = Of("Undefined field {0} in type {1}"), - UnableToFindConstructorWithArgs = Of("Unable to find a constructor in Type {0} with args: {1}"), - UnableToFindSingleConstructor = Of( - "Unable to find a single constructor in Type {0} (including non-public={1})"), - DisposerTrackForDisposeError = Of("Something is {0} already."), - NoServicesWereRegisteredByRegisterMany = Of( - "No service types were discovered in `RegisterMany` (or in `RegisterInstanceMany`) for the specified implementation types: " + NewLine + - "[{0}]" + NewLine + - "Maybe you missed the implementation or service type(s), " + - "e.g. provided only abstract or compiler-generated implementation types, " + - "or specified a wrong `serviceTypeCondition`," + - "or did not specify to use `nonPublicServiceTypes`, etc."), - FoundNoRootsToValidate = Of( - "No roots to Validate found. Check the `condition` passed to Validate method for container: {0}" + NewLine + - "You may also examine all container registrations via `container.container.GetServiceRegistrations()` method."), - UnableToInterpretTheNestedLambda = Of( - "Unable to interpret the nested lambda with Body:" + NewLine + - "{0}") - ; - -#pragma warning restore 1591 // "Missing XML-comment" - - private static int Of(string message) - { - var errorIndex = Interlocked.Increment(ref _errorIndex); - Messages[errorIndex] = message; - return errorIndex; - } - - /// Returns the name of error with the provided error code. - public static string NameOf(int error) => - typeof(Error).GetTypeInfo().DeclaredFields - .Where(f => f.FieldType == typeof(int)).Where((_, i) => i == error + 1) - .FirstOrDefault()?.Name; - - static Error() - { - Throw.GetMatchedException = ContainerException.Of; - } - } - - /// Checked error condition, possible error sources. - public enum ErrorCheck - { - /// Unspecified, just throw. - Unspecified, - /// Predicate evaluated to false. - InvalidCondition, - /// Checked object is null. - IsNull, - /// Checked object is of unexpected type. - IsNotOfType, - /// Checked type is not assignable to expected type - TypeIsNotOfType, - /// Invoked operation throws, it is source of inner exception. - OperationThrows, - } - - /// Enables more clean error message formatting and a bit of code contracts. - public static class Throw - { - private static string[] CreateDefaultMessages() - { - var messages = new string[(int)ErrorCheck.OperationThrows + 1]; - messages[(int)ErrorCheck.Unspecified] = "The error reason is unspecified, which is bad thing."; - messages[(int)ErrorCheck.InvalidCondition] = "Argument {0} of type {1} has invalid condition."; - messages[(int)ErrorCheck.IsNull] = "Argument of type {0} is null."; - messages[(int)ErrorCheck.IsNotOfType] = "Argument {0} is not of type {1}."; - messages[(int)ErrorCheck.TypeIsNotOfType] = "Type argument {0} is not assignable from type {1}."; - messages[(int)ErrorCheck.OperationThrows] = "Invoked operation throws the inner exception {0}."; - return messages; - } - - private static readonly string[] _defaultMessages = CreateDefaultMessages(); - - /// Returns the default message specified for code. - public static string GetDefaultMessage(ErrorCheck error) => - _defaultMessages[(int)error]; - - /// Declares mapping between type and code to specific . - public delegate Exception GetMatchedExceptionHandler(ErrorCheck errorCheck, int error, object arg0, object arg1, object arg2, object arg3, Exception inner); - - /// Returns matched exception for error check and error code. - public static GetMatchedExceptionHandler GetMatchedException = ContainerException.Of; - - /// Throws matched exception with provided error code if throw condition is true. - public static void If(bool throwCondition, int error = -1, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - if (throwCondition) - throw GetMatchedException(ErrorCheck.InvalidCondition, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws matched exception with provided error code if throw condition is true. - /// Otherwise returns source . - public static T ThrowIf(this T arg0, bool throwCondition, int error = -1, object arg1 = null, object arg2 = null, object arg3 = null) - { - if (!throwCondition) return arg0; - throw GetMatchedException(ErrorCheck.InvalidCondition, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws exception if is null, otherwise returns . - public static T ThrowIfNull(this T arg, int error = -1, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - where T : class - { - if (arg != null) return arg; - throw GetMatchedException(ErrorCheck.IsNull, error, arg0 ?? typeof(T), arg1, arg2, arg3, null); - } - - /// Throws exception if is not assignable to type specified by , - /// otherwise just returns . - public static T ThrowIfNotInstanceOf(this T arg0, Type arg1, int error = -1, object arg2 = null, object arg3 = null) - where T : class - { - if (arg1.IsTypeOf(arg0)) return arg0; - throw GetMatchedException(ErrorCheck.IsNotOfType, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws if is not assignable from . - public static Type ThrowIfNotImplementedBy(this Type arg0, Type arg1, int error = -1, object arg2 = null, object arg3 = null) - { - if (arg1.IsAssignableTo(arg0)) return arg0; - throw GetMatchedException(ErrorCheck.TypeIsNotOfType, error, arg0, arg1, arg2, arg3, null); - } - - /// Invokes and in case of re-throws it as inner-exception. - public static T IfThrows(Func operation, bool throwCondition, int error, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) where TEx : Exception - { - try - { - return operation(); - } - catch (TEx ex) - { - if (throwCondition) - throw GetMatchedException(ErrorCheck.OperationThrows, error, arg0, arg1, arg2, arg3, ex); - return default(T); - } - } - - /// Just throws the exception with the code. - public static object It(int error, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - throw GetMatchedException(ErrorCheck.Unspecified, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws instead of returning value of . - /// Supposed to be used in expression that require some return value. - public static T For(int error, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - throw GetMatchedException(ErrorCheck.Unspecified, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws if contidion is true, otherwise returns the `default(T)` value - public static T For(bool throwCondition, int error, - object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - if (!throwCondition) return default(T); - throw GetMatchedException(ErrorCheck.Unspecified, error, arg0, arg1, arg2, arg3, null); - } - } - - /// Called from generated code. - public static class ThrowInGeneratedCode - { - /// Throws if object is null. - public static object WeakRefReuseWrapperGCed(this object obj) - { - if (obj == null) Throw.It(Error.WeakRefReuseWrapperGCed); - return obj; - } - } - - /// Contains helper methods to work with Type: for instance to find Type implemented base types and interfaces, etc. - public static class ReflectionTools - { -#if SUPPORTS_DELEGATE_METHOD - private static Lazy> _preserveExceptionStackTraceAction = new Lazy>(() => - typeof(Exception).GetSingleMethodOrNull("InternalPreserveStackTrace", true) - ?.To(x => x.CreateDelegate(typeof(Action)).To>())); - - /// Preserves the stack trace becfore re-throwing. - public static void TryRethrowWithPreservedStackTrace(this Exception ex) - { - _preserveExceptionStackTraceAction.Value?.Invoke(ex); - throw ex; - } -#else - /// Preserves the stack trace becfore re-throwing. - public static void TryRethrowWithPreservedStackTrace(this Exception ex) - { - throw ex; - } -#endif - - /// Flags for method. - [Flags] - public enum AsImplementedType - { - /// Include nor object not source type. - None = 0, - /// Include source type to list of implemented types. - SourceType = 1, - /// Include type to list of implemented types. - ObjectType = 2 - } - - /// Returns all interfaces and all base types (in that order) implemented by . - /// Specify to include itself as first item and - /// type as the last item. - public static Type[] GetImplementedTypes(this Type sourceType, AsImplementedType asImplementedType = AsImplementedType.None) - { - Type[] results; - - var sourceTypeInfo = sourceType.GetTypeInfo(); - var interfaces = sourceTypeInfo.ImplementedInterfaces.ToArrayOrSelf(); - var interfaceStartIndex = (asImplementedType & AsImplementedType.SourceType) == 0 ? 0 : 1; - var includingObjectType = (asImplementedType & AsImplementedType.ObjectType) == 0 ? 0 : 1; - var sourcePlusInterfaceCount = interfaceStartIndex + interfaces.Length; - - var baseType = sourceTypeInfo.BaseType; - if (baseType == null || baseType == typeof(object)) - results = new Type[sourcePlusInterfaceCount + includingObjectType]; - else - { - List baseBaseTypes = null; - for (var bb = baseType.GetTypeInfo().BaseType; bb != null && bb != typeof(object); bb = bb.GetTypeInfo().BaseType) - (baseBaseTypes ?? (baseBaseTypes = new List(2))).Add(bb); - - if (baseBaseTypes == null) - results = new Type[sourcePlusInterfaceCount + includingObjectType + 1]; - else - { - results = new Type[sourcePlusInterfaceCount + baseBaseTypes.Count + includingObjectType + 1]; - baseBaseTypes.CopyTo(results, sourcePlusInterfaceCount + 1); - } - - results[sourcePlusInterfaceCount] = baseType; - } - - if (interfaces.Length == 1) - results[interfaceStartIndex] = interfaces[0]; - else if (interfaces.Length > 1) - Array.Copy(interfaces, 0, results, interfaceStartIndex, interfaces.Length); - - if (interfaceStartIndex == 1) - results[0] = sourceType; - if (includingObjectType == 1) - results[results.Length - 1] = typeof(object); - - return results; - } - - /// Gets a collection of the interfaces implemented by the current type and its base types. - public static Type[] GetImplementedInterfaces(this Type type) => - type.GetTypeInfo().ImplementedInterfaces.ToArrayOrSelf(); - - /// Gets all declared and if specified, the base members too. - public static IEnumerable GetAllMembers(this Type type, bool includeBase = false) => - type.GetMembers(t => - t.DeclaredMethods.Cast().Concat( - t.DeclaredProperties.Cast().Concat( - t.DeclaredFields.Cast())), - includeBase); - - /// Returns true if the contains all generic parameters - /// from . - public static bool ContainsAllGenericTypeParameters(this Type openGenericType, Type[] genericParameters) - { - if (!openGenericType.IsOpenGeneric()) - return false; - - var matchedParams = new Type[genericParameters.Length]; - Array.Copy(genericParameters, 0, matchedParams, 0, genericParameters.Length); - - ClearGenericParametersReferencedInConstraints(matchedParams); - ClearMatchesFoundInGenericParameters(matchedParams, openGenericType.GetGenericParamsAndArgs()); - - for (var i = 0; i < matchedParams.Length; i++) - if (matchedParams[i] != null) - return false; - return true; - } - - /// Returns true if class is compiler generated. Checking for CompilerGeneratedAttribute - /// is not enough, because this attribute is not applied for classes generated from "async/await". - public static bool IsCompilerGenerated(this Type type) => - type.FullName != null && type.FullName.Contains("<>c__DisplayClass"); - - /// Returns true if type is generic. - public static bool IsGeneric(this Type type) => - type.GetTypeInfo().IsGenericType; - - /// Returns true if type is generic type definition (open type). - public static bool IsGenericDefinition(this Type type) => - type.GetTypeInfo().IsGenericTypeDefinition; - - /// Returns true if type is closed generic: does not have open generic parameters, only closed/concrete ones. - public static bool IsClosedGeneric(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsGenericType && - !typeInfo.IsGenericTypeDefinition && - AreAllTypeArgumentsClosed(typeInfo.GetGenericParamsAndArgsUnsafe()); - } - - /// Returns true if type if open generic: contains at list one open generic parameter. Could be - /// generic type definition as well. - public static bool IsOpenGeneric(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsGenericTypeDefinition || - typeInfo.IsGenericType && - !AreAllTypeArgumentsClosed(typeInfo.GetGenericParamsAndArgsUnsafe()); - } - - /// Checks that all type args are closed. - internal static bool AreAllTypeArgumentsClosed(this Type[] typeArgs) - { - foreach (var typeArg in typeArgs) - { - if (typeArg.IsGenericParameter) - return false; - var typeArgInfo = typeArg.GetTypeInfo(); - if (!typeArgInfo.IsGenericType) - continue; - if (!AreAllTypeArgumentsClosed(typeArgInfo.GetGenericParamsAndArgsUnsafe())) - return false; - } - - return true; - } - - /// Returns generic type definition if type is generic and null otherwise. - public static Type GetGenericDefinitionOrNull(this Type type) => - type != null && type.GetTypeInfo().IsGenericType ? type.GetGenericTypeDefinition() : null; - - /// Returns generic type parameters and arguments in order they specified. If type is not generic, returns empty array. - public static Type[] GetGenericParamsAndArgs(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsGenericTypeDefinition || typeInfo.IsGenericType - ? typeInfo.GetGenericParamsAndArgsUnsafe() - : Empty(); - } - - [MethodImpl((MethodImplOptions)256)] - internal static Type[] GetGenericParamsAndArgsUnsafe(this TypeInfo typeInfo) - { -#if PCL || PCL259 || PCL328 || NET35 || NET40 || NET403 || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 - return typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters : typeInfo.GenericTypeArguments; -#else - return typeInfo.GetGenericArguments(); -#endif - } - - /// Returns array of interface and base class constraints for provider generic parameter type. - public static Type[] GetGenericParamConstraints(this Type type) => - type.GetTypeInfo().GetGenericParameterConstraints(); - - /// If type is array returns is element type, otherwise returns null. - /// Source type. Array element type or null. - public static Type GetArrayElementTypeOrNull(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsArray ? typeInfo.GetElementType() : null; - } - - /// Return base type or null, if not exist (the case for only for object type). - public static Type GetBaseType(this Type type) => - type.GetTypeInfo().BaseType; - - /// Checks if type is public or nested public in public type. - public static bool IsPublicOrNestedPublic(this Type type) - { - var ti = type.GetTypeInfo(); - return ti.IsPublic || ti.IsNestedPublic && ti.DeclaringType.IsPublicOrNestedPublic(); - } - - /// Returns true if type is class. - public static bool IsClass(this Type type) => - type.GetTypeInfo().IsClass; - - /// Returns true if type is value type. - public static bool IsValueType(this Type type) => - type.GetTypeInfo().IsValueType; - - /// Returns true if type is interface. - public static bool IsInterface(this Type type) => - type.GetTypeInfo().IsInterface; - - /// Returns true if type if abstract or interface. - public static bool IsAbstract(this Type type) => - type.GetTypeInfo().IsAbstract; - - /// Returns true if type is static. - public static bool IsStatic(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsAbstract && typeInfo.IsSealed; - } - - /// Returns true if type is enum type. - public static bool IsEnum(this Type type) => - type.GetTypeInfo().IsEnum; - - /// Returns true if type can be casted with conversion operators. - public static bool HasConversionOperatorTo(this Type sourceType, Type targetType) => - (sourceType.FindConvertOperator(sourceType, targetType) ?? - targetType.FindConvertOperator(sourceType, targetType)) != null; - - /// Returns `target source.op_(Explicit|Implicit)(source)` or null if not found - public static MethodInfo GetSourceConversionOperatorToTarget(this Type sourceType, Type targetType) => - sourceType.FindConvertOperator(sourceType, targetType); - - /// Returns `target target.op_(Explicit|Implicit)(source)` or null if not found - public static MethodInfo GetTargetConversionOperatorFromSource(this Type sourceType, Type targetType) => - targetType.FindConvertOperator(sourceType, targetType); - - internal static MethodInfo FindConvertOperator(this Type type, Type sourceType, Type targetType) - { - var methods = type.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsStatic && m.IsSpecialName && m.ReturnType == targetType) - { - var n = m.Name; - // n == "op_Implicit" || n == "op_Explicit" - if (n.Length == 11 && - n[2] == '_' && n[5] == 'p' && n[6] == 'l' && n[7] == 'i' && n[8] == 'c' && n[9] == 'i' && n[10] == 't' && - m.GetParameters()[0].ParameterType == sourceType) - return m; - } - } - - return null; - } - - /// Returns true if type is assignable to type. - public static bool IsAssignableTo(this Type type, Type other) => - type != null && other != null && other.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()); - - /// Returns true if type is assignable to type. - public static bool IsAssignableTo(this Type type) => - type != null && typeof(T).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()); - - /// Returns true if type of is assignable to source . - public static bool IsTypeOf(this Type type, object obj) => - obj != null && obj.GetType().IsAssignableTo(type); - - /// Returns true if provided type IsPrimitive in .Net terms, or enum, or string, - /// or array of primitives if is true. - public static bool IsPrimitive(this Type type, bool orArrayOfPrimitives = false) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsPrimitive || typeInfo.IsEnum || type == typeof(string) - || orArrayOfPrimitives && typeInfo.IsArray && typeInfo.GetElementType().IsPrimitive(true); - } - - /// Returns all attributes defined on . - public static Attribute[] GetAttributes(this Type type, Type attributeType = null, bool inherit = false) => - type.GetTypeInfo().GetCustomAttributes(attributeType ?? typeof(Attribute), inherit) - // ReSharper disable once RedundantEnumerableCastCall - .Cast() // required in .NET 4.5 - .ToArrayOrSelf(); - - /// Recursive method to enumerate all input type and its base types for specific details. - /// Details are returned by delegate. - public static IEnumerable GetMembers( - this Type type, Func> getMembers, bool includeBase = false) - { - var typeInfo = type.GetTypeInfo(); - var members = getMembers(typeInfo); - if (!includeBase || typeInfo.BaseType == null || typeInfo.BaseType == typeof(object)) - return members; - return members.Append(typeInfo.BaseType.GetMembers(getMembers, true)); - } - - /// Returns all public instance constructors for the type - public static IEnumerable PublicConstructors(this Type type) - { - foreach (var x in type.GetTypeInfo().DeclaredConstructors) - if (x.IsPublic && !x.IsStatic) - yield return x; - } - - /// Returns all public instance constructors for the type - public static IEnumerable PublicAndInternalConstructors(this Type type) - { - foreach (var x in type.GetTypeInfo().DeclaredConstructors) - if (!x.IsPrivate && !x.IsStatic) - yield return x; - } - - //todo: [Obsolete("Prefer to use `GetInstanceConstructors` and `GetPublicInstanceConstructors`")] - /// Enumerates all constructors from input type. - public static IEnumerable Constructors(this Type type, - bool includeNonPublic = false, bool includeStatic = false) - { - var ctors = type.GetTypeInfo().DeclaredConstructors.ToArrayOrSelf(); - if (ctors.Length == 0) - return ctors; - - var ctor0 = ctors[0]; - var skip0 = !includeNonPublic && !ctor0.IsPublic || !includeStatic && ctor0.IsStatic; - if (ctors.Length == 1) - return skip0 ? ArrayTools.Empty() : ctors; - - if (ctors.Length == 2) - { - var ctor1 = ctors[1]; - var skip1 = !includeNonPublic && !ctor1.IsPublic || !includeStatic && ctor1.IsStatic; - if (skip0 && skip1) - return ArrayTools.Empty(); - if (skip0) - return new[] { ctor1 }; - if (skip1) - return new[] { ctor0 }; - return ctors; - } - - if (!includeNonPublic && !includeStatic) - return ctors.Match(x => !x.IsStatic && x.IsPublic); - if (!includeNonPublic) - return ctors.Match(x => x.IsPublic); - if (!includeStatic) - return ctors.Match(x => !x.IsStatic); - return ctors; - } - - /// Returns public and non-public instance constructors. - public static ConstructorInfo[] GetInstanceConstructors(this Type type) - { - var ctorsEnumerable = type.GetTypeInfo().DeclaredConstructors; - var ctors = ctorsEnumerable as ConstructorInfo[] ?? ctorsEnumerable.ToArray(); - if (ctors.Length == 0) - return ctors; - - var ctor0 = ctors[0]; - if (ctors.Length == 1) - return ctor0.IsStatic ? ArrayTools.Empty() : ctors; - - if (ctors.Length == 2) - { - var ctor1 = ctors[1]; - if (ctor0.IsStatic && ctor1.IsStatic) - return ArrayTools.Empty(); - if (ctor0.IsStatic) - return new[] { ctor1 }; - if (ctor1.IsStatic) - return new[] { ctor0 }; - return ctors; - } - - return ctors.MatchUnsafe(x => !x.IsStatic); - } - - /// Returns public-only instance constructors. - public static ConstructorInfo[] GetPublicInstanceConstructors(this Type type) - { - var ctorsEnumerable = type.GetTypeInfo().DeclaredConstructors; - var ctors = ctorsEnumerable as ConstructorInfo[] ?? ctorsEnumerable.ToArray(); - if (ctors.Length == 0) - return ctors; - - var ctor0 = ctors[0]; - var skip0 = !ctor0.IsPublic || ctor0.IsStatic; - if (ctors.Length == 1) - return skip0 ? ArrayTools.Empty() : ctors; - - if (ctors.Length == 2) - { - var ctor1 = ctors[1]; - var skip1 = !ctor1.IsPublic || ctor1.IsStatic; - if (skip0 && skip1) - return ArrayTools.Empty(); - if (skip0) - return new[] { ctor1 }; - if (skip1) - return new[] { ctor0 }; - return ctors; - } - - return ctors.MatchUnsafe(x => !x.IsStatic && x.IsPublic); - } - - /// Searches and returns the first constructor by its signature, e.g. with the same number of parameters of the same type. - public static ConstructorInfo GetConstructorOrNull(this Type type, bool includeNonPublic = false, params Type[] args) - { - var argsLength = args.Length; - var ctors = includeNonPublic ? type.GetInstanceConstructors() : type.GetPublicInstanceConstructors(); - foreach (var ctor in ctors) - { - var ctorParams = ctor.GetParameters(); - if (ctorParams.Length == argsLength) - { - var i = 0; - for (; i < argsLength; ++i) - { - var paramType = ctorParams[i].ParameterType; - if (paramType != args[i] && paramType.GetGenericDefinitionOrNull() != args[i]) - break; - } - - if (i == argsLength) - return ctor; - } - } - - return null; - } - - /// Searches and returns constructor by its signature. - public static ConstructorInfo GetConstructorOrNull(this Type type, params Type[] args) => - type.GetConstructorOrNull(true, args); - - /// Searches and returns constructor by its signature, or throws if not found - public static ConstructorInfo Constructor(this Type type, params Type[] args) => - type.GetConstructorOrNull(includeNonPublic: true, args: args).ThrowIfNull(Error.UnableToFindConstructorWithArgs, type, args); - - /// Returns single constructor otherwise (if no constructor or more than one) returns null. - public static ConstructorInfo GetSingleConstructorOrNull(this Type type, bool includeNonPublic = false) - { - ConstructorInfo foundCtor = null; - if (includeNonPublic) - { - foreach (var ctor in type.GetTypeInfo().DeclaredConstructors) - { - if (!ctor.IsStatic) - { - if (foundCtor != null) - return null; - foundCtor = ctor; - } - } - } - else - { - foreach (var ctor in type.GetTypeInfo().DeclaredConstructors) - { - if (!ctor.IsStatic && ctor.IsPublic) - { - if (foundCtor != null) - return null; - foundCtor = ctor; - } - } - } - return foundCtor; - } - - /// Returns single constructor otherwise (if no or more than one) throws an exception - public static ConstructorInfo SingleConstructor(this Type type, bool includeNonPublic = false) => - type.GetSingleConstructorOrNull(includeNonPublic).ThrowIfNull(Error.UnableToFindSingleConstructor, type, includeNonPublic); - - /// Looks up for single declared method with the specified name. Returns null if method is not found. - public static MethodInfo GetSingleMethodOrNull(this Type type, string name, bool includeNonPublic = false) - { - MethodInfo foundMethod = null; - if (includeNonPublic) - { - foreach (var method in type.GetTypeInfo().DeclaredMethods) - if (method.Name == name) - { - if (foundMethod != null) - return null; - foundMethod = method; - } - } - else - { - foreach (var method in type.GetTypeInfo().DeclaredMethods) - if (method.IsPublic && method.Name == name) - { - if (foundMethod != null) - return null; - foundMethod = method; - } - } - return foundMethod; - } - - /// Looks for single declared (not inherited) method by name, and throws if not found. - public static MethodInfo SingleMethod(this Type type, string name, bool includeNonPublic = false) => - type.GetSingleMethodOrNull(name, includeNonPublic).ThrowIfNull( - Error.UndefinedMethodWhenGettingTheSingleMethod, name, type, includeNonPublic); - - /// Looks up for method with and specified parameter types. - public static MethodInfo Method(this Type type, string name, params Type[] args) => - type.GetMethodOrNull(name, args).ThrowIfNull( - Error.UndefinedMethodWhenGettingMethodWithSpecifiedParameters, name, type, args); - - /// Looks up for method with and specified parameter types. - public static MethodInfo GetMethodOrNull(this Type type, string name, params Type[] paramTypes) - { - var pTypesCount = paramTypes.Length; - var methods = type.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - foreach (var method in methods) - { - if (method.Name == name) - { - var ps = method.GetParameters(); - if (ps.Length == pTypesCount) - { - var p = 0; - for (; p < pTypesCount; ++p) - { - var paramType = ps[p].ParameterType; - if (paramType != paramTypes[p] && paramType.GetGenericDefinitionOrNull() != paramTypes[p]) - break; - } - - if (p == pTypesCount) - return method; - } - } - } - - return null; - } - - /// Returns property by name, including inherited. Or null if not found. - public static PropertyInfo Property(this Type type, string name, bool includeBase = false) => - type.GetPropertyOrNull(name, includeBase).ThrowIfNull(Error.UndefinedPropertyWhenGettingProperty, name, type); - - /// Returns property by name, including inherited. Or null if not found. - public static PropertyInfo GetPropertyOrNull(this Type type, string name, bool includeBase = false) - { - var props = type.GetTypeInfo().DeclaredProperties.ToArrayOrSelf(); - for (var i = 0; i < props.Length; i++) - { - var p = props[i]; - if (p.Name == name) - return p; - } - - return !includeBase ? null : type.GetTypeInfo().BaseType?.GetPropertyOrNull(name, includeBase); - } - - /// Returns field by name, including inherited. Or null if not found. - public static FieldInfo Field(this Type type, string name, bool includeBase = false) => - type.GetFieldOrNull(name, includeBase).ThrowIfNull(Error.UndefinedFieldWhenGettingField, name, type); - - /// Returns field by name, including inherited. Or null if not found. - public static FieldInfo GetFieldOrNull(this Type type, string name, bool includeBase = false) - { - var fields = type.GetTypeInfo().DeclaredFields.ToArrayOrSelf(); - for (var i = 0; i < fields.Length; i++) - { - var f = fields[i]; - if (f.Name == name) - return f; - } - - return !includeBase ? null : type.GetTypeInfo().BaseType?.GetFieldOrNull(name, includeBase); - } - - /// Returns type assembly. - public static Assembly GetAssembly(this Type type) => type.GetTypeInfo().Assembly; - - /// Is true for interface declared property explicitly implemented, e.g. IInterface.Prop - public static bool IsExplicitlyImplemented(this PropertyInfo property) => property.Name.Contains("."); - - /// Returns true if member is static, otherwise returns false. - public static bool IsStatic(this MemberInfo member) => - member is MethodInfo method ? method.IsStatic - : member is FieldInfo field ? field.IsStatic - : member is PropertyInfo prop && !prop.IsExplicitlyImplemented() && prop.IsStatic(true); - - /// Find if property is static - public static bool IsStatic(this PropertyInfo property, bool includeNonPublic = false) - { - // e.g.: set_Blah or get_Blah - var propName = property.Name; - var methods = property.DeclaringType.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var index = 0; index < methods.Length; index++) - { - var m = methods[index]; - if (m.IsSpecialName && (includeNonPublic || m.IsPublic)) - { - var name = m.Name; - var nameLength = name.Length; - if (nameLength > 4 && name[3] == '_' && nameLength - 4 == propName.Length) - { - var i = 4; - for (var j = 0; i < nameLength; i++, j++) - if (name[i] != propName[j]) - break; - if (i == nameLength) - return m.IsStatic; - } - } - } - - return false; - } - - /// Return either , or , - /// . - public static Type GetReturnTypeOrDefault(this MemberInfo member) => - member is ConstructorInfo ? member.DeclaringType - : (member as MethodInfo) ?.ReturnType - ?? (member as PropertyInfo)?.PropertyType - ?? (member as FieldInfo) ?.FieldType; - - /// Returns true if field is backing field for property. - public static bool IsBackingField(this FieldInfo field) => - field.Name[0] == '<'; - - /// Returns true if property is indexer: aka this[]. - public static bool IsIndexer(this PropertyInfo property) => - property.GetIndexParameters().Length != 0; - - /// Returns true if type is generated type of hoisted closure. - public static bool IsClosureType(this Type type) => - type.Name.Contains("<>c__DisplayClass"); - - /// Returns attributes defined for the member/method. - public static IEnumerable GetAttributes(this MemberInfo member, Type attributeType = null, bool inherit = false) => - member.GetCustomAttributes(attributeType ?? typeof(Attribute), inherit).Cast(); - - /// Returns attributes defined for parameter. - public static IEnumerable GetAttributes(this ParameterInfo parameter, Type attributeType = null, bool inherit = false) => - parameter.GetCustomAttributes(attributeType ?? typeof(Attribute), inherit).Cast(); - - /// Get types from assembly that are loaded successfully. - /// Hacks the to get failing to load types metadata. - public static Type[] GetLoadedTypes(this Assembly assembly) - { - try - { - return Portable.GetAssemblyTypes(assembly).ToArrayOrSelf(); - } - catch (ReflectionTypeLoadException ex) - { - return ex.Types.Where(type => type != null).ToArray(); - } - } - - private static void ClearGenericParametersReferencedInConstraints(Type[] genericParams) - { - for (var i = 0; i < genericParams.Length; i++) - { - var genericParam = genericParams[i]; - if (genericParam == null) - continue; - - var genericConstraints = genericParam.GetGenericParamConstraints(); - for (var j = 0; j < genericConstraints.Length; j++) - { - var genericConstraint = genericConstraints[j]; - if (genericConstraint.IsOpenGeneric()) - { - var constraintGenericParams = genericConstraint.GetGenericParamsAndArgs(); - for (var k = 0; k < constraintGenericParams.Length; k++) - { - var constraintGenericParam = constraintGenericParams[k]; - if (constraintGenericParam != genericParam) - { - for (var g = 0; g < genericParams.Length; ++g) - if (genericParams[g] == constraintGenericParam) - { - genericParams[g] = null; // match - break; - } - } - } - } - } - } - } - - private static void ClearMatchesFoundInGenericParameters(Type[] matchedParams, Type[] genericParams) - { - for (var i = 0; i < genericParams.Length; i++) - { - var genericParam = genericParams[i]; - if (genericParam.IsGenericParameter) - { - for (var j = 0; j < matchedParams.Length; ++j) - if (matchedParams[j] == genericParam) - { - matchedParams[j] = null; // match - break; - } - } - else if (genericParam.IsOpenGeneric()) - ClearMatchesFoundInGenericParameters(matchedParams, genericParam.GetGenericParamsAndArgs()); - } - } - - internal static T GetDefault() => default(T); - internal static readonly MethodInfo GetDefaultMethod = - typeof(ReflectionTools).SingleMethod(nameof(GetDefault), true); - - /// Creates default(T) expression for provided . - public static Expression GetDefaultValueExpression(this Type type) => - !type.IsValueType() ? Constant(null, type) : (Expression)Call(GetDefaultMethod.MakeGenericMethod(type), Empty()); - } - - /// Provides pretty printing/debug view for number of types. - public static class PrintTools - { - /// Default separator used for printing enumerable. - public static string DefaultItemSeparator = ", " + NewLine; - - /// Prints input object by using corresponding Print methods for know types. - /// Builder to append output to. Object to print. - /// (optional) Quote to use for quoting string object. - /// (optional) Separator for enumerable. - /// (optional) Custom type printing policy. - /// String builder with appended output. - public static StringBuilder Print(this StringBuilder s, object x, - string quote = "\"", string itemSeparator = null, Func getTypeName = null) => - x == null ? s.Append("null") - : x is string ? s.Print((string)x, quote) - : x is Type ? s.Print((Type)x, getTypeName) - : x is IPrintable ? ((IPrintable)x).Print(s, (b, p) => b.Print(p, quote, itemSeparator, getTypeName)) - : x is IScope || x is Request ? s.Append(x) // prevent recursion for IEnumerable - : x.GetType().IsEnum() ? s.Print(x.GetType()).Append('.').Append(Enum.GetName(x.GetType(), x)) - : (x is IEnumerable || x is IEnumerable) && - !x.GetType().IsAssignableTo(typeof(IEnumerable<>).MakeGenericType(x.GetType())) // exclude infinite recursion and StackOverflowEx - ? s.Print((IEnumerable)x, itemSeparator ?? DefaultItemSeparator, (_, o) => _.Print(o, quote, null, getTypeName)) - : s.Append(x); - - /// Appends string to string builder quoting with if provided. - /// String builder to append string to. String to print. - /// (optional) Quote to add before and after string. - /// String builder with appended string. - public static StringBuilder Print(this StringBuilder s, string str, string quote = "\"") => - quote == null ? s.Append(str) : s.Append(quote).Append(str).Append(quote); - - /// Prints enumerable by using corresponding Print method for known item type. - /// String builder to append output to. - /// Items to print. - /// (optional) Custom separator if provided. - /// (optional) Custom item printer if provided. - /// String builder with appended output. - public static StringBuilder Print(this StringBuilder s, IEnumerable items, - string separator = ", ", Action printItem = null) - { - if (items == null) - return s; - printItem = printItem ?? ((_, x) => _.Print(x)); - var itemCount = 0; - foreach (var item in items) - printItem(itemCount++ == 0 ? s : s.Append(separator), item); - return s; - } - - /// Default delegate to print Type details: by default prints Type FullName and - /// skips namespace if it start with "System." - public static Func GetTypeNameDefault = t => -#if DEBUG - t.Name; -#else - t.FullName != null && t.Namespace != null && !t.Namespace.StartsWith("System") ? t.FullName : t.Name; -#endif - - /// Pretty prints the in proper C# representation. - /// Allows to specify if you want Name instead of FullName. - public static StringBuilder Print(this StringBuilder s, Type type, Func getTypeName = null) - { - if (type == null) - return s; - - var isArray = type.IsArray; - if (isArray) - type = type.GetElementType(); - - var typeName = (getTypeName ?? GetTypeNameDefault).Invoke(type); - - if (!type.IsGeneric()) - return s.Append(typeName.Replace('+', '.')); - - var tickIndex = typeName.IndexOf('`'); - if (tickIndex != -1) - typeName = typeName.Substring(0, tickIndex); - - s.Append(typeName.Replace('+', '.')); - - s.Append('<'); - var genericArgs = type.GetGenericParamsAndArgs(); - if (type.IsGenericDefinition()) - s.Append(',', genericArgs.Length - 1); - else - s.Print(genericArgs, ", ", (b, t) => b.Print((Type)t, getTypeName)); - s.Append('>'); - - if (isArray) - s.Append("[]"); - - return s; - } - - /// Pretty-prints the type - public static string Print(this Type type, Func getTypeName = null) => - new StringBuilder().Print(type, getTypeName).ToString(); - } - - /// Ports some methods from .Net 4.0/4.5 - public static partial class Portable - { - /// Portable version of Assembly.GetTypes or Assembly.DefinedTypes. - public static IEnumerable GetAssemblyTypes(Assembly a) => _getAssemblyTypes.Value(a); - - private static readonly Lazy>> _getAssemblyTypes = Lazy.Of(GetAssemblyTypesMethod); - private static Func> GetAssemblyTypesMethod() - { - var asmExpr = Parameter(typeof(Assembly), "a"); - - var asmDefinedTypesProperty = typeof(Assembly).GetPropertyOrNull("DefinedTypes"); - - var typesExpr = asmDefinedTypesProperty == null - ? Call(asmExpr, typeof(Assembly).SingleMethod("GetTypes"), Empty()) - : asmDefinedTypesProperty.PropertyType == typeof(IEnumerable) - ? Call(typeof(Portable).SingleMethod(nameof(ToTypes), true), Property(asmExpr, asmDefinedTypesProperty)) - : (Expression)Property(asmExpr, asmDefinedTypesProperty); - - return FastExpressionCompiler.LightExpression.ExpressionCompiler.CompileFast( - Lambda>>(typesExpr, asmExpr)); - } - - internal static IEnumerable ToTypes(IEnumerable x) => x.Select(t => t.AsType()); - - /// Portable version of PropertyInfo.GetGetMethod. - public static MethodInfo GetGetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) - { - var name = "get_" + p.Name; - var methods = p.DeclaringType.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && (includeNonPublic || m.IsPublic) && m.Name == name) - return m; - } - - return null; - } - - /// Portable version of PropertyInfo.GetSetMethod. - public static MethodInfo GetSetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) - { - var name = "set_" + p.Name; - var methods = p.DeclaringType.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && (includeNonPublic || m.IsPublic) && m.Name == name) - return m; - } - - return null; - } - - private static readonly Lazy> _getEnvCurrentManagedThreadId = Lazy.Of(() => - { - var method = typeof(Environment).GetMethodOrNull("get_CurrentManagedThreadId", Empty()); - if (method == null) - return null; - - return Lambda>(Call(method, Empty()), Empty()) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToLambdaExpression() -#endif - .Compile(); - }); - - /// Returns managed Thread ID either from Environment or Thread.CurrentThread whichever is available. - [MethodImpl((MethodImplOptions)256)] - public static int GetCurrentManagedThreadID() - { - var resultID = -1; - GetCurrentManagedThreadID(ref resultID); - if (resultID == -1) - resultID = _getEnvCurrentManagedThreadId.Value(); - return resultID; - } - - static partial void GetCurrentManagedThreadID(ref int threadID); - } -} // end of DryIoc namespace - -#if SUPPORTS_ASYNC_LOCAL -namespace DryIoc -{ - using System.Threading; - - /// Stores scopes propagating through async-await boundaries. - public sealed class AsyncExecutionFlowScopeContext : IScopeContext - { - /// Statically known name of root scope in this context. - public static readonly string ScopeContextName = typeof(AsyncExecutionFlowScopeContext).FullName; - - /// It is fine to use a default instance, cause the async local scope are actually a static one - public static readonly AsyncExecutionFlowScopeContext Default = new AsyncExecutionFlowScopeContext(); - - private static readonly AsyncLocal _ambientScope = new AsyncLocal(); - - /// Returns current scope or null if no ambient scope available at the moment. - /// Current scope or null. - public IScope GetCurrentOrDefault() => _ambientScope.Value; - - /// Changes current scope using provided delegate. Delegate receives current scope as input and should return new current scope. - /// Delegate to change the scope. - /// Important: may be called multiple times in concurrent environment. - /// Make it predictable by removing any side effects. - /// New current scope. It is convenient to use method in "using (var newScope = ctx.SetCurrent(...))". - public IScope SetCurrent(SetCurrentScopeHandler changeCurrentScope) => - _ambientScope.Value = changeCurrentScope(GetCurrentOrDefault()); - - /// Nothing to dispose. - public void Dispose() { } - } -} -#endif -#if !SUPPORTS_ASYNC_LOCAL && SUPPORTS_SERIALIZABLE && !NETSTANDARD2_0 -namespace DryIoc -{ - using System; - using System.Threading; - - /// Stores scopes propagating through async-await boundaries. - public sealed class AsyncExecutionFlowScopeContext : IScopeContext - { - /// Statically known name of root scope in this context. - public static readonly string ScopeContextName = typeof(AsyncExecutionFlowScopeContext).FullName; - - /// It is fine to use a default instance, cause the async local scope are actually a static one - public static readonly AsyncExecutionFlowScopeContext Default = new AsyncExecutionFlowScopeContext(); - - [Serializable] - internal sealed class ScopeEntry : MarshalByRefObject - { - public readonly T Value; - public ScopeEntry(T value) { Value = value; } - } - - private static int _seedKey; - private readonly string _scopeEntryKey = ScopeContextName + Interlocked.Increment(ref _seedKey); - - /// Returns current scope or null if no ambient scope available at the moment. - /// Current scope or null. - public IScope GetCurrentOrDefault() => - ((ScopeEntry)System.Runtime.Remoting.Messaging.CallContext.LogicalGetData(_scopeEntryKey))?.Value; - - /// Changes current scope using provided delegate. Delegate receives current scope as input and should return new current scope. - /// Delegate to change the scope. - /// Important: may be called multiple times in concurrent environment. - /// Make it predictable by removing any side effects. - /// New current scope. It is convenient to use method in "using (var newScope = ctx.SetCurrent(...))". - public IScope SetCurrent(SetCurrentScopeHandler changeCurrentScope) - { - var newScope = changeCurrentScope(GetCurrentOrDefault()); - var scopeEntry = newScope == null ? null : new ScopeEntry(newScope); - System.Runtime.Remoting.Messaging.CallContext.LogicalSetData(_scopeEntryKey, scopeEntry); - return newScope; - } - - /// Nothing to dispose. - public void Dispose() { } - } -} -#endif - -#if !SUPPORTS_STACK_TRACE -namespace DryIoc -{ - internal class StackTrace - { - public override string ToString() => ""; - } -} -#endif -#if PCL328 || NET35 || NET40 || NET403 -namespace DryIoc -{ - using System.Threading; - - /// Something for portability - public static partial class Portable - { - // ReSharper disable once RedundantAssignment - static partial void GetCurrentManagedThreadID(ref int threadID) - { - threadID = Thread.CurrentThread.ManagedThreadId; - } - } -} - -namespace System.Reflection -{ - using Collections.Generic; - using Linq; - - /// Provides for the type. - public static class TypeInfoTools - { - /// Wraps input type into structure. - /// Input type. Type info wrapper. - public static TypeInfo GetTypeInfo(this Type type) => new TypeInfo(type); - } - - /// Partial analog of TypeInfo existing in .NET 4.5 and higher. - public struct TypeInfo - { - private readonly Type _type; - - /// Creates type info by wrapping input type. Type to wrap. - public TypeInfo(Type type) - { - _type = type; - } - -#pragma warning disable 1591 // "Missing XML-comment" - public Type AsType() => _type; - - public Assembly Assembly => _type.Assembly; - - public MethodInfo GetDeclaredMethod(string name) => _type.GetMethod(name); - public PropertyInfo GetDeclaredProperty(string name) => _type.GetProperty(name); - public FieldInfo GetDeclaredField(string name) => _type.GetField(name); - - public IEnumerable DeclaredConstructors => - _type.GetConstructors(ALL_DECLARED ^ BindingFlags.Static); - - public IEnumerable DeclaredMembers => - _type.GetMembers(ALL_DECLARED); - - public IEnumerable DeclaredMethods => - _type.GetMethods(ALL_DECLARED); - - public IEnumerable DeclaredFields => - _type.GetFields(ALL_DECLARED); - - public IEnumerable DeclaredProperties => - _type.GetProperties(ALL_DECLARED); - - public IEnumerable ImplementedInterfaces => - _type.GetInterfaces(); - - public IEnumerable GetCustomAttributes(Type attributeType, bool inherit) => - _type.GetCustomAttributes(attributeType, inherit).Cast(); - - public Type BaseType => _type.BaseType; - public bool IsGenericType => _type.IsGenericType; - public bool IsGenericTypeDefinition => _type.IsGenericTypeDefinition; - public bool ContainsGenericParameters => _type.ContainsGenericParameters; - public Type[] GenericTypeParameters => _type.GetGenericArguments(); - public Type[] GenericTypeArguments => _type.GetGenericArguments(); - - public bool IsClass => _type.IsClass; - public bool IsInterface => _type.IsInterface; - public bool IsValueType => _type.IsValueType; - public bool IsPrimitive => _type.IsPrimitive; - public bool IsArray => _type.IsArray; - public bool IsPublic => _type.IsPublic; - public bool IsNestedPublic => _type.IsNestedPublic; - public Type DeclaringType => _type.DeclaringType; - public bool IsAbstract => _type.IsAbstract; - public bool IsSealed => _type.IsSealed; - public bool IsEnum => _type.IsEnum; - - public Type[] GetGenericParameterConstraints() => _type.GetGenericParameterConstraints(); - public Type GetElementType() => _type.GetElementType(); - - public bool IsAssignableFrom(TypeInfo typeInfo) => _type.IsAssignableFrom(typeInfo.AsType()); -#pragma warning restore 1591 // "Missing XML-comment" - - private const BindingFlags ALL_DECLARED = - BindingFlags.Instance | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic | - BindingFlags.DeclaredOnly; - } -} -#endif - -#if NET35 -namespace System -{ - /// Func with 5 input parameters. - public delegate TResult Func( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5); - - /// Func with 6 input parameters. - public delegate TResult Func( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6); - - /// Func with 7 input parameters. - public delegate TResult Func( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6, - T7 arg7); - - /// Action with 5 input parameters. - public delegate void Action( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5); - - /// Action with 6 input parameters. - public delegate void Action( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6); - - /// Action with 7 input parameters. - public delegate void Action( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6, - T7 arg7); - - - /// Wrapper for value computation required on-demand. Since computed the same value will be returned over and over again. - /// Type of value. - public sealed class Lazy - { - /// Creates lazy object with passed value computation delegate. - /// Value computation. Will be stored until computation is done. - /// Throws for null computation. - public Lazy(Func valueFactory) - { - if (valueFactory == null) throw new ArgumentNullException("valueFactory"); - _valueFactory = valueFactory; - } - - /// Indicates if value is computed already, or not. - public bool IsValueCreated { get; private set; } - - /// Computes value if it was not before, and returns it. - /// Value is guaranteed to be computed only once despite possible thread contention. - /// Throws if value computation is recursive. - public T Value => IsValueCreated ? _value : Create(); - -#region Implementation - - private Func _valueFactory; - private T _value; - private readonly object _valueCreationLock = new object(); - - private T Create() - { - lock (_valueCreationLock) - { - if (!IsValueCreated) - { - if (_valueFactory == null) throw new InvalidOperationException("The initialization function tries to access Value on this instance."); - var factory = _valueFactory; - _valueFactory = null; - _value = factory(); - IsValueCreated = true; - } - } - - return _value; - } - -#endregion - } - - /// Contains utility methods for creating and working with tuple. - public static class Tuple - { - /// Creates a new 2-tuple, or pair. - /// A 2-tuple whose value is (, ). - /// The value of the first component of the tuple.The value of the second component of the tuple.The type of the first component of the tuple.The type of the second component of the tuple. - public static Tuple Create(T1 item1, T2 item2) => new Tuple(item1, item2); - } - - /// Represents a 2-tuple, or pair. - /// The type of the tuple's first component.The type of the tuple's second component.2 - public sealed class Tuple - { - private readonly T1 _item1; - private readonly T2 _item2; - - /// Gets the value of the first component. - public T1 Item1 => _item1; - - /// Gets the value of the current second component. - public T2 Item2 => _item2; - - /// Initializes a new instance of the class. - /// The value of the tuple's first component.The value of the tuple's second component. - public Tuple(T1 item1, T2 item2) - { - _item1 = item1; - _item2 = item2; - } - - /// Returns a value that indicates whether the current object is equal to a specified object. - /// The object to compare with this instance. - /// true if the current instance is equal to the specified object; otherwise, false. - public override bool Equals(object obj) - { - var other = obj as Tuple; - return other != null && Equals(other.Item1, Item1) && Equals(other.Item2, Item2); - } - - /// Returns the hash code for the current object. - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - var h1 = _item1 == null ? 0 : _item1.GetHashCode(); - var h2 = _item2 == null ? 0 : _item2.GetHashCode(); - return (h1 << 5) + h1 ^ h2; - } - - /// Returns a string that represents the value of this instance. - public override string ToString() => "(" + _item1 + ", " + _item2 + ")"; - } -} -#endif -#if SUPPORTS_VARIANCE -namespace DryIoc.Messages -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - - /// Base type for messages - public interface IMessage { } - - /// Type for an empty response - public struct EmptyResponse - { - /// Single value of empty response - public static readonly EmptyResponse Value = new EmptyResponse(); - - /// Single completed task for the empty response - public static readonly Task Task = System.Threading.Tasks.Task.FromResult(Value); - } - - /// Message extensions - public static class MessageExtensions - { - /// Converts the task to empty response task - public static async Task ToEmptyResponse(this Task task) - { - await task; - return EmptyResponse.Value; - } - } - - /// Message with empty response - public interface IMessage : IMessage { } - - /// Base message handler - public interface IMessageHandler where M : IMessage - { - /// Generic handler - Task Handle(M message, CancellationToken cancellationToken); - } - - /// Base message handler for message with empty response - public interface IMessageHandler : IMessageHandler where M : IMessage { } - - /// Message handler middleware to handle the message and pass the result to the next middleware - public interface IMessageMiddleware - { - /// `0` means the default registration order, - /// lesser numbers incuding the `-1`, `-2` mean execute as a first, - /// bigger numbers mean execute as a last - int RelativeOrder { get; } - - /// Handles message and passes to the next middleware - Task Handle(M message, CancellationToken cancellationToken, Func> nextMiddleware); - } - - /// Base class for implementing async handlers - public abstract class AsyncMessageHandler : IMessageHandler - where M : IMessage - { - /// Base method to implement in your inheritor - protected abstract Task Handle(M message, CancellationToken cancellationToken); - - async Task IMessageHandler.Handle(M message, CancellationToken cancellationToken) => - await Handle(message, cancellationToken).ConfigureAwait(false); - } - - /// Sequential middleware type of message handler decorator - public class MiddlewareMessageHandler : IMessageHandler where M : IMessage - { - private readonly IMessageHandler _handler; - private readonly IEnumerable> _middlewares; - - /// Decorates message handler with optional middlewares - public MiddlewareMessageHandler(IMessageHandler handler, IEnumerable> middlewares) - { - _handler = handler; - _middlewares = middlewares; - } - - /// Composes middlewares with handler - public Task Handle(M message, CancellationToken cancellationToken) - { - return _middlewares - .OrderBy(x => x.RelativeOrder) - .Reverse() - .Aggregate( - new Func>(() => _handler.Handle(message, cancellationToken)), - (f, middleware) => () => middleware.Handle(message, cancellationToken, f)) - .Invoke(); - } - } - - /// Broadcasting type of message handler decorator - public class BroadcastMessageHandler: IMessageHandler - where M : IMessage - { - private readonly IEnumerable> _handlers; - - /// Constructs the hub with the handler and optional middlewares - public BroadcastMessageHandler(IEnumerable> handlers) => - _handlers = handlers; - - /// Composes middlewares with handler - public async Task Handle(M message, CancellationToken cancellationToken) - { - await Task.WhenAll(_handlers.Select(h => h.Handle(message, cancellationToken))); - return EmptyResponse.Value; - } - } - - /// A subject - public class MessageMediator - { - private readonly IResolver _resolver; - - /// Constructs with resolver - public MessageMediator(IResolver resolver) => - _resolver = resolver; - - /// Sends message with response to resolved handlers - public Task Send(M message, CancellationToken cancellationToken) where M : IMessage => - _resolver.Resolve>().Handle(message, cancellationToken); - - /// Sends message with empty response to resolved handlers - public Task Send(M message, CancellationToken cancellationToken) where M : IMessage => - _resolver.Resolve>().Handle(message, cancellationToken); - } -} -#endif diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/Expression.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/Expression.cs deleted file mode 100644 index 960f07b..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/Expression.cs +++ /dev/null @@ -1,2789 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2016-2020 Maksim Volkau - -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 -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. -*/ - -// ReSharper disable CoVariantArrayConversion -/* -// Lists the target platforms that are Not supported by FEC - simplifies the direct referencing of Expression.cs file -// Don't forget to uncomment #endif at the end of file -*/ -#if !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_FAST_EXPRESSION_COMPILER -#endif - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using static System.Environment; -using SysExpr = System.Linq.Expressions.Expression; - -namespace FastExpressionCompiler.LightExpression -{ -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - - /// Facade for constructing Expression. - public abstract class Expression - { - /// Expression node type. - public abstract ExpressionType NodeType { get; } - - /// All expressions should have a Type. - public abstract Type Type { get; } - - internal struct LightAndSysExpr - { - public Expression LightExpr; - public SysExpr SysExpr; - } - - public SysExpr ToExpression() - { - var exprsConverted = new LiveCountArray(Tools.Empty()); - return ToExpression(ref exprsConverted); - } - - /// Converts back to respective System Expression, so you may Compile it by usual means. - internal SysExpr ToExpression(ref LiveCountArray exprsConverted) - { - var i = exprsConverted.Count - 1; - while (i != -1 && !ReferenceEquals(exprsConverted.Items[i].LightExpr, this)) --i; - if (i != -1) - return exprsConverted.Items[i].SysExpr; - - var sysExpr = CreateSysExpression(ref exprsConverted); - - ref var item = ref exprsConverted.PushSlot(); - item.LightExpr = this; - item.SysExpr = sysExpr; - - return sysExpr; - } - - internal abstract SysExpr CreateSysExpression(ref LiveCountArray exprsConverted); - - /// Tries to print the expression in its constructing syntax - helpful to get it from debug and put into code to test, - /// e.g. . - /// - /// NOTE: It is trying hard but the Parameter expression are not consolidated into one. Hopefully R# will help you to re-factor them into a single variable. - public abstract string CodeString { get; } - - /// Converts to Expression and outputs its as string - public override string ToString() => ToExpression().ToString(); - - /// Reduces the Expression to simple ones - public virtual Expression Reduce() => this; - - internal static SysExpr[] ToExpressions(IReadOnlyList exprs, ref LiveCountArray exprsConverted) - { - if (exprs.Count == 0) - return Tools.Empty(); - - if (exprs.Count == 1) - return new[] { exprs[0].ToExpression(ref exprsConverted) }; - - var result = new SysExpr[exprs.Count]; - for (var i = 0; i < result.Length; ++i) - result[i] = exprs[i].ToExpression(ref exprsConverted); - return result; - } - - internal static string ToParamsCode(IReadOnlyList arguments) where T : Expression - { - if (arguments.Count == 0) - return $"new {typeof(T).Name}[0]"; - - var s = new StringBuilder(); - for (var i = 0; i < arguments.Count; i++) - { - if (i > 0) - s.Append(","); - s.Append(arguments[i].CodeString); - } - - return s.ToString(); - } - - public static ParameterExpression Parameter(Type type, string name = null) => - new ParameterExpression(type.IsByRef ? type.GetElementType() : type, name, type.IsByRef); - - public static ParameterExpression Variable(Type type, string name = null) => Parameter(type, name); - - public static readonly ConstantExpression NullConstant = new TypedConstantExpression(null, typeof(object)); - public static readonly ConstantExpression FalseConstant = new ConstantExpression(false); - public static readonly ConstantExpression TrueConstant = new ConstantExpression(true); - public static readonly ConstantExpression ZeroConstant = new ConstantExpression(0); - - public static ConstantExpression Constant(bool value) => - value ? TrueConstant : FalseConstant; - - public static ConstantExpression Constant(int value) => - value == 0 ? ZeroConstant : new TypedConstantExpression(value); - - public static ConstantExpression Constant(T value) => - new TypedConstantExpression(value); - - public static ConstantExpression Constant(object value) - { - if (value == null) - return NullConstant; - - if (value is bool b) - return b ? TrueConstant : FalseConstant; - - if (value is int n) - return n == 0 ? ZeroConstant : new TypedConstantExpression(n); - - return new ConstantExpression(value); - } - - public static ConstantExpression Constant(object value, Type type) => - new TypedConstantExpression(value, type); - - public static NewExpression New(Type type) - { - if (type.IsValueType()) - return new NewValueTypeExpression(type); - - foreach (var x in type.GetTypeInfo().DeclaredConstructors) - if (x.GetParameters().Length == 0) - return new NewExpression(x); - - throw new ArgumentException($"The type {type} is missing the default constructor"); - } - - public static NewExpression New(ConstructorInfo ctor, params Expression[] arguments) => - arguments == null || arguments.Length == 0 ? new NewExpression(ctor): new ManyArgumentsNewExpression(ctor, arguments); - - public static NewExpression New(ConstructorInfo ctor, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - return args == null || args.Count == 0 ? new NewExpression(ctor) : new ManyArgumentsNewExpression(ctor, args); - } - - public static NewExpression New(ConstructorInfo ctor) => new NewExpression(ctor); - - public static NewExpression New(ConstructorInfo ctor, Expression arg) => - new OneArgumentNewExpression(ctor, arg); - - public static NewExpression New(ConstructorInfo ctor, Expression arg0, Expression arg1) => - new TwoArgumentsNewExpression(ctor, arg0, arg1); - - public static NewExpression New(ConstructorInfo ctor, Expression arg0, Expression arg1, Expression arg2) => - new ThreeArgumentsNewExpression(ctor, arg0, arg1, arg2); - - public static NewExpression New(ConstructorInfo ctor, - Expression arg0, Expression arg1, Expression arg2, Expression arg3) => - new FourArgumentsNewExpression(ctor, arg0, arg1, arg2, arg3); - - public static NewExpression New(ConstructorInfo ctor, - Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) => - new FiveArgumentsNewExpression(ctor, arg0, arg1, arg2, arg3, arg4); - - public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new MethodCallExpression(method); - return new ManyArgumentsMethodCallExpression(method, arguments); - } - - public static MethodCallExpression Call(MethodInfo method, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new MethodCallExpression(method); - return new ManyArgumentsMethodCallExpression(method, args); - } - - public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new InstanceMethodCallExpression(instance, method); - return new InstanceManyArgumentsMethodCallExpression(instance, method, arguments); - } - - public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new InstanceMethodCallExpression(instance, method); - return new InstanceManyArgumentsMethodCallExpression(instance, method, args); - } - - public static MethodCallExpression Call(Type type, string methodName, Type[] typeArguments, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new MethodCallExpression(type.FindMethod(methodName, typeArguments, arguments, isStatic: true)); - return new ManyArgumentsMethodCallExpression(type.FindMethod(methodName, typeArguments, arguments, isStatic: true), arguments); - } - - public static MethodCallExpression Call(Type type, string methodName, Type[] typeArguments, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new MethodCallExpression(type.FindMethod(methodName, typeArguments, args, isStatic: true)); - return new ManyArgumentsMethodCallExpression(type.FindMethod(methodName, typeArguments, args, isStatic: true), args); - } - - public static MethodCallExpression Call(Expression instance, string methodName, Type[] typeArguments, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new InstanceMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, arguments)); - return new InstanceManyArgumentsMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, arguments), arguments); - } - - public static MethodCallExpression Call(Expression instance, string methodName, Type[] typeArguments, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new InstanceMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, args)); - return new InstanceManyArgumentsMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, args), args); - } - - public static MethodCallExpression Call(MethodInfo method) => - new MethodCallExpression(method); - - public static MethodCallExpression Call(Expression instance, MethodInfo method) => - instance == null - ? new MethodCallExpression(method) - : new InstanceMethodCallExpression(instance, method); - - public static MethodCallExpression Call(MethodInfo method, Expression argument) => - new OneArgumentMethodCallExpression(method, argument); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression argument) => - instance == null - ? new OneArgumentMethodCallExpression(method, argument) - : new InstanceOneArgumentMethodCallExpression(instance, method, argument); - - public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1) => - new TwoArgumentsMethodCallExpression(method, arg0, arg1); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1) => - instance == null - ? new TwoArgumentsMethodCallExpression(method, arg0, arg1) - : new InstanceTwoArgumentsMethodCallExpression(instance, method, arg0, arg1); - - public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2) => - new ThreeArgumentsMethodCallExpression(method, arg0, arg1, arg2); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1, Expression arg2) => - instance == null - ? new ThreeArgumentsMethodCallExpression(method, arg0, arg1, arg2) - : new InstanceThreeArgumentsMethodCallExpression(instance, method, arg0, arg1, arg2); - - public static MethodCallExpression Call(MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3) => - new FourArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3) => - instance == null - ? new FourArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3) - : new InstanceFourArgumentsMethodCallExpression(instance, method, arg0, arg1, arg2, arg3); - - public static MethodCallExpression Call(MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) => - new FiveArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3, arg4); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) => - instance == null - ? new FiveArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3, arg4) - : new InstanceFiveArgumentsMethodCallExpression(instance, method, arg0, arg1, arg2, arg3, arg4); - - public static Expression CallIfNotNull(Expression instance, MethodInfo method) => - CallIfNotNull(instance, method, Tools.Empty()); - - public static Expression CallIfNotNull(Expression instance, MethodInfo method, IEnumerable arguments) - { - var instanceVar = Parameter(instance.Type, "f"); - return Block(instanceVar, - Assign(instanceVar, instance), - Condition(Equal(instanceVar, Constant(null)), - Constant(null), Call(instanceVar, method, arguments))); - } - - public static MemberExpression Property(PropertyInfo property) => - new PropertyExpression(null, property); - - public static MemberExpression Property(Expression instance, PropertyInfo property) => - new PropertyExpression(instance, property); - - public static MemberExpression Property(Expression expression, string propertyName) => - Property(expression, expression.Type.FindProperty(propertyName) - ?? throw new ArgumentException($"Declared property with the name '{propertyName}' is not found in '{expression.Type}'", nameof(propertyName))); - - public static IndexExpression Property(Expression instance, PropertyInfo indexer, params Expression[] arguments) => - new IndexExpression(instance, indexer, arguments); - - public static IndexExpression Property(Expression instance, PropertyInfo indexer, IEnumerable arguments) => - new IndexExpression(instance, indexer, arguments.AsReadOnlyList()); - - public static MemberExpression PropertyOrField(Expression expression, string propertyName) => - expression.Type.FindProperty(propertyName) != null ? - (MemberExpression)new PropertyExpression(expression, expression.Type.FindProperty(propertyName) - ?? throw new ArgumentException($"Declared property with the name '{propertyName}' is not found in '{expression.Type}'", nameof(propertyName))) : - new FieldExpression(expression, expression.Type.FindField(propertyName) - ?? throw new ArgumentException($"Declared field with the name '{propertyName}' is not found '{expression.Type}'", nameof(propertyName))); - - public static MemberExpression MakeMemberAccess(Expression expression, MemberInfo member) - { - if (member is FieldInfo field) - return Field(expression, field); - if (member is PropertyInfo property) - return Property(expression, property); - throw new ArgumentException($"Member is not field or property: {member}", nameof(member)); - } - - public static IndexExpression MakeIndex(Expression instance, PropertyInfo indexer, IEnumerable arguments) => - indexer != null ? Property(instance, indexer, arguments) : ArrayAccess(instance, arguments); - - public static IndexExpression ArrayAccess(Expression array, params Expression[] indexes) => - new IndexExpression(array, null, indexes); - - public static IndexExpression ArrayAccess(Expression array, IEnumerable indexes) => - new IndexExpression(array, null, indexes.AsReadOnlyList()); - - public static MemberExpression Field(FieldInfo field) => - new FieldExpression(null, field); - - public static MemberExpression Field(Expression instance, FieldInfo field) => - new FieldExpression(instance, field); - - public static MemberExpression Field(Expression instance, string fieldName) => - new FieldExpression(instance, instance.Type.FindField(fieldName)); - - /// Creates a UnaryExpression that represents a bitwise complement operation. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Not and the Operand property set to the specified value. - public static UnaryExpression Not(Expression expression) => - new UnaryExpression(ExpressionType.Not, expression); - - /// Creates a UnaryExpression that represents an explicit reference or boxing conversion where null is supplied if the conversion fails. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to TypeAs and the Operand and Type properties set to the specified values. - public static UnaryExpression TypeAs(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.TypeAs, expression, type); - - public static TypeBinaryExpression TypeEqual(Expression operand, Type type) => - new TypeBinaryExpression(ExpressionType.TypeEqual, operand, type); - - public static TypeBinaryExpression TypeIs(Expression operand, Type type) => - new TypeBinaryExpression(ExpressionType.TypeIs, operand, type); - - /// Creates a UnaryExpression that represents an expression for obtaining the length of a one-dimensional array. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to ArrayLength and the Operand property equal to array. - public static UnaryExpression ArrayLength(Expression array) => - new TypedUnaryExpression(ExpressionType.ArrayLength, array); - - /// Creates a UnaryExpression that represents a type conversion operation. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to Convert and the Operand and Type properties set to the specified values. - public static UnaryExpression Convert(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.Convert, expression, type); - - /// Creates a UnaryExpression that represents a type conversion operation. - /// A Type to set the Type property equal to. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Convert and the Operand and Type properties set to the specified values. - public static UnaryExpression Convert(Expression expression) => - new TypedUnaryExpression(ExpressionType.Convert, expression); - - /// Creates a UnaryExpression that represents a conversion operation for which the implementing method is specified. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A MethodInfo to set the Method property equal to. - /// A UnaryExpression that has the NodeType property equal to Convert and the Operand, Type, and Method properties set to the specified values. - public static UnaryExpression Convert(Expression expression, Type type, MethodInfo method) => - new ConvertWithMethodUnaryExpression(ExpressionType.Convert, expression, type, method); - - /// Creates a UnaryExpression that represents a conversion operation that throws an exception if the target type is overflowed. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to ConvertChecked and the Operand and Type properties set to the specified values. - public static UnaryExpression ConvertChecked(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.ConvertChecked, expression, type); - - /// Creates a UnaryExpression that represents a conversion operation that throws an exception if the target type is overflowed and for which the implementing method is specified. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A MethodInfo to set the Method property equal to. - /// A UnaryExpression that has the NodeType property equal to ConvertChecked and the Operand, Type, and Method properties set to the specified values. - public static UnaryExpression ConvertChecked(Expression expression, Type type, MethodInfo method) => - new ConvertWithMethodUnaryExpression(ExpressionType.ConvertChecked, expression, type, method); - - /// Creates a UnaryExpression that represents the decrementing of the expression by 1. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the decremented expression. - public static UnaryExpression Decrement(Expression expression) => - new UnaryExpression(ExpressionType.Decrement, expression); - - /// Creates a UnaryExpression that represents the incrementing of the expression value by 1. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the incremented expression. - public static UnaryExpression Increment(Expression expression) => - new UnaryExpression(ExpressionType.Increment, expression); - - /// Returns whether the expression evaluates to false. - /// An Expression to set the Operand property equal to. - /// An instance of UnaryExpression. - public static UnaryExpression IsFalse(Expression expression) => - new TypedUnaryExpression(ExpressionType.IsFalse, expression); - - /// Returns whether the expression evaluates to true. - /// An Expression to set the Operand property equal to. - /// An instance of UnaryExpression. - public static UnaryExpression IsTrue(Expression expression) => - new TypedUnaryExpression(ExpressionType.IsTrue, expression); - - /// Creates a UnaryExpression, given an operand, by calling the appropriate factory method. - /// The ExpressionType that specifies the type of unary operation. - /// An Expression that represents the operand. - /// The Type that specifies the type to be converted to (pass null if not applicable). - /// The UnaryExpression that results from calling the appropriate factory method. - public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type) => - type == null - ? new UnaryExpression(unaryType, operand) - : new TypedUnaryExpression(unaryType, operand, type); - - /// Creates a UnaryExpression that represents an arithmetic negation operation. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Negate and the Operand property set to the specified value. - public static UnaryExpression Negate(Expression expression) => - new UnaryExpression(ExpressionType.Negate, expression); - - /// Creates a UnaryExpression that represents an arithmetic negation operation that has overflow checking. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to NegateChecked and the Operand property set to the specified value. - public static UnaryExpression NegateChecked(Expression expression) => - new UnaryExpression(ExpressionType.NegateChecked, expression); - - /// Returns the expression representing the ones complement. - /// An Expression to set the Operand property equal to. - /// An instance of UnaryExpression. - public static UnaryExpression OnesComplement(Expression expression) => - new UnaryExpression(ExpressionType.OnesComplement, expression); - - /// Creates a UnaryExpression that increments the expression by 1 and assigns the result back to the expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PreIncrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PreIncrementAssign, expression); - - /// Creates a UnaryExpression that represents the assignment of the expression followed by a subsequent increment by 1 of the original expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PostIncrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PostIncrementAssign, expression); - - /// Creates a UnaryExpression that decrements the expression by 1 and assigns the result back to the expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PreDecrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PreDecrementAssign, expression); - - /// Creates a UnaryExpression that represents the assignment of the expression followed by a subsequent decrement by 1 of the original expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PostDecrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PostDecrementAssign, expression); - - /// Creates a UnaryExpression that represents an expression that has a constant value of type Expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Quote and the Operand property set to the specified value. - public static UnaryExpression Quote(Expression expression) => - new UnaryExpression(ExpressionType.Quote, expression); - - /// Creates a UnaryExpression that represents a unary plus operation. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to UnaryPlus and the Operand property set to the specified value. - public static UnaryExpression UnaryPlus(Expression expression) => - new UnaryExpression(ExpressionType.UnaryPlus, expression); - - /// Creates a UnaryExpression that represents an explicit unboxing. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to unbox and the Operand and Type properties set to the specified values. - public static UnaryExpression Unbox(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.Unbox, expression, type); - - public static LambdaExpression Lambda(Expression body) => - new LambdaExpression(Tools.GetFuncOrActionType(Tools.Empty(), body.Type), - body, body.Type); - - public static LambdaExpression Lambda(Type delegateType, Expression body) => - new LambdaExpression(delegateType, body, body.Type); - - public static LambdaExpression Lambda(Type delegateType, Expression body, Type returnType) => - new LambdaExpression(delegateType, body, returnType); - - public static LambdaExpression Lambda(Expression body, params ParameterExpression[] parameters) => - Lambda(Tools.GetFuncOrActionType(Tools.GetParamTypes(parameters), body.Type), body, parameters, body.Type); - - public static LambdaExpression Lambda(Type delegateType, Expression body, params ParameterExpression[] parameters) => - Lambda(delegateType, body, parameters, GetDelegateReturnType(delegateType)); - - public static LambdaExpression Lambda(Type delegateType, Expression body, ParameterExpression[] parameters, Type returnType) => - parameters == null || parameters.Length == 0 - ? new LambdaExpression(delegateType, body, returnType) - : new ManyParametersLambdaExpression(delegateType, body, parameters, returnType); - - public static Expression Lambda(Expression body) => - new Expression(body, typeof(TDelegate).FindDelegateInvokeMethod().ReturnType); - - public static Expression Lambda(Expression body, Type returnType) => - new Expression(body, returnType); - - public static Expression Lambda(Expression body, params ParameterExpression[] parameters) => - Lambda(body, parameters, GetDelegateReturnType(typeof(TDelegate))); - - public static Expression Lambda(Expression body, ParameterExpression[] parameters, Type returnType) => - parameters == null || parameters.Length == 0 - ? new Expression(body, returnType) - : new ManyParametersExpression(body, parameters, returnType); - - /// - /// is ignored for now, the method is just for compatibility with SysExpression - /// - public static Expression Lambda(Expression body, string name, params ParameterExpression[] parameters) where TDelegate : class => - new ManyParametersExpression(body, parameters, GetDelegateReturnType(typeof(TDelegate))); - - private static Type GetDelegateReturnType(Type delType) - { - var typeInfo = delType.GetTypeInfo(); - if (typeInfo.IsGenericType) - { - var typeArguments = typeInfo.GenericTypeArguments; - var index = typeArguments.Length - 1; - var typeDef = typeInfo.GetGenericTypeDefinition(); - if (typeDef == FuncTypes[index]) - return typeArguments[index]; - - if (typeDef == ActionTypes[index]) - return typeof(void); - } - else if (delType == typeof(Action)) - return typeof(void); - - return delType.FindDelegateInvokeMethod().ReturnType; - } - - internal static readonly Type[] FuncTypes = - { - typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>), - typeof(Func<,,,,,>), typeof(Func<,,,,,,>), typeof(Func<,,,,,,,>) - }; - - internal static readonly Type[] ActionTypes = - { - typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>), - typeof(Action<,,,,>), typeof(Action<,,,,,>), typeof(Action<,,,,,,>) - }; - - /// Creates a BinaryExpression that represents applying an array index operator to an array of rank one. - /// A Expression to set the Left property equal to. - /// A Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ArrayIndex and the Left and Right properties set to the specified values. - public static BinaryExpression ArrayIndex(Expression array, Expression index) => - new ArrayIndexExpression(array, index, array.Type.GetElementType()); - - public static MemberAssignment Bind(MemberInfo member, Expression expression) => - new MemberAssignment(member, expression); - - public static MemberInitExpression MemberInit(NewExpression newExpr, params MemberBinding[] bindings) => - new MemberInitExpression(newExpr, bindings); - - /// Does not present in System Expression. Enables member assignment on existing instance expression. - public static MemberInitExpression MemberInit(Expression instanceExpr, params MemberBinding[] assignments) => - new MemberInitExpression(instanceExpr, assignments); - - public static NewArrayExpression NewArrayInit(Type type, params Expression[] initializers) => - new NewArrayExpression(ExpressionType.NewArrayInit, type.MakeArrayType(), initializers); - - public static NewArrayExpression NewArrayBounds(Type type, params Expression[] bounds) => - new NewArrayExpression(ExpressionType.NewArrayBounds, - bounds.Length == 1 ? type.MakeArrayType() : type.MakeArrayType(bounds.Length), - bounds); - - /// Creates a BinaryExpression that represents an assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Assign and the Left and Right properties set to the specified values. - public static BinaryExpression Assign(Expression left, Expression right) => - new AssignBinaryExpression(left, right, left.Type); - - /// Creates a BinaryExpression that represents raising an expression to a power and assigning the result back to the expression. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to PowerAssign and the Left and Right properties set to the specified values. - public static BinaryExpression PowerAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.PowerAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents an addition assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AddAssign and the Left and Right properties set to the specified values. - public static BinaryExpression AddAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.AddAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents an addition assignment operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AddAssignChecked and the Left and Right properties set to the specified values. - public static BinaryExpression AddAssignChecked(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.AddAssignChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise AND assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AndAssign and the Left and Right properties set to the specified values. - public static BinaryExpression AndAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.AndAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise XOR assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ExclusiveOrAssign and the Left and Right properties set to the specified values. - public static BinaryExpression ExclusiveOrAssign(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.ExclusiveOrAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise left-shift assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LeftShiftAssign and the Left and Right properties set to the specified values. - public static BinaryExpression LeftShiftAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.LeftShiftAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a remainder assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ModuloAssign and the Left and Right properties set to the specified values. - public static BinaryExpression ModuloAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.ModuloAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise OR assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to OrAssign and the Left and Right properties set to the specified values. - public static BinaryExpression OrAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.OrAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise right-shift assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to RightShiftAssign and the Left and Right properties set to the specified values. - public static BinaryExpression RightShiftAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.RightShiftAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a subtraction assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to SubtractAssign and the Left and Right properties set to the specified values. - public static BinaryExpression SubtractAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.SubtractAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a subtraction assignment operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to SubtractAssignChecked and the Left and Right properties set to the specified values. - public static BinaryExpression SubtractAssignChecked(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.SubtractAssignChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a multiplication assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to MultiplyAssign and the Left and Right properties set to the specified values. - public static BinaryExpression MultiplyAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.MultiplyAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a multiplication assignment operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to MultiplyAssignChecked and the Left and Right properties set to the specified values. - public static BinaryExpression MultiplyAssignChecked(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.MultiplyAssignChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a division assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to DivideAssign and the Left and Right properties set to the specified values. - public static BinaryExpression DivideAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.DivideAssign, left, right, left.Type); - - public static InvocationExpression Invoke(Expression lambda, IEnumerable args) => - new InvocationExpression(lambda, args.AsReadOnlyList(), - (lambda as LambdaExpression)?.ReturnType ?? lambda.Type.FindDelegateInvokeMethod().ReturnType); - - public static InvocationExpression Invoke(Expression lambda, params Expression[] args) => - Invoke(lambda, (IEnumerable)args); - - public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse) => - new ConditionalExpression(test, ifTrue, ifFalse); - - public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, Type type) => - new ConditionalExpression(test, ifTrue, ifFalse, type); - - public static ConditionalExpression IfThen(Expression test, Expression ifTrue) => - Condition(test, ifTrue, Empty(), typeof(void)); - - public static DefaultExpression Empty() => new DefaultExpression(typeof(void)); - - public static DefaultExpression Default(Type type) => - type == typeof(void) ? Empty() : new DefaultExpression(type); - - public static ConditionalExpression IfThenElse(Expression test, Expression ifTrue, Expression ifFalse) => - Condition(test, ifTrue, ifFalse, typeof(void)); - - /// Creates a BinaryExpression that represents an arithmetic addition operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Add and the Left and Right properties set to the specified values. - public static BinaryExpression Add(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Add, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic addition operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AddChecked and the Left and Right properties set to the specified values. - public static BinaryExpression AddChecked(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.AddChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise XOR operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ExclusiveOr and the Left and Right properties set to the specified values. - public static BinaryExpression ExclusiveOr(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.ExclusiveOr, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise left-shift operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LeftShift and the Left and Right properties set to the specified values. - public static BinaryExpression LeftShift(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.LeftShift, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic remainder operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Modulo and the Left and Right properties set to the specified values. - public static BinaryExpression Modulo(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Modulo, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise OR operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Or and the Left and Right properties set to the specified values. - public static BinaryExpression Or(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Or, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise right-shift operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to RightShift and the Left and Right properties set to the specified values. - public static BinaryExpression RightShift(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.RightShift, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic subtraction operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Subtract and the Left and Right properties set to the specified values. - public static BinaryExpression Subtract(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Subtract, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic subtraction operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to SubtractChecked and the Left, Right, and Method properties set to the specified values. - public static BinaryExpression SubtractChecked(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.SubtractChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic multiplication operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Multiply and the Left and Right properties set to the specified values. - public static BinaryExpression Multiply(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Multiply, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic multiplication operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to MultiplyChecked and the Left and Right properties set to the specified values. - public static BinaryExpression MultiplyChecked(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.MultiplyChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic division operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Divide and the Left and Right properties set to the specified values. - public static BinaryExpression Divide(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Divide, left, right, left.Type); - - /// Creates a BinaryExpression that represents raising a number to a power. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Power and the Left and Right properties set to the specified values. - public static BinaryExpression Power(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Power, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise AND operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to And, and the Left and Right properties are set to the specified values. - public static BinaryExpression And(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.And, left, right, left.Type); - - /// Creates a BinaryExpression that represents a conditional AND operation that evaluates the second operand only if the first operand evaluates to true. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AndAlso and the Left and Right properties set to the specified values. - public static BinaryExpression AndAlso(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.AndAlso, left, right, left.Type); - - /// Creates a BinaryExpression that represents a conditional OR operation that evaluates the second operand only if the first operand evaluates to false. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to OrElse and the Left and Right properties set to the specified values. - public static BinaryExpression OrElse(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.OrElse, left, right, left.Type); - - /// Creates a BinaryExpression that represents an equality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Equal and the Left and Right properties set to the specified values. - public static BinaryExpression Equal(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Equal, left, right, typeof(bool)); - - /// Creates a BinaryExpression that represents a "greater than" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to GreaterThan and the Left and Right properties set to the specified values. - public static BinaryExpression GreaterThan(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.GreaterThan, left, right, left.Type); - - /// Creates a BinaryExpression that represents a "greater than or equal" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to GreaterThanOrEqual and the Left and Right properties set to the specified values. - public static BinaryExpression GreaterThanOrEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.GreaterThanOrEqual, left, right, left.Type); - - /// Creates a BinaryExpression that represents a "less than" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LessThan and the Left and Right properties set to the specified values. - public static BinaryExpression LessThan(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.LessThan, left, right, left.Type); - - /// Creates a BinaryExpression that represents a " less than or equal" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LessThanOrEqual and the Left and Right properties set to the specified values. - public static BinaryExpression LessThanOrEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.LessThanOrEqual, left, right, left.Type); - - /// Creates a BinaryExpression that represents an inequality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to NotEqual and the Left and Right properties set to the specified values. - public static BinaryExpression NotEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.NotEqual, left, right, typeof(bool)); - - public static BlockExpression Block(params Expression[] expressions) => - Block(expressions[expressions.Length - 1].Type, Tools.Empty(), expressions); - - public static BlockExpression Block(IReadOnlyList expressions) => - Block(Tools.Empty(), expressions); - - public static BlockExpression Block(IEnumerable variables, params Expression[] expressions) => - Block(variables.AsReadOnlyList(), new List(expressions)); - - public static BlockExpression Block(IReadOnlyList variables, IReadOnlyList expressions) => - Block(expressions[expressions.Count - 1].Type, variables, expressions); - - public static BlockExpression Block(Type type, IEnumerable variables, params Expression[] expressions) => - new BlockExpression(type, variables.AsReadOnlyList(), expressions.AsReadOnlyList()); - - public static BlockExpression Block(Type type, IEnumerable variables, IEnumerable expressions) => - new BlockExpression(type, variables.AsReadOnlyList(), expressions.AsReadOnlyList()); - - public static Expression Block(ParameterExpression variable, Expression expression1, Expression expression2) => - expression2.NodeType == ExpressionType.Throw || expression1.NodeType == ExpressionType.Throw - ? (Expression)Block(new[] {variable}, expression1, expression2) - : new OneVariableTwoExpressionBlockExpression(variable, expression1, expression2); - - /// - /// Creates a LoopExpression with the given body and (optional) break target. - /// - /// The body of the loop. - /// The break target used by the loop body, if required. - /// The created LoopExpression. - public static LoopExpression Loop(Expression body, LabelTarget @break = null) => - @break == null ? new LoopExpression(body, null, null) : new LoopExpression(body, @break, null); - - /// - /// Creates a LoopExpression with the given body. - /// - /// The body of the loop. - /// The break target used by the loop body. - /// The continue target used by the loop body. - /// The created LoopExpression. - public static LoopExpression Loop(Expression body, LabelTarget @break, LabelTarget @continue) => - new LoopExpression(body, @break, @continue); - - public static TryExpression TryCatch(Expression body, params CatchBlock[] handlers) => - new TryExpression(body, null, handlers); - - public static TryExpression TryCatchFinally(Expression body, Expression @finally, params CatchBlock[] handlers) => - new TryExpression(body, @finally, handlers); - - public static TryExpression TryFinally(Expression body, Expression @finally) => - new TryExpression(body, @finally, null); - - public static CatchBlock Catch(ParameterExpression variable, Expression body) => - new CatchBlock(variable, body, null, variable.Type); - - public static CatchBlock Catch(Type test, Expression body) => - new CatchBlock(null, body, null, test); - - /// Creates a UnaryExpression that represents a throwing of an exception. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the exception. - public static UnaryExpression Throw(Expression value) => Throw(value, typeof(void)); - - /// Creates a UnaryExpression that represents a throwing of an exception with a given type. - /// An Expression to set the Operand property equal to. - /// The Type of the expression. - /// A UnaryExpression that represents the exception. - public static UnaryExpression Throw(Expression value, Type type) => - new TypedUnaryExpression(ExpressionType.Throw, value, type); - - public static LabelExpression Label(LabelTarget target, Expression defaultValue = null) => - new LabelExpression(target, defaultValue); - - public static LabelTarget Label(Type type = null, string name = null) => - SysExpr.Label(type ?? typeof(void), name); - - public static LabelTarget Label(string name) => - SysExpr.Label(typeof(void), name); - - /// Creates a BinaryExpression, given the left and right operands, by calling an appropriate factory method. - /// The ExpressionType that specifies the type of binary operation. - /// An Expression that represents the left operand. - /// An Expression that represents the right operand. - /// The BinaryExpression that results from calling the appropriate factory method. - public static BinaryExpression MakeBinary(ExpressionType binaryType, Expression left, Expression right) - { - switch (binaryType) - { - case ExpressionType.AddAssign: - case ExpressionType.AddAssignChecked: - case ExpressionType.AndAssign: - case ExpressionType.Assign: - case ExpressionType.DivideAssign: - case ExpressionType.ExclusiveOrAssign: - case ExpressionType.LeftShiftAssign: - case ExpressionType.ModuloAssign: - case ExpressionType.MultiplyAssign: - case ExpressionType.MultiplyAssignChecked: - case ExpressionType.OrAssign: - case ExpressionType.PowerAssign: - case ExpressionType.RightShiftAssign: - case ExpressionType.SubtractAssign: - case ExpressionType.SubtractAssignChecked: - return new AssignBinaryExpression(binaryType, left, right, left.Type); - case ExpressionType.ArrayIndex: - return ArrayIndex(left, right); - case ExpressionType.Coalesce: - return Coalesce(left, right); - default: - return new SimpleBinaryExpression(binaryType, left, right, left.Type); - } - } - - public static GotoExpression MakeGoto(GotoExpressionKind kind, LabelTarget target, Expression value, Type type = null) => - new GotoExpression(kind, target, value, type ?? typeof(void)); - - public static GotoExpression Break(LabelTarget target, Expression value = null, Type type = null) => - MakeGoto(GotoExpressionKind.Break, target, value, type); - - public static GotoExpression Continue(LabelTarget target, Type type = null) => - MakeGoto(GotoExpressionKind.Continue, target, null, type); - - /// Creates a BinaryExpression that represents a reference equality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Equal and the Left and Right properties set to the specified values. - public static BinaryExpression ReferenceEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Equal, left, right, typeof(bool)); - - /// Creates a BinaryExpression that represents a reference inequality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to NotEqual and the Left and Right properties set to the specified values. - public static BinaryExpression ReferenceNotEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.NotEqual, left, right, typeof(bool)); - - public static GotoExpression Return(LabelTarget target, Expression value = null, Type type = null) => - MakeGoto(GotoExpressionKind.Return, target, value, type); - - public static GotoExpression Goto(LabelTarget target, Expression value = null, Type type = null) => - MakeGoto(GotoExpressionKind.Goto, target, value, type); - - public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, params SwitchCase[] cases) => - new SwitchExpression(defaultBody.Type, switchValue, defaultBody, null, cases); - - public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, MethodInfo comparison, params SwitchCase[] cases) => - new SwitchExpression(defaultBody.Type, switchValue, defaultBody, comparison, cases); - - public static SwitchExpression Switch(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, params SwitchCase[] cases) => - new SwitchExpression(type, switchValue, defaultBody, comparison, cases); - - public static SwitchExpression Switch(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable cases) => - new SwitchExpression(type, switchValue, defaultBody, comparison, cases.AsArray()); - - public static SwitchExpression Switch(Expression switchValue, params SwitchCase[] cases) => - new SwitchExpression(null, switchValue, null, null, cases); - - public static SwitchCase SwitchCase(Expression body, IEnumerable testValues) => - new SwitchCase(body, testValues); - - public static SwitchCase SwitchCase(Expression body, params Expression[] testValues) => - new SwitchCase(body, testValues); - - /// Creates a BinaryExpression that represents a coalescing operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Coalesce and the Left and Right properties set to the specified values. - public static BinaryExpression Coalesce(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, GetCoalesceType(left.Type, right.Type)); - - /// Creates a BinaryExpression that represents a coalescing operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// Result type - /// A BinaryExpression that has the NodeType property equal to Coalesce and the Left and Right properties set to the specified values. - public static BinaryExpression Coalesce(Expression left, Expression right, Type type) => - new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, type); - - /// Creates a BinaryExpression that represents a coalescing operation, given a conversion function. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A LambdaExpression to set the Conversion property equal to. - /// A BinaryExpression that has the NodeType property equal to Coalesce and the Left, Right and Conversion properties set to the specified values. - public static BinaryExpression Coalesce(Expression left, Expression right, LambdaExpression conversion) => - conversion == null ? - new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, GetCoalesceType(left.Type, right.Type)) : - (BinaryExpression)new CoalesceConversionBinaryExpression(left, right, conversion); - - private static Type GetCoalesceType(Type left, Type right) - { - var leftTypeInfo = left.GetTypeInfo(); - if (leftTypeInfo.IsGenericType && leftTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>)) - left = leftTypeInfo.GenericTypeArguments[0]; - - if (right == left) - return left; - - if (leftTypeInfo.IsAssignableFrom(right.GetTypeInfo()) || - right.IsImplicitlyBoxingConvertibleTo(left) || - right.IsImplicitlyNumericConvertibleTo(left)) - return left; - - if (right.GetTypeInfo().IsAssignableFrom(leftTypeInfo) || - left.IsImplicitlyBoxingConvertibleTo(right) || - left.IsImplicitlyNumericConvertibleTo(right)) - return right; - - throw new ArgumentException($"Unable to coalesce arguments of left type of {left} and right type of {right}."); - } - } - - internal static class TypeTools - { - internal static int GetFirstIndex(this IReadOnlyList source, T item) - { - if (source.Count != 0) - for (var i = 0; i < source.Count; ++i) - if (ReferenceEquals(source[i], item)) - return i; - return -1; - } - - internal static bool IsImplicitlyBoxingConvertibleTo(this Type source, Type target) => - source.GetTypeInfo().IsValueType && - (target == typeof(object) || - target == typeof(ValueType)) || - source.GetTypeInfo().IsEnum && target == typeof(Enum); - - internal static PropertyInfo FindProperty(this Type type, string propertyName) - { - var properties = type.GetTypeInfo().DeclaredProperties.AsArray(); - for (var i = 0; i < properties.Length; i++) - if (properties[i].Name == propertyName) - return properties[i]; - - return type.GetTypeInfo().BaseType?.FindProperty(propertyName); - } - - internal static FieldInfo FindField(this Type type, string fieldName) - { - var fields = type.GetTypeInfo().DeclaredFields.AsArray(); - for (var i = 0; i < fields.Length; i++) - if (fields[i].Name == fieldName) - return fields[i]; - - return type.GetTypeInfo().BaseType?.FindField(fieldName); - } - - internal static MethodInfo FindMethod(this Type type, - string methodName, Type[] typeArgs, IReadOnlyList args, bool isStatic = false) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (isStatic == m.IsStatic && methodName == m.Name) - { - typeArgs = typeArgs ?? Type.EmptyTypes; - var mTypeArgs = m.GetGenericArguments(); - - if (typeArgs.Length == mTypeArgs.Length && - (typeArgs.Length == 0 || AreTypesTheSame(typeArgs, mTypeArgs))) - { - args = args ?? Tools.Empty(); - var pars = m.GetParameters(); - if (args.Count == pars.Length && - (args.Count == 0 || AreArgExpressionsAndParamsOfTheSameType(args, pars))) - return m; - } - } - } - - return type.GetTypeInfo().BaseType?.FindMethod(methodName, typeArgs, args, isStatic); - } - - private static bool AreTypesTheSame(Type[] source, Type[] target) - { - for (var i = 0; i < source.Length; i++) - if (source[i] != target[i]) - return false; - return true; - } - - private static bool AreArgExpressionsAndParamsOfTheSameType(IReadOnlyList args, ParameterInfo[] pars) - { - for (var i = 0; i < pars.Length; i++) - if (pars[i].ParameterType != args[i].Type) - return false; - return true; - } - - public static IReadOnlyList AsReadOnlyList(this IEnumerable xs) - { - if (xs is IReadOnlyList list) - return list; - return xs == null ? null : new List(xs); - } - - internal static bool IsImplicitlyNumericConvertibleTo(this Type source, Type target) - { - if (source == typeof(Char)) - return - target == typeof(UInt16) || - target == typeof(Int32) || - target == typeof(UInt32) || - target == typeof(Int64) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(SByte)) - return - target == typeof(Int16) || - target == typeof(Int32) || - target == typeof(Int64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Byte)) - return - target == typeof(Int16) || - target == typeof(UInt16) || - target == typeof(Int32) || - target == typeof(UInt32) || - target == typeof(Int64) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Int16)) - return - target == typeof(Int32) || - target == typeof(Int64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(UInt16)) - return - target == typeof(Int32) || - target == typeof(UInt32) || - target == typeof(Int64) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Int32)) - return - target == typeof(Int64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(UInt32)) - return - target == typeof(UInt32) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Int64) || - source == typeof(UInt64)) - return - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Single)) - return target == typeof(Double); - - return false; - } - } - - /// Converts the object of known type into the valid C# code representation - public static class CodePrinter - { - /// Replace with yours if needed - public static Func GetTypeNameDefault = t => t.Name; - - /// Converts the `typeof()` into the proper C# representation. - public static string ToCode(this Type type, Func getTypeName = null) => - type == null ? "null" : $"typeof({type.ToTypeCode()})"; - - /// Converts the into the proper C# representation. - public static string ToTypeCode(this Type type, Func getTypeName = null) - { - var isArray = type.IsArray; - if (isArray) - type = type.GetElementType(); - - var typeName = (getTypeName ?? GetTypeNameDefault)(type); - - var typeInfo = type.GetTypeInfo(); - if (!typeInfo.IsGenericType) - return typeName.Replace('+', '.'); - - var s = new StringBuilder(typeName.Substring(0, typeName.IndexOf('`')).Replace('+', '.')); - s.Append('<'); - - var genericArgs = typeInfo.GetGenericTypeParametersOrArguments(); - if (typeInfo.IsGenericTypeDefinition) - s.Append(',', genericArgs.Length - 1); - else - { - for (var i = 0; i < genericArgs.Length; i++) - { - if (i > 0) - s.Append(", "); - s.Append(genericArgs[i].ToTypeCode(getTypeName)); - } - } - - s.Append('>'); - - if (isArray) - s.Append("[]"); - - return s.ToString(); - } - - /// Prints valid C# Boolean - public static string ToCode(this bool x) => x ? "true" : "false"; - - /// Prints valid C# String escaping the things - public static string ToCode(this string x) => - x == null ? "null" - : $"\"{x.Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n")}\""; - - - /// Prints valid c# Enum literal - public static string ToEnumValueCode(this Type enumType, object x) - { - var enumTypeInfo = enumType.GetTypeInfo(); - if (enumTypeInfo.IsGenericType && enumTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - if (x == null) - return "null"; - enumType = GetGenericTypeParametersOrArguments(enumTypeInfo)[0]; - } - - return $"{enumType.ToTypeCode()}.{Enum.GetName(enumType, x)}"; - } - - private static Type[] GetGenericTypeParametersOrArguments(this TypeInfo typeInfo) => - typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters : typeInfo.GenericTypeArguments; - - /// Prints many code items as array initializer. - public static string ToCommaSeparatedCode(this IEnumerable items, Func notRecognizedToCode) - { - var s = new StringBuilder(); - var count = 0; - foreach (var item in items) - { - if (count++ != 0) - s.Append(", "); - s.Append(item.ToCode(notRecognizedToCode)); - } - return s.ToString(); - } - - /// Prints many code items as array initializer. - public static string ToArrayInitializerCode(this IEnumerable items, Type itemType, Func notRecognizedToCode) => - $"new {itemType.ToTypeCode()}[]{{{items.ToCommaSeparatedCode(notRecognizedToCode)}}}"; - - /// Prints valid C# for known type, - /// otherwise uses , - /// otherwise falls back to `ToString()` - public static string ToCode(this object x, Func notRecognizedToCode) - { - if (x == null) - return "null"; - - if (x is bool b) - return b.ToCode(); - - if (x is string s) - return s.ToCode(); - - if (x is Type t) - return t.ToCode(); - - var xTypeInfo = x.GetType().GetTypeInfo(); - if (xTypeInfo.IsEnum) - return x.GetType().ToEnumValueCode(x); - - if (x is IEnumerable e) - { - var elemType = xTypeInfo.IsArray - ? xTypeInfo.GetElementType() - : xTypeInfo.GetGenericTypeParametersOrArguments().GetFirst(); - if (elemType != null) - return e.ToArrayInitializerCode(elemType, notRecognizedToCode); - } - - if (xTypeInfo.IsPrimitive) - return x.ToString(); - - if (notRecognizedToCode != null) - return notRecognizedToCode(x); - - return x.ToString(); - } - } - - public class UnaryExpression : Expression - { - public override ExpressionType NodeType { get; } - - public override Type Type => Operand.Type; - public readonly Expression Operand; - - public virtual MethodInfo Method => null; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) - { - switch (NodeType) - { - case ExpressionType.ArrayLength: - return SysExpr.ArrayLength(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Convert: - return SysExpr.Convert(Operand.ToExpression(ref exprsConverted), Type, Method); - case ExpressionType.Decrement: - return SysExpr.Decrement(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Increment: - return SysExpr.Increment(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.IsFalse: - return SysExpr.IsFalse(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.IsTrue: - return SysExpr.IsTrue(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Negate: - return SysExpr.Negate(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.NegateChecked: - return SysExpr.NegateChecked(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.OnesComplement: - return SysExpr.OnesComplement(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PostDecrementAssign: - return SysExpr.PostDecrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PostIncrementAssign: - return SysExpr.PostIncrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PreDecrementAssign: - return SysExpr.PreDecrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PreIncrementAssign: - return SysExpr.PreIncrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Quote: - return SysExpr.Quote(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.UnaryPlus: - return SysExpr.UnaryPlus(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Unbox: - return SysExpr.Unbox(Operand.ToExpression(ref exprsConverted), Type); - case ExpressionType.Throw: - return SysExpr.Throw(Operand.ToExpression(ref exprsConverted), Type); - default: - throw new NotSupportedException("Cannot convert Expression to Expression of type " + NodeType); - } - } - - /// - public override string CodeString - { - get - { - switch (NodeType) - { - case ExpressionType.ArrayLength: - return $"ArrayLength({Operand.CodeString})"; - case ExpressionType.Convert: - if (Method == null) - return $"Convert({Operand.CodeString}, {Type.ToCode()})"; - var methodIndex = Method.DeclaringType.GetTypeInfo().DeclaredMethods.AsArray().GetFirstIndex(Method); - return $"Convert({Operand.CodeString}, {Type.ToCode()}, {Method.DeclaringType.ToCode()}.GetTypeInfo().DeclaredMethods.ToArray()[{methodIndex}])"; - case ExpressionType.Decrement: - return $"Decrement({Operand.CodeString})"; - case ExpressionType.Increment: - return $"Increment({Operand.CodeString})"; - case ExpressionType.IsFalse: - return $"IsFalse({Operand.CodeString})"; - case ExpressionType.IsTrue: - return $"IsTrue({Operand.CodeString})"; - case ExpressionType.Negate: - return $"Negate({Operand.CodeString})"; - case ExpressionType.NegateChecked: - return $"NegateChecked({Operand.CodeString})"; - case ExpressionType.OnesComplement: - return $"OnesComplement({Operand.CodeString})"; - case ExpressionType.PostDecrementAssign: - return $"PostDecrementAssign({Operand.CodeString})"; - case ExpressionType.PostIncrementAssign: - return $"PostIncrementAssign({Operand.CodeString})"; - case ExpressionType.PreDecrementAssign: - return $"PreDecrementAssign({Operand.CodeString})"; - case ExpressionType.PreIncrementAssign: - return $"PreIncrementAssign({Operand.CodeString})"; - case ExpressionType.Quote: - return $"Quote({Operand.CodeString})"; - case ExpressionType.UnaryPlus: - return $"UnaryPlus({Operand.CodeString})"; - case ExpressionType.Unbox: - return $"Unbox({Operand.CodeString}, {Type.ToCode()})"; - case ExpressionType.Throw: - return $"Throw({Operand.CodeString}, {Type.ToCode()})"; - default: - throw new NotSupportedException("Cannot convert Expression to Expression of type " + NodeType); - } - } - } - - public UnaryExpression(ExpressionType nodeType, Expression operand) - { - NodeType = nodeType; - Operand = operand; - } - } - - public class TypedUnaryExpression : UnaryExpression - { - public override Type Type { get; } - - public TypedUnaryExpression(ExpressionType nodeType, Expression operand, Type type) : base(nodeType, operand) => - Type = type; - } - - public sealed class TypedUnaryExpression : UnaryExpression - { - public override Type Type => typeof(T); - - public TypedUnaryExpression(ExpressionType nodeType, Expression operand) : base(nodeType, operand) { } - } - - public sealed class ConvertWithMethodUnaryExpression : TypedUnaryExpression - { - public override MethodInfo Method { get; } - public override Type Type => Method.ReturnType; - - public ConvertWithMethodUnaryExpression(ExpressionType nodeType, Expression operand, MethodInfo method) - : base(nodeType, operand, method.ReturnType) => - Method = method; - - public ConvertWithMethodUnaryExpression(ExpressionType nodeType, Expression operand, Type type, MethodInfo method) - : base(nodeType, operand, type) => - Method = method; - } - - public abstract class BinaryExpression : Expression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - public readonly Expression Left, Right; - - public override string CodeString => - $"{Enum.GetName(typeof(ExpressionType), NodeType)}(" + NewLine + - $"{Left.CodeString}," + NewLine + - $"{Right.CodeString})"; - - protected BinaryExpression(ExpressionType nodeType, Expression left, Expression right, Type type) - { - NodeType = nodeType; - - Left = left; - Right = right; - - if (nodeType == ExpressionType.Equal || - nodeType == ExpressionType.NotEqual || - nodeType == ExpressionType.GreaterThan || - nodeType == ExpressionType.GreaterThanOrEqual || - nodeType == ExpressionType.LessThan || - nodeType == ExpressionType.LessThanOrEqual || - nodeType == ExpressionType.And || - nodeType == ExpressionType.AndAlso || - nodeType == ExpressionType.Or || - nodeType == ExpressionType.OrElse) - { - Type = typeof(bool); - } - else - Type = type; - } - } - - public class TypeBinaryExpression : Expression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - public Type TypeOperand { get; } - - public readonly Expression Expression; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.TypeIs(Expression.ToExpression(ref exprsConverted), TypeOperand); - - public override string CodeString => $"TypeIs({Expression.CodeString}, {TypeOperand.ToCode()})"; - - internal TypeBinaryExpression(ExpressionType nodeType, Expression expression, Type typeOperand) - { - NodeType = nodeType; - Expression = expression; - Type = typeof(bool); - TypeOperand = typeOperand; - } - } - - public sealed class SimpleBinaryExpression : BinaryExpression - { - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) - { - switch (NodeType) - { - case ExpressionType.Add: - return SysExpr.Add(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Subtract: - return SysExpr.Subtract(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Multiply: - return SysExpr.Multiply(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Divide: - return SysExpr.Divide(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Power: - return SysExpr.Power(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Coalesce: - return SysExpr.Coalesce(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.And: - return SysExpr.And(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AndAlso: - return SysExpr.AndAlso(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Or: - return SysExpr.Or(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.OrElse: - return SysExpr.OrElse(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Equal: - return SysExpr.Equal(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.NotEqual: - return SysExpr.NotEqual(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.GreaterThan: - return SysExpr.GreaterThan(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.GreaterThanOrEqual: - return SysExpr.GreaterThanOrEqual(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.LessThan: - return SysExpr.LessThan(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.LessThanOrEqual: - return SysExpr.LessThanOrEqual(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - default: - throw new NotSupportedException($"Not a valid {NodeType} for arithmetic or boolean binary expression."); - } - } - - internal SimpleBinaryExpression(ExpressionType nodeType, Expression left, Expression right, Type type) - : base(nodeType, left, right, type) { } - } - - public class CoalesceConversionBinaryExpression : BinaryExpression - { - public readonly LambdaExpression Conversion; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Coalesce(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted), Conversion.ToLambdaExpression()); - - public override string CodeString => - $"Coalesce({Left.CodeString}, {Right.CodeString}, {Conversion.CodeString})"; - - internal CoalesceConversionBinaryExpression(Expression left, Expression right, LambdaExpression conversion) - : base(ExpressionType.Coalesce, left, right, null) - { - Conversion = conversion; - } - } - - public sealed class ArrayIndexExpression : BinaryExpression - { - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.ArrayIndex(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - - internal ArrayIndexExpression(Expression left, Expression right, Type type) - : base(ExpressionType.ArrayIndex, left, right, type) { } - } - - public sealed class AssignBinaryExpression : BinaryExpression - { - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) - { - switch (NodeType) - { - case ExpressionType.Assign: - return SysExpr.Assign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AddAssign: - return SysExpr.AddAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AddAssignChecked: - return SysExpr.AddAssignChecked(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.SubtractAssign: - return SysExpr.SubtractAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.SubtractAssignChecked: - return SysExpr.SubtractAssignChecked(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.MultiplyAssign: - return SysExpr.MultiplyAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.MultiplyAssignChecked: - return SysExpr.MultiplyAssignChecked(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.DivideAssign: - return SysExpr.DivideAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.PowerAssign: - return SysExpr.PowerAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AndAssign: - return SysExpr.AndAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.OrAssign: - return SysExpr.OrAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - default: - throw new NotSupportedException($"Not a valid {NodeType} for Assign binary expression."); - } - } - - public override string CodeString => - $"{Enum.GetName(typeof(ExpressionType), NodeType)}({Left.CodeString}, {Right.CodeString})"; - - internal AssignBinaryExpression(Expression left, Expression right, Type type) - : base(ExpressionType.Assign, left, right, type) { } - - internal AssignBinaryExpression(ExpressionType expressionType, Expression left, Expression right, Type type) - : base(expressionType, left, right, type) { } - } - - public sealed class MemberInitExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.MemberInit; - public override Type Type => Expression.Type; - - public NewExpression NewExpression => Expression as NewExpression; - - public readonly Expression Expression; - public readonly IReadOnlyList Bindings; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.MemberInit((System.Linq.Expressions.NewExpression)NewExpression.ToExpression(ref exprsConverted), - BindingsToExpressions(Bindings, ref exprsConverted)); - - internal static System.Linq.Expressions.MemberBinding[] BindingsToExpressions( - IReadOnlyList ms, ref LiveCountArray exprsConverted) - { - if (ms.Count == 0) - return Tools.Empty(); - - if (ms.Count == 1) - return new[] { ms[0].ToMemberBinding(ref exprsConverted) }; - - var result = new System.Linq.Expressions.MemberBinding[ms.Count]; - for (var i = 0; i < result.Length; ++i) - result[i] = ms[i].ToMemberBinding(ref exprsConverted); - return result; - } - - public override string CodeString - { - get - { - var bindings = ""; - for (var i = 0; i < Bindings.Count; i++) - { - if (i > 0) - bindings += "," + NewLine; - bindings += Bindings[i].CodeString; - } - return $"MemberInit({Expression.CodeString}," + NewLine + - $"{bindings})"; - } - } - - internal MemberInitExpression(NewExpression newExpression, MemberBinding[] bindings) - : this((Expression)newExpression, bindings) { } - - internal MemberInitExpression(Expression expression, MemberBinding[] bindings) - { - Expression = expression; - Bindings = bindings ?? Tools.Empty(); - } - } - - public sealed class ParameterExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Parameter; - public override Type Type { get; } - - // todo: we need the version without this members - public readonly string Name; - public readonly bool IsByRef; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Parameter(IsByRef ? Type.MakeByRefType() : Type, Name); - - public override string CodeString => - Name != null - ? $"Parameter({Type.ToCode()}{(IsByRef ? ".MakeByRefType()" : "")}, \"{Name}\")" - : $"Parameter({Type.ToCode()}{(IsByRef ? ".MakeByRefType()" : "")})"; - - internal static System.Linq.Expressions.ParameterExpression[] ToParameterExpressions( - IReadOnlyList ps, ref LiveCountArray exprsConverted) - { - if (ps.Count == 0) - return Tools.Empty(); - - if (ps.Count == 1) - return new[] { (System.Linq.Expressions.ParameterExpression)ps[0].ToExpression(ref exprsConverted) }; - - var result = new System.Linq.Expressions.ParameterExpression[ps.Count]; - for (var i = 0; i < result.Length; ++i) - result[i] = (System.Linq.Expressions.ParameterExpression)ps[i].ToExpression(ref exprsConverted); - return result; - } - - internal ParameterExpression(Type type, string name, bool isByRef) - { - Type = type; - Name = name; - IsByRef = isByRef; - } - } - - public class ConstantExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Constant; - public override Type Type => Value.GetType(); - - public readonly object Value; - - internal ConstantExpression(object value) => Value = value; - - internal override SysExpr CreateSysExpression(ref LiveCountArray _) => - SysExpr.Constant(Value, Type); - - /// note: Change to your method to use in for spitting the C# code for the - /// You may try to use `ObjectToCode` from `https://www.nuget.org/packages/ExpressionToCodeLib` - public static Func NotRecognizedValueToCode = x => x.ToString(); - - public override string CodeString => - $"Constant({Value.ToCode(NotRecognizedValueToCode)}, {Type.ToCode()})"; - } - - public sealed class TypedConstantExpression : ConstantExpression - { - public override Type Type { get; } - - internal TypedConstantExpression(object value, Type type) : base(value) => Type = type; - } - - public sealed class TypedConstantExpression : ConstantExpression - { - public override Type Type => typeof(T); - - internal TypedConstantExpression(T value) : base(value) { } - } - - public abstract class ArgumentsExpression : Expression - { - public readonly IReadOnlyList Arguments; - - protected ArgumentsExpression(IReadOnlyList arguments) => Arguments = arguments ?? Tools.Empty(); - } - - public class NewExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.New; - public override Type Type => Constructor.DeclaringType; - - public readonly ConstructorInfo Constructor; - - public virtual int FewArgumentCount => 0; - public virtual IReadOnlyList Arguments => Tools.Empty(); - - internal NewExpression(ConstructorInfo constructor) => Constructor = constructor; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.New(Constructor, ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString - { - get - { - var ctorIndex = Constructor.DeclaringType.GetTypeInfo().DeclaredConstructors.ToArray().GetFirstIndex(Constructor); - return $"New({Type.ToCode()}.GetTypeInfo().DeclaredConstructors.ToArray()[{ctorIndex}]," + NewLine + - $"{ToParamsCode(Arguments)})"; - } - } - } - - public sealed class NewValueTypeExpression : NewExpression - { - public override Type Type { get; } - - internal NewValueTypeExpression(Type type) : base(null) => Type = type; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => SysExpr.New(Type); - - public override string CodeString => $"New({Type.ToCode()})"; - } - - public sealed class OneArgumentNewExpression : NewExpression - { - public readonly Expression Argument; - public override IReadOnlyList Arguments => new[] { Argument }; - public override int FewArgumentCount => 1; - - internal OneArgumentNewExpression(ConstructorInfo constructor, Expression argument) : base(constructor) => - Argument = argument; - } - - public sealed class TwoArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1 }; - public override int FewArgumentCount => 2; - - internal TwoArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - } - } - - public sealed class ThreeArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2 }; - public override int FewArgumentCount => 3; - - internal ThreeArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1, Expression argument2) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - } - } - - public sealed class FourArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3 }; - public override int FewArgumentCount => 4; - - internal FourArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1, Expression argument2, Expression argument3) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - } - } - - public sealed class FiveArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - public readonly Expression Argument4; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3, Argument4 }; - public override int FewArgumentCount => 5; - - internal FiveArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1, Expression argument2, Expression argument3, Expression argument4) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - Argument4 = argument4; - } - } - - public sealed class ManyArgumentsNewExpression : NewExpression - { - public override IReadOnlyList Arguments { get; } - public override int FewArgumentCount => -1; - - internal ManyArgumentsNewExpression(ConstructorInfo constructor, IReadOnlyList arguments) : base(constructor) => - Arguments = arguments; - } - - public sealed class NewArrayExpression : ArgumentsExpression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - // I made it a ICollection for now to use Arguments as input, without changing Arguments type - public IReadOnlyList Expressions => Arguments; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - NodeType == ExpressionType.NewArrayInit - // ReSharper disable once AssignNullToNotNullAttribute - ? SysExpr.NewArrayInit(Type.GetElementType(), ToExpressions(Arguments, ref exprsConverted)) - // ReSharper disable once AssignNullToNotNullAttribute - : SysExpr.NewArrayBounds(Type.GetElementType(), ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString => - NodeType == ExpressionType.NewArrayInit - // ReSharper disable once AssignNullToNotNullAttribute - ? $"NewArrayInit({Type.GetElementType().ToCode()}," + NewLine + $"{ToParamsCode(Arguments)})" - // ReSharper disable once AssignNullToNotNullAttribute - : $"NewArrayBounds({Type.GetElementType().ToCode()}," + NewLine + $"{ToParamsCode(Arguments)})"; - - internal NewArrayExpression(ExpressionType expressionType, Type arrayType, IReadOnlyList elements) : base(elements) - { - NodeType = expressionType; - Type = arrayType; - } - } - - public class MethodCallExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Call; - public override Type Type => Method.ReturnType; - - public virtual Expression Object => null; - public virtual IReadOnlyList Arguments => Tools.Empty(); - public virtual int FewArgumentCount => 0; - - public readonly MethodInfo Method; - - internal MethodCallExpression(MethodInfo method) => Method = method; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Call(Object?.ToExpression(ref exprsConverted), Method, - ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString - { - get - { - var methodIndex = Method.DeclaringType.GetTypeInfo().GetDeclaredMethods(Method.Name).AsArray().GetFirstIndex(Method); - return $"Call({Object?.CodeString ?? "null"}," + NewLine + - $"{Method.DeclaringType.ToCode()}.GetTypeInfo().GetDeclaredMethods(\"{Method.Name}\").ToArray()[{methodIndex}]," + NewLine + - $"{ToParamsCode(Arguments)})"; - } - } - } - - public sealed class InstanceMethodCallExpression : MethodCallExpression - { - public override Expression Object { get; } - - internal InstanceMethodCallExpression(Expression instance, MethodInfo method) : base(method) => - Object = instance; - } - - public class ManyArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments { get; } - public override int FewArgumentCount => -1; - - internal ManyArgumentsMethodCallExpression(MethodInfo method, IReadOnlyList arguments) : base(method) => - Arguments = arguments; - } - - public sealed class InstanceManyArgumentsMethodCallExpression : ManyArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceManyArgumentsMethodCallExpression(Expression instance, MethodInfo method, IReadOnlyList arguments) - : base(method, arguments) => Object = instance; - } - - public class OneArgumentMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument }; - public override int FewArgumentCount => 1; - - public readonly Expression Argument; - - internal OneArgumentMethodCallExpression(MethodInfo method, Expression argument) : base(method) => - Argument = argument; - } - - public sealed class InstanceOneArgumentMethodCallExpression : OneArgumentMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceOneArgumentMethodCallExpression(Expression instance, MethodInfo method, Expression argument) - : base(method, argument) => Object = instance; - } - - public class TwoArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1 }; - public override int FewArgumentCount => 2; - - public readonly Expression Argument0; - public readonly Expression Argument1; - - internal TwoArgumentsMethodCallExpression(MethodInfo method, Expression argument0, Expression argument1) : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - } - } - - public sealed class InstanceTwoArgumentsMethodCallExpression : TwoArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceTwoArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1) : base(method, argument0, argument1) => Object = instance; - } - - public class ThreeArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2 }; - public override int FewArgumentCount => 3; - - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - - internal ThreeArgumentsMethodCallExpression(MethodInfo method, - Expression argument0, Expression argument1, Expression argument2) : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - } - } - - public sealed class InstanceThreeArgumentsMethodCallExpression : ThreeArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceThreeArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1, Expression argument2) - : base(method, argument0, argument1, argument2) => Object = instance; - } - - public class FourArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3 }; - public override int FewArgumentCount => 4; - - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - - internal FourArgumentsMethodCallExpression(MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3) : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - } - } - - public sealed class InstanceFourArgumentsMethodCallExpression : FourArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceFourArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3) - : base(method, argument0, argument1, argument2, argument3) => Object = instance; - } - - public class FiveArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3, Argument4 }; - public override int FewArgumentCount => 5; - - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - public readonly Expression Argument4; - - internal FiveArgumentsMethodCallExpression(MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3, Expression argument4) - : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - Argument4 = argument4; - } - } - - public sealed class InstanceFiveArgumentsMethodCallExpression : FiveArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceFiveArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3, Expression argument4) - : base(method, argument0, argument1, argument2, argument3, argument4) => Object = instance; - } - - public abstract class MemberExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.MemberAccess; - public readonly MemberInfo Member; - public readonly Expression Expression; - - protected MemberExpression(Expression expression, MemberInfo member) - { - Expression = expression; - Member = member; - } - } - - // todo: specialize to 2 class - with and without object expression - public sealed class PropertyExpression : MemberExpression - { - public override Type Type => PropertyInfo.PropertyType; - public PropertyInfo PropertyInfo => (PropertyInfo)Member; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Property(Expression?.ToExpression(ref exprsConverted), PropertyInfo); - - public override string CodeString - { - get - { - var propIndex = PropertyInfo.DeclaringType.GetTypeInfo().DeclaredProperties.AsArray().GetFirstIndex(PropertyInfo); - return $"Property({Expression?.CodeString ?? "null"}," + NewLine + - $"{PropertyInfo.DeclaringType.ToCode()}.GetTypeInfo().DeclaredProperties.ToArray()[{propIndex}])"; - } - } - - internal PropertyExpression(Expression instance, PropertyInfo property) : - base(instance, property) { } - } - - // todo: specialize to 2 class - with and without object expression - public sealed class FieldExpression : MemberExpression - { - public override Type Type => FieldInfo.FieldType; - public FieldInfo FieldInfo => (FieldInfo)Member; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Field(Expression?.ToExpression(ref exprsConverted), FieldInfo); - - public override string CodeString - { - get - { - var fieldIndex = FieldInfo.DeclaringType.GetTypeInfo().DeclaredFields.AsArray().GetFirstIndex(FieldInfo); - return $"Field({Expression?.CodeString ?? "null"}," + NewLine + - $"{FieldInfo.DeclaringType.ToCode()}.GetTypeInfo().DeclaredProperties.ToArray()[{fieldIndex}])"; - } - } - - internal FieldExpression(Expression instance, FieldInfo field) - : base(instance, field) { } - } - - public abstract class MemberBinding - { - public readonly MemberInfo Member; - - public abstract MemberBindingType BindingType { get; } - public abstract string CodeString { get; } - - internal abstract System.Linq.Expressions.MemberBinding ToMemberBinding(ref LiveCountArray exprsConverted); - - internal MemberBinding(MemberInfo member) - { - Member = member; - } - } - - public sealed class MemberAssignment : MemberBinding - { - public readonly Expression Expression; - - public override MemberBindingType BindingType => MemberBindingType.Assignment; - - internal override System.Linq.Expressions.MemberBinding ToMemberBinding(ref LiveCountArray exprsConverted) => - SysExpr.Bind(Member, Expression.ToExpression(ref exprsConverted)); - - public override string CodeString - { - get - { - var memberIndex = Member.DeclaringType.GetTypeInfo().DeclaredMembers.AsArray().GetFirstIndex(Member); - return $"Bind({Member.DeclaringType.ToCode()}.GetTypeInfo().DeclaredMembers.ToArray()[{memberIndex}]," + NewLine + - $"{Expression.CodeString})"; - } - } - - internal MemberAssignment(MemberInfo member, Expression expression) : base(member) - { - Expression = expression; - } - } - - public sealed class InvocationExpression : ArgumentsExpression - { - public override ExpressionType NodeType => ExpressionType.Invoke; - public override Type Type { get; } - - public readonly Expression Expression; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Invoke(Expression.ToExpression(ref exprsConverted), ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString => - $"Invoke({Expression.CodeString}," + NewLine + - $"{ToParamsCode(Arguments)})"; - - internal InvocationExpression(Expression expression, IReadOnlyList arguments, Type type) : base(arguments) - { - Expression = expression; - Type = type; - } - } - - public sealed class DefaultExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Default; - public override Type Type { get; } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - Type == typeof(void)? SysExpr.Empty() : SysExpr.Default(Type); - - public override string CodeString => - Type == typeof(void) ? "Empty()" : $"Default({Type.ToCode()})"; - - internal DefaultExpression(Type type) - { - Type = type; - } - } - - public sealed class ConditionalExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Conditional; - public override Type Type => _type ?? IfTrue.Type; - - public readonly Expression Test; - public readonly Expression IfTrue; - public readonly Expression IfFalse; - private readonly Type _type; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - _type == null - ? SysExpr.Condition(Test.ToExpression(ref exprsConverted), IfTrue.ToExpression(ref exprsConverted), IfFalse.ToExpression(ref exprsConverted)) - : SysExpr.Condition(Test.ToExpression(ref exprsConverted), IfTrue.ToExpression(ref exprsConverted), IfFalse.ToExpression(ref exprsConverted), _type); - - public override string CodeString => - _type == null - ? $"Condition({Test.CodeString}," + NewLine + $"{IfTrue.CodeString}," + NewLine + $"{IfFalse.CodeString})" - : $"Condition({Test.CodeString}," + NewLine + $"{IfTrue.CodeString}," + NewLine + $"{IfFalse.CodeString}," + NewLine + $"{_type.ToCode()})"; - - internal ConditionalExpression(Expression test, Expression ifTrue, Expression ifFalse, Type type = null) - { - Test = test; - IfTrue = ifTrue; - IfFalse = ifFalse; - _type = type; - } - } - - /// For indexer property or array access. - public sealed class IndexExpression : ArgumentsExpression - { - public override ExpressionType NodeType => ExpressionType.Index; - public override Type Type => Indexer != null ? Indexer.PropertyType : Object.Type.GetElementType(); - - public readonly Expression Object; - public readonly PropertyInfo Indexer; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.MakeIndex(Object.ToExpression(ref exprsConverted), Indexer, ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString - { - get - { - var propIndex = Indexer.DeclaringType.GetTypeInfo().DeclaredProperties.AsArray().GetFirstIndex(Indexer); - return $"MakeIndex({Object.CodeString}," + NewLine + - $"{Indexer.DeclaringType.ToCode()}.GetTypeInfo().DeclaredProperties.ToArray()[{propIndex}], {ToParamsCode(Arguments)})"; - } - } - - internal IndexExpression(Expression @object, PropertyInfo indexer, IReadOnlyList arguments) - : base(arguments) - { - Object = @object; - Indexer = indexer; - } - } - - /// Optimized version for the specific block structure - public sealed class OneVariableTwoExpressionBlockExpression : Expression - { - public static explicit operator BlockExpression(OneVariableTwoExpressionBlockExpression x) => - Block(new[] { x.Variable }, x.Expression1, x.Expression2); - - public override ExpressionType NodeType => ExpressionType.Block; - public override Type Type => Expression2.Type; - - public new readonly ParameterExpression Variable; - public readonly Expression Expression1; - public readonly Expression Expression2; - public Expression Result => Expression2; - - internal OneVariableTwoExpressionBlockExpression(ParameterExpression variable, Expression expression1, Expression expression2) - { - Variable = variable; - Expression1 = expression1; - Expression2 = expression2; - } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - ((BlockExpression)this).CreateSysExpression(ref exprsConverted); - - public override string CodeString => ((BlockExpression)this).CodeString; - } - - public sealed class BlockExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Block; - public override Type Type { get; } - - public readonly IReadOnlyList Variables; - public readonly IReadOnlyList Expressions; - public Expression Result => Expressions[Expressions.Count - 1]; - - internal BlockExpression(Type type, IReadOnlyList variables, IReadOnlyList expressions) - { - Variables = variables ?? Tools.Empty(); - Expressions = expressions ?? Tools.Empty(); - Type = type; - } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Block(Type, - ParameterExpression.ToParameterExpressions(Variables, ref exprsConverted), - ToExpressions(Expressions, ref exprsConverted)); - - public override string CodeString => - "Block(" + NewLine + - $"{Type.ToCode()}," + NewLine + - $"new ParameterExpression[]{{ {(Variables.Count == 0 ? "" : ToParamsCode(Variables))} }}," + NewLine + - $"{ToParamsCode(Expressions)})"; - } - - public sealed class LoopExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Loop; - - public override Type Type => typeof(void); - - public readonly Expression Body; - public readonly LabelTarget BreakLabel; - public readonly LabelTarget ContinueLabel; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - BreakLabel == null ? SysExpr.Loop(Body.ToExpression(ref exprsConverted)) : - ContinueLabel == null ? SysExpr.Loop(Body.ToExpression(ref exprsConverted), BreakLabel) : - SysExpr.Loop(Body.ToExpression(ref exprsConverted), BreakLabel, ContinueLabel); - - public override string CodeString => - BreakLabel == null ? $"Loop({Body.CodeString})" : - ContinueLabel == null ? $"Loop({Body.CodeString}," + NewLine + "Label(\"break\"))" : - $"Loop({Body.CodeString}," + NewLine + "Label(\"break\"), Label(\"continue\"))"; - - internal LoopExpression(Expression body, LabelTarget breakLabel, LabelTarget continueLabel) - { - Body = body; - BreakLabel = breakLabel; - ContinueLabel = continueLabel; - } - } - - public sealed class TryExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Try; - public override Type Type => Body.Type; - - public readonly Expression Body; - public IReadOnlyList Handlers => _handlers; - private readonly CatchBlock[] _handlers; - public readonly Expression Finally; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - Finally == null ? - SysExpr.TryCatch(Body.ToExpression(ref exprsConverted), - ToCatchBlocks(_handlers, ref exprsConverted)) : - Handlers == null ? - SysExpr.TryFinally(Body.ToExpression(ref exprsConverted), - Finally.ToExpression(ref exprsConverted)) : - SysExpr.TryCatchFinally(Body.ToExpression(ref exprsConverted), - Finally.ToExpression(ref exprsConverted), ToCatchBlocks(_handlers, ref exprsConverted)); - - private static System.Linq.Expressions.CatchBlock ToCatchBlock( - ref CatchBlock cb, ref LiveCountArray exprsConverted) => - SysExpr.MakeCatchBlock(cb.Test, - (System.Linq.Expressions.ParameterExpression)cb.Variable?.ToExpression(ref exprsConverted), - cb.Body.ToExpression(ref exprsConverted), - cb.Filter?.ToExpression(ref exprsConverted)); - - private static System.Linq.Expressions.CatchBlock[] ToCatchBlocks( - CatchBlock[] hs, ref LiveCountArray exprsConverted) - { - if (hs == null) - return Tools.Empty(); - var catchBlocks = new System.Linq.Expressions.CatchBlock[hs.Length]; - for (var i = 0; i < hs.Length; ++i) - catchBlocks[i] = ToCatchBlock(ref hs[i], ref exprsConverted); - return catchBlocks; - } - - public override string CodeString => - Finally == null ? $"TryCatch({Body.CodeString}," + NewLine + $"{ToCatchBlocksCode(Handlers)})" : - Handlers == null ? $"TryFinally({Body.CodeString}, " + NewLine + $"{Finally.CodeString})" : - $"TryCatchFinally({Body.CodeString}," + NewLine + $"{Finally.CodeString}," + NewLine + $"{ToCatchBlocksCode(Handlers)})"; - - private static string ToCatchBlocksCode(IReadOnlyList hs) - { - if (hs.Count == 0) - return "new CatchBlock[0]"; - - var s = ""; - for (var i = 0; i < hs.Count; i++) - { - if (i > 0) - s += ", " + NewLine; - s += hs[i].CodeString; - } - - return s; - } - - internal TryExpression(Expression body, Expression @finally, CatchBlock[] handlers) - { - Body = body; - _handlers = handlers; - Finally = @finally; - } - } - - public struct CatchBlock - { - public readonly ParameterExpression Variable; - public readonly Expression Body; - public readonly Expression Filter; - public readonly Type Test; - - internal CatchBlock(ParameterExpression variable, Expression body, Expression filter, Type test) - { - Variable = variable; - Body = body; - Filter = filter; - Test = test; - } - - internal string CodeString => - $"MakeCatchBlock({Test.ToCode()}, {Variable?.CodeString ?? "null"}," + NewLine + - $"{Body.CodeString}," + NewLine + - $"{Filter?.CodeString ?? "null"})"; - } - - public sealed class LabelExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Label; - public override Type Type => Target.Type; - - public readonly LabelTarget Target; - public readonly Expression DefaultValue; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - DefaultValue == null ? SysExpr.Label(Target) : SysExpr.Label(Target, DefaultValue.ToExpression(ref exprsConverted)); - - // todo: Introduce proper LabelTarget instead of system one - public override string CodeString => - DefaultValue == null ? $"Label({Target})" : $"Label({Target}, {DefaultValue.CodeString})"; - - internal LabelExpression(LabelTarget target, Expression defaultValue) - { - Target = target; - DefaultValue = defaultValue; - } - } - - public sealed class GotoExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Goto; - public override Type Type { get; } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - Value == null - ? SysExpr.Goto(Target, Type) - : SysExpr.Goto(Target, Value.ToExpression(ref exprsConverted), Type); - - public override string CodeString => - Value == null - ? $"Goto({Target}, {Type.ToCode()})" - : $"Goto({Target}, {Value.CodeString}, {Type.ToCode()})"; - - public readonly Expression Value; - public readonly LabelTarget Target; - public readonly GotoExpressionKind Kind; - - internal GotoExpression(GotoExpressionKind kind, LabelTarget target, Expression value, Type type) - { - Type = type; - Kind = kind; - Value = value; - Target = target; - } - } - - public struct SwitchCase - { - public readonly IReadOnlyList TestValues; - public readonly Expression Body; - - public string CodeString => - $"SwitchCase({Body.CodeString}, {Expression.ToParamsCode(TestValues)})"; - - public SwitchCase(Expression body, IEnumerable testValues) - { - Body = body; - TestValues = testValues.AsReadOnlyList(); - } - } - - public class SwitchExpression : Expression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Switch(SwitchValue.ToExpression(ref exprsConverted), - DefaultBody.ToExpression(ref exprsConverted), Comparison, - ToSwitchCaseExpressions(_cases, ref exprsConverted)); - - public override string CodeString - { - get - { - var methodIndex = Comparison.DeclaringType.GetTypeInfo().DeclaredMethods.AsArray().GetFirstIndex(Comparison); - return $"Switch({SwitchValue.CodeString}," + NewLine + - $"{DefaultBody.CodeString}," + NewLine + - $"{Comparison.DeclaringType.ToCode()}.GetTypeInfo().DeclaredMethods.ToArray()[{methodIndex}]," + NewLine + - $"{ToSwitchCasesCode(_cases)})"; - } - } - - internal static System.Linq.Expressions.SwitchCase ToSwitchCase(ref SwitchCase sw, ref LiveCountArray exprsConverted) => - SysExpr.SwitchCase(sw.Body.ToExpression(ref exprsConverted), ToExpressions(sw.TestValues, ref exprsConverted)); - - internal static System.Linq.Expressions.SwitchCase[] ToSwitchCaseExpressions( - SwitchCase[] sw, ref LiveCountArray exprsConverted) - { - if (sw.Length == 0) - return Tools.Empty(); - - var result = new System.Linq.Expressions.SwitchCase[sw.Length]; - for (var i = 0; i < result.Length; ++i) - result[i] = ToSwitchCase(ref sw[i], ref exprsConverted); - return result; - } - - internal static string ToSwitchCasesCode(IReadOnlyList items) - { - if (items.Count == 0) - return "new SwitchCase[0]"; - - var s = ""; - for (var i = 0; i < items.Count; i++) - { - if (i > 0) - s += "," + NewLine; - s += items[i].CodeString; - } - - return s; - } - - public readonly Expression SwitchValue; - public IReadOnlyList Cases => _cases; - private readonly SwitchCase[] _cases; - public readonly Expression DefaultBody; - public readonly MethodInfo Comparison; - - public SwitchExpression(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, SwitchCase[] cases) - { - NodeType = ExpressionType.Switch; - Type = type; - SwitchValue = switchValue; - DefaultBody = defaultBody; - Comparison = comparison; - _cases = cases; - } - } - - public class LambdaExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Lambda; - public override Type Type { get; } - - public readonly Type ReturnType; - public readonly Expression Body; - public virtual IReadOnlyList Parameters => Tools.Empty(); - - public System.Linq.Expressions.LambdaExpression ToLambdaExpression() => - (System.Linq.Expressions.LambdaExpression)ToExpression(); - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Lambda(Type, Body.ToExpression(ref exprsConverted), ParameterExpression.ToParameterExpressions(Parameters, ref exprsConverted)); - - public override string CodeString => - $"Lambda({Type.ToCode()}," + NewLine + - $"{Body.CodeString}," + NewLine + - $"{ToParamsCode(Parameters)})"; - - internal LambdaExpression(Type delegateType, Expression body, Type returnType) - { - Body = body; - ReturnType = returnType; - - if (delegateType != null && delegateType != typeof(Delegate)) - Type = delegateType; - else - Type = Tools.GetFuncOrActionType(Tools.Empty(), ReturnType); - } - } - - public sealed class ManyParametersLambdaExpression : LambdaExpression - { - public override IReadOnlyList Parameters { get; } - - internal ManyParametersLambdaExpression(Type delegateType, Expression body, IReadOnlyList parameters, Type returnType) - : base(delegateType, body, returnType) => Parameters = parameters; - } - - public class Expression : LambdaExpression - { - public new System.Linq.Expressions.Expression ToLambdaExpression() - { - var exprsConverted = new LiveCountArray(Tools.Empty()); - return SysExpr.Lambda(Body.ToExpression(ref exprsConverted), - ParameterExpression.ToParameterExpressions(Parameters, ref exprsConverted)); - } - - internal Expression(Expression body, Type returnType) - : base(typeof(TDelegate), body, returnType) { } - } - - public sealed class ManyParametersExpression : Expression - { - public override IReadOnlyList Parameters { get; } - - internal ManyParametersExpression(Expression body, IReadOnlyList parameters, Type returnType) - : base(body, returnType) => Parameters = parameters; - } -} - -#endif diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/FastExpressionCompiler.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/FastExpressionCompiler.cs deleted file mode 100644 index 674de92..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/FastExpressionCompiler.cs +++ /dev/null @@ -1,4739 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2016-2020 Maksim Volkau - -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 AddOrUpdateServiceFactory -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. -*/ - -// ReSharper disable CoVariantArrayConversion - -/* -// Lists the target platforms that are Not supported by FEC - simplifies the direct referencing of Expression.cs file -*/ -#if !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_FAST_EXPRESSION_COMPILER -#endif - -#if !SUPPORTS_FAST_EXPRESSION_COMPILER -namespace FastExpressionCompiler.LightExpression -{ - using System; - using System.Linq.Expressions; - - /// Polyfill for absence of FastExpressionCompiler: https://github.com/dadhi/FastExpressionCompiler - public static class ExpressionCompiler - { - internal static object TryCompileBoundToFirstClosureParam(Type delegateType, - Expression bodyExpr, ParameterExpression[] paramExprs, Type[] closurePlusParamTypes, Type returnType) => null; - - internal static Func CompileFast(this Expression> lambdaExpr) => lambdaExpr.Compile(); - - internal class ArrayClosure {} - } -} -#else -// ReSharper disable CoVariantArrayConversion -namespace FastExpressionCompiler.LightExpression -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Reflection; - using System.Reflection.Emit; - using System.Threading; - - /// Compiles expression to delegate ~20 times faster than Expression.Compile. - /// Partial to extend with your things when used as source file. - // ReSharper disable once PartialTypeWithSinglePart - public static partial class ExpressionCompiler - { -#region Expression.CompileFast overloads for Delegate, Func, and Action - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static TDelegate CompileFast(this LambdaExpression lambdaExpr, - bool ifFastFailedReturnNull = false) where TDelegate : class => - (TDelegate)(TryCompileBoundToFirstClosureParam(typeof(TDelegate) == typeof(Delegate) ? lambdaExpr.Type : typeof(TDelegate), - lambdaExpr.Body, lambdaExpr.Parameters, GetClosureTypeToParamTypes(lambdaExpr.Parameters), lambdaExpr.ReturnType) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys())); - - /// Compiles a static method to the passed IL Generator. - /// Could be used as alternative for `CompileToMethod` like this . - /// Check `IssueTests.Issue179_Add_something_like_LambdaExpression_CompileToMethod.cs` for example. - public static bool CompileFastToIL(this LambdaExpression lambdaExpr, ILGenerator il, bool ifFastFailedReturnNull = false) - { - var closureInfo = new ClosureInfo(ClosureStatus.ShouldBeStaticMethod); - - var parentFlags = lambdaExpr.ReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(lambdaExpr.Body, lambdaExpr.Parameters, il, ref closureInfo, parentFlags)) - return false; - - il.Emit(OpCodes.Ret); - return true; - } - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Delegate CompileFast(this LambdaExpression lambdaExpr, bool ifFastFailedReturnNull = false) => - (Delegate)TryCompileBoundToFirstClosureParam(lambdaExpr.Type, lambdaExpr.Body, lambdaExpr.Parameters, - GetClosureTypeToParamTypes(lambdaExpr.Parameters), lambdaExpr.ReturnType) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Unifies Compile for System.Linq.Expressions and FEC.LightExpression - public static TDelegate CompileSys(this Expression lambdaExpr) where TDelegate : class => - lambdaExpr - .ToLambdaExpression() - .Compile(); - - /// Unifies Compile for System.Linq.Expressions and FEC.LightExpression - public static Delegate CompileSys(this LambdaExpression lambdaExpr) => - lambdaExpr - .ToLambdaExpression() - .Compile(); - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static TDelegate CompileFast(this Expression lambdaExpr, - bool ifFastFailedReturnNull = false) - where TDelegate : class => ((LambdaExpression)lambdaExpr).CompileFast(ifFastFailedReturnNull); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, _closureAsASingleParamType, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1), typeof(T2) }, - typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, _closureAsASingleParamType, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1), typeof(T2) }, - typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - -#endregion - - /// Tries to compile lambda expression to - public static TDelegate TryCompile(this LambdaExpression lambdaExpr) where TDelegate : class => - (TDelegate)TryCompileBoundToFirstClosureParam(typeof(TDelegate) == typeof(Delegate) ? lambdaExpr.Type : typeof(TDelegate), - lambdaExpr.Body, lambdaExpr.Parameters, GetClosureTypeToParamTypes(lambdaExpr.Parameters), lambdaExpr.ReturnType); - - /// Tries to compile lambda expression to - /// with the provided closure object and constant expressions (or lack there of) - - /// Constant expression should be the in order of Fields in closure object! - /// Note 1: Use it on your own risk - FEC won't verify the expression is compile-able with passed closure, it is up to you! - /// Note 2: The expression with NESTED LAMBDA IS NOT SUPPORTED! - /// Note 3: `Label` and `GoTo` are not supported in this case, because they need first round to collect out-of-order labels - public static TDelegate TryCompileWithPreCreatedClosure(this LambdaExpression lambdaExpr, - params ConstantExpression[] closureConstantsExprs) - where TDelegate : class - { - var constValues = new object[closureConstantsExprs.Length]; - for (var i = 0; i < constValues.Length; i++) - constValues[i] = closureConstantsExprs[i].Value; - - var closureInfo = new ClosureInfo(ClosureStatus.UserProvided | ClosureStatus.HasClosure, constValues); - - var closurePlusParamTypes = GetClosureTypeToParamTypes(lambdaExpr.Parameters); - var method = new DynamicMethod(string.Empty, lambdaExpr.ReturnType, closurePlusParamTypes, - typeof(ExpressionCompiler), skipVisibility: true); - - var il = method.GetILGenerator(); - - EmittingVisitor.EmitLoadConstantsAndNestedLambdasIntoVars(il, ref closureInfo); - - var parentFlags = lambdaExpr.ReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(lambdaExpr.Body, lambdaExpr.Parameters, il, ref closureInfo, parentFlags)) - return null; - il.Emit(OpCodes.Ret); - - var delegateType = typeof(TDelegate) != typeof(Delegate) ? typeof(TDelegate) : lambdaExpr.Type; - var @delegate = (TDelegate)(object)method.CreateDelegate(delegateType, new ArrayClosure(constValues)); - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return @delegate; - } - - /// Tries to compile expression to "static" delegate, skipping the step of collecting the closure object. - public static TDelegate TryCompileWithoutClosure(this LambdaExpression lambdaExpr) - where TDelegate : class - { - var closureInfo = new ClosureInfo(ClosureStatus.UserProvided); - var closurePlusParamTypes = GetClosureTypeToParamTypes(lambdaExpr.Parameters); - - var method = new DynamicMethod(string.Empty, lambdaExpr.ReturnType, closurePlusParamTypes, - typeof(ArrayClosure), skipVisibility: true); - - var il = method.GetILGenerator(); - var parentFlags = lambdaExpr.ReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(lambdaExpr.Body, lambdaExpr.Parameters, il, ref closureInfo, parentFlags)) - return null; - il.Emit(OpCodes.Ret); - - var delegateType = typeof(TDelegate) != typeof(Delegate) ? typeof(TDelegate) : lambdaExpr.Type; - var @delegate = (TDelegate)(object)method.CreateDelegate(delegateType, EmptyArrayClosure); - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return @delegate; - } - -#region Obsolete - - /// Obsolete - [Obsolete("Not used - candidate for removal")] - public static TDelegate TryCompile( - Expression bodyExpr, IReadOnlyList paramExprs, Type[] paramTypes, Type returnType) - where TDelegate : class => - (TDelegate)TryCompile(typeof(TDelegate), bodyExpr, paramExprs, paramTypes, returnType); - - /// Obsolete - [Obsolete("Not used - candidate for removal")] - public static object TryCompile(Type delegateType, - Expression bodyExpr, IReadOnlyList paramExprs, Type[] paramTypes, Type returnType) => - TryCompileBoundToFirstClosureParam( - delegateType != typeof(Delegate) ? delegateType : Tools.GetFuncOrActionType(paramTypes, returnType), - bodyExpr, paramExprs, GetClosureTypeToParamTypes(paramExprs), returnType); - -#endregion - - internal static object TryCompileBoundToFirstClosureParam(Type delegateType, - Expression bodyExpr, IReadOnlyList paramExprs, Type[] closurePlusParamTypes, Type returnType) - { - var closureInfo = new ClosureInfo(ClosureStatus.ToBeCollected); - if (!TryCollectBoundConstants(ref closureInfo, bodyExpr, paramExprs, false, ref closureInfo)) - return null; - - var nestedLambdas = closureInfo.NestedLambdas; - if (nestedLambdas.Length != 0) - for (var i = 0; i < nestedLambdas.Length; ++i) - if (!TryCompileNestedLambda(ref closureInfo, i)) - return null; - - var closure = (closureInfo.Status & ClosureStatus.HasClosure) == 0 - ? EmptyArrayClosure - : new ArrayClosure(closureInfo.GetArrayOfConstantsAndNestedLambdas()); - - var method = new DynamicMethod(string.Empty, - returnType, closurePlusParamTypes, typeof(ArrayClosure), true); - - var il = method.GetILGenerator(); - - if (closure.ConstantsAndNestedLambdas != null) - EmittingVisitor.EmitLoadConstantsAndNestedLambdasIntoVars(il, ref closureInfo); - - var parentFlags = returnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(bodyExpr, paramExprs, il, ref closureInfo, parentFlags)) - return null; - - il.Emit(OpCodes.Ret); - - var @delegate = method.CreateDelegate(delegateType, closure); - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return @delegate; - } - - private static Type[] PrependClosureTypeToParamTypes(IReadOnlyList paramExprs) - { - var count = paramExprs.Count; - var closureAndParamTypes = new Type[count + 1]; - closureAndParamTypes[0] = typeof(ArrayClosure); - for (var i = 0; i < count; i++) - { - var parameterExpr = paramExprs[i]; - closureAndParamTypes[i + 1] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type; - } - return closureAndParamTypes; - } - - private static readonly Type[] _closureAsASingleParamType = { typeof(ArrayClosure) }; - private static readonly Type[][] _closureTypePlusParamTypesPool = new Type[8][]; - - private static Type[] GetClosureTypeToParamTypes(IReadOnlyList paramExprs) - { - var paramCount = paramExprs.Count; - if (paramCount == 0) - return _closureAsASingleParamType; - - if (paramCount < 8) - { - var closureAndParamTypes = Interlocked.Exchange(ref _closureTypePlusParamTypesPool[paramCount], null); - if (closureAndParamTypes != null) - { - for (var i = 0; i < paramExprs.Count; i++) - { - var parameterExpr = paramExprs[i]; - closureAndParamTypes[i + 1] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type; - } - return closureAndParamTypes; - } - } - - return PrependClosureTypeToParamTypes(paramExprs); - } - - private static void ReturnClosureTypeToParamTypesToPool(Type[] closurePlusParamTypes) - { - var paramCount = closurePlusParamTypes.Length - 1; - if (paramCount != 0 && paramCount < 8) - Interlocked.Exchange(ref _closureTypePlusParamTypesPool[paramCount], closurePlusParamTypes); - } - - private struct BlockInfo - { - public object VarExprs; // ParameterExpression | IReadOnlyList - public int[] VarIndexes; - } - - [Flags] - private enum ClosureStatus - { - ToBeCollected = 1, - UserProvided = 1 << 1, - HasClosure = 1 << 2, - ShouldBeStaticMethod = 1 << 3 - } - - /// Track the info required to build a closure object + some context information not directly related to closure. - private struct ClosureInfo - { - public bool LastEmitIsAddress; - - /// Helpers to know if a Return GotoExpression's Label should be emitted. - /// First set bit is ContainsReturnGoto, the rest is ReturnLabelIndex - private int[] _tryCatchFinallyInfos; - public int CurrentTryCatchFinallyIndex; - - /// Tracks the stack of blocks where are we in emit phase - private LiveCountArray _blockStack; - - /// Dictionary for the used Labels in IL - private KeyValuePair[] _labels; - - public ClosureStatus Status; - - /// Constant expressions to find an index (by reference) of constant expression from compiled expression. - public LiveCountArray Constants; - - /// Parameters not passed through lambda parameter list But used inside lambda body. - /// The top expression should Not contain not passed parameters. - public ParameterExpression[] NonPassedParameters; - - /// All nested lambdas recursively nested in expression - public NestedLambdaInfo[] NestedLambdas; - - /// Constant usage count and variable index - public LiveCountArray ConstantUsage; - - /// Populates info directly with provided closure object and constants. - public ClosureInfo(ClosureStatus status, object[] constValues = null) - { - Status = status; - - Constants = new LiveCountArray(constValues ?? Tools.Empty()); - ConstantUsage = new LiveCountArray(constValues == null ? Tools.Empty() : new int[constValues.Length]); - - NonPassedParameters = Tools.Empty(); - NestedLambdas = Tools.Empty(); - - LastEmitIsAddress = false; - CurrentTryCatchFinallyIndex = -1; - _tryCatchFinallyInfos = null; - _labels = null; - _blockStack = new LiveCountArray(Tools.Empty()); - } - - public void AddConstant(object value) - { - Status |= ClosureStatus.HasClosure; - - var constItems = Constants.Items; - var constIndex = Constants.Count - 1; - while (constIndex != -1 && !ReferenceEquals(constItems[constIndex], value)) - --constIndex; - if (constIndex == -1) - { - Constants.PushSlot(value); - ConstantUsage.PushSlot(1); - } - else - { - ++ConstantUsage.Items[constIndex]; - } - } - - public void AddNonPassedParam(ParameterExpression expr) - { - Status |= ClosureStatus.HasClosure; - - if (NonPassedParameters.Length == 0) - { - NonPassedParameters = new[] { expr }; - return; - } - - var count = NonPassedParameters.Length; - for (var i = 0; i < count; ++i) - if (ReferenceEquals(NonPassedParameters[i], expr)) - return; - - if (NonPassedParameters.Length == 1) - NonPassedParameters = new[] { NonPassedParameters[0], expr }; - else if (NonPassedParameters.Length == 2) - NonPassedParameters = new[] { NonPassedParameters[0], NonPassedParameters[1], expr }; - else - { - var newItems = new ParameterExpression[count + 1]; - Array.Copy(NonPassedParameters, 0, newItems, 0, count); - newItems[count] = expr; - NonPassedParameters = newItems; - } - } - - public void AddNestedLambda(NestedLambdaInfo nestedLambdaInfo) - { - Status |= ClosureStatus.HasClosure; - - var nestedLambdas = NestedLambdas; - var count = nestedLambdas.Length; - if (count == 0) - NestedLambdas = new[] { nestedLambdaInfo }; - else if (count == 1) - NestedLambdas = new[] { nestedLambdas[0], nestedLambdaInfo }; - else if (count == 2) - NestedLambdas = new[] { nestedLambdas[0], nestedLambdas[1], nestedLambdaInfo }; - else - { - var newNestedLambdas = new NestedLambdaInfo[count + 1]; - Array.Copy(nestedLambdas, 0, newNestedLambdas, 0, count); - newNestedLambdas[count] = nestedLambdaInfo; - NestedLambdas = newNestedLambdas; - } - } - - public void AddLabel(LabelTarget labelTarget) - { - if (labelTarget != null && - GetLabelIndex(labelTarget) == -1) - _labels = _labels.WithLast(new KeyValuePair(labelTarget, null)); - } - - public Label GetOrCreateLabel(LabelTarget labelTarget, ILGenerator il) => - GetOrCreateLabel(GetLabelIndex(labelTarget), il); - - public Label GetOrCreateLabel(int index, ILGenerator il) - { - var labelPair = _labels[index]; - var label = labelPair.Value; - if (!label.HasValue) - _labels[index] = new KeyValuePair(labelPair.Key, label = il.DefineLabel()); - return label.Value; - } - - public int GetLabelIndex(LabelTarget labelTarget) - { - if (_labels != null) - for (var i = 0; i < _labels.Length; ++i) - if (_labels[i].Key == labelTarget) - return i; - return -1; - } - - public void AddTryCatchFinallyInfo() - { - ++CurrentTryCatchFinallyIndex; - var infos = _tryCatchFinallyInfos; - if (infos == null) - _tryCatchFinallyInfos = new int[1]; - else if (infos.Length == 1) - _tryCatchFinallyInfos = new[] { infos[0], 0 }; - else if (infos.Length == 2) - _tryCatchFinallyInfos = new[] { infos[0], infos[1], 0 }; - else - { - var sourceLength = infos.Length; - var newInfos = new int[sourceLength + 1]; - Array.Copy(infos, newInfos, sourceLength); - _tryCatchFinallyInfos = newInfos; - } - } - - public void MarkAsContainsReturnGotoExpression() - { - if (CurrentTryCatchFinallyIndex != -1) - _tryCatchFinallyInfos[CurrentTryCatchFinallyIndex] |= 1; - } - - public void MarkReturnLabelIndex(int index) - { - if (CurrentTryCatchFinallyIndex != -1) - _tryCatchFinallyInfos[CurrentTryCatchFinallyIndex] |= index << 1; - } - - public bool TryCatchFinallyContainsReturnGotoExpression() => - _tryCatchFinallyInfos != null && (_tryCatchFinallyInfos[++CurrentTryCatchFinallyIndex] & 1) != 0; - - public object[] GetArrayOfConstantsAndNestedLambdas() - { - var constCount = Constants.Count; - var nestedLambdas = NestedLambdas; - if (constCount == 0) - { - if (nestedLambdas.Length == 0) - return null; - - var nestedLambdaItems = new object[nestedLambdas.Length]; - for (var i = 0; i < nestedLambdas.Length; i++) - { - var nestedLambda = nestedLambdas[i]; - if (nestedLambda.ClosureInfo.NonPassedParameters.Length == 0) - nestedLambdaItems[i] = nestedLambda.Lambda; - else - nestedLambdaItems[i] = new NestedLambdaWithConstantsAndNestedLambdas( - nestedLambda.Lambda, nestedLambda.ClosureInfo.GetArrayOfConstantsAndNestedLambdas()); - } - - return nestedLambdaItems; - } - - var constItems = Constants.Items; - if (nestedLambdas.Length == 0) - return constItems; - - var itemCount = constCount + nestedLambdas.Length; - - var closureItems = constItems; - if (itemCount > constItems.Length) - { - closureItems = new object[itemCount]; - for (var i = 0; i < constCount; ++i) - closureItems[i] = constItems[i]; - } - - for (var i = 0; i < nestedLambdas.Length; i++) - { - var nestedLambda = nestedLambdas[i]; - if (nestedLambda.ClosureInfo.NonPassedParameters.Length == 0) - closureItems[constCount + i] = nestedLambda.Lambda; - else - closureItems[constCount + i] = new NestedLambdaWithConstantsAndNestedLambdas( - nestedLambda.Lambda, nestedLambda.ClosureInfo.GetArrayOfConstantsAndNestedLambdas()); - } - - return closureItems; - } - - /// LocalVar maybe a `null` in collecting phase when we only need to decide if ParameterExpression is an actual parameter or variable - public void PushBlockWithVars(ParameterExpression blockVarExpr) - { - ref var block = ref _blockStack.PushSlot(); - block.VarExprs = blockVarExpr; - } - - public void PushBlockWithVars(ParameterExpression blockVarExpr, int varIndex) - { - ref var block = ref _blockStack.PushSlot(); - block.VarExprs = blockVarExpr; - block.VarIndexes = new[] { varIndex }; - } - - /// LocalVars maybe a `null` in collecting phase when we only need to decide if ParameterExpression is an actual parameter or variable - public void PushBlockWithVars(IReadOnlyList blockVarExprs, int[] localVarIndexes = null) - { - ref var block = ref _blockStack.PushSlot(); - block.VarExprs = blockVarExprs; - block.VarIndexes = localVarIndexes; - } - - public void PushBlockAndConstructLocalVars(IReadOnlyList blockVarExprs, ILGenerator il) - { - var localVars = new int[blockVarExprs.Count]; - for (var i = 0; i < localVars.Length; i++) - localVars[i] = il.GetNextLocalVarIndex(blockVarExprs[i].Type); - - PushBlockWithVars(blockVarExprs, localVars); - } - - public void PopBlock() => _blockStack.Pop(); - - public bool IsLocalVar(object varParamExpr) - { - for (var i = _blockStack.Count - 1; i > -1; --i) - { - var varExprObj = _blockStack.Items[i].VarExprs; - if (ReferenceEquals(varExprObj, varParamExpr)) - return true; - - if (varExprObj is IReadOnlyList varExprs) - for (var j = 0; j < varExprs.Count; j++) - if (ReferenceEquals(varExprs[j], varParamExpr)) - return true; - } - - return false; - } - - public int GetDefinedLocalVarOrDefault(ParameterExpression varParamExpr) - { - for (var i = _blockStack.Count - 1; i > -1; --i) - { - ref var block = ref _blockStack.Items[i]; - var varExprObj = block.VarExprs; - - if (ReferenceEquals(varExprObj, varParamExpr)) - return block.VarIndexes[0]; - - if (varExprObj is IReadOnlyList varExprs) - for (var j = 0; j < varExprs.Count; j++) - if (ReferenceEquals(varExprs[j], varParamExpr)) - return block.VarIndexes[j]; - } - return -1; - } - - public bool IsTryReturnLabel(int index) - { - var tryCatchFinallyInfos = _tryCatchFinallyInfos; - if (tryCatchFinallyInfos != null) - for (var i = 0; i < tryCatchFinallyInfos.Length; ++i) - if (tryCatchFinallyInfos[i] >> 1 == index) - return true; - return false; - } - } - -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - - public static readonly ArrayClosure EmptyArrayClosure = new ArrayClosure(null); - - public static FieldInfo ArrayClosureArrayField = - typeof(ArrayClosure).GetTypeInfo().GetDeclaredField(nameof(ArrayClosure.ConstantsAndNestedLambdas)); - - public static FieldInfo ArrayClosureWithNonPassedParamsField = - typeof(ArrayClosureWithNonPassedParams).GetTypeInfo().GetDeclaredField(nameof(ArrayClosureWithNonPassedParams.NonPassedParams)); - - public static ConstructorInfo ArrayClosureWithNonPassedParamsConstructor = - typeof(ArrayClosureWithNonPassedParams).GetTypeInfo().DeclaredConstructors.GetFirst(); - - public class ArrayClosure - { - public readonly object[] ConstantsAndNestedLambdas; - public ArrayClosure(object[] constantsAndNestedLambdas) => ConstantsAndNestedLambdas = constantsAndNestedLambdas; - } - - public sealed class ArrayClosureWithNonPassedParams : ArrayClosure - { - public readonly object[] NonPassedParams; - - public ArrayClosureWithNonPassedParams(object[] constantsAndNestedLambdas, object[] nonPassedParams) : base(constantsAndNestedLambdas) => - NonPassedParams = nonPassedParams; - } - - public sealed class NestedLambdaWithConstantsAndNestedLambdas - { - public static FieldInfo NestedLambdaField = - typeof(NestedLambdaWithConstantsAndNestedLambdas).GetTypeInfo().GetDeclaredField(nameof(NestedLambda)); - - public static FieldInfo ConstantsAndNestedLambdasField = - typeof(NestedLambdaWithConstantsAndNestedLambdas).GetTypeInfo().GetDeclaredField(nameof(ConstantsAndNestedLambdas)); - - public readonly object NestedLambda; - public readonly object ConstantsAndNestedLambdas; - public NestedLambdaWithConstantsAndNestedLambdas(object nestedLambda, object constantsAndNestedLambdas) - { - NestedLambda = nestedLambda; - ConstantsAndNestedLambdas = constantsAndNestedLambdas; - } - } - - private sealed class NestedLambdaInfo - { - public readonly LambdaExpression LambdaExpression; - public ClosureInfo ClosureInfo; - public object Lambda; - public int UsageCountOrVarIndex; - - public NestedLambdaInfo(LambdaExpression lambdaExpression) - { - LambdaExpression = lambdaExpression; - ClosureInfo = new ClosureInfo(ClosureStatus.ToBeCollected); - Lambda = null; - } - } - - internal static class CurryClosureFuncs - { - public static readonly MethodInfo[] Methods = - typeof(CurryClosureFuncs).GetTypeInfo().DeclaredMethods.AsArray(); - - public static Func Curry(Func f, C c) => - () => f(c); - - public static Func Curry(Func f, C c) => - t1 => f(c, t1); - - public static Func Curry(Func f, C c) => - (t1, t2) => f(c, t1, t2); - - public static Func Curry(Func f, C c) => - (t1, t2, t3) => f(c, t1, t2, t3); - - public static Func Curry(Func f, C c) => - (t1, t2, t3, t4) => f(c, t1, t2, t3, t4); - - public static Func Curry(Func f, - C c) => (t1, t2, t3, t4, t5) => f(c, t1, t2, t3, t4, t5); - - public static Func - Curry(Func f, C c) => - (t1, t2, t3, t4, t5, t6) => f(c, t1, t2, t3, t4, t5, t6); - } - - internal static class CurryClosureActions - { - public static readonly MethodInfo[] Methods = - typeof(CurryClosureActions).GetTypeInfo().DeclaredMethods.AsArray(); - - public static Action Curry(Action a, C c) => - () => a(c); - - public static Action Curry(Action f, C c) => - t1 => f(c, t1); - - public static Action Curry(Action f, C c) => - (t1, t2) => f(c, t1, t2); - - public static Action Curry(Action f, C c) => - (t1, t2, t3) => f(c, t1, t2, t3); - - public static Action Curry(Action f, C c) => - (t1, t2, t3, t4) => f(c, t1, t2, t3, t4); - - public static Action Curry(Action f, - C c) => (t1, t2, t3, t4, t5) => f(c, t1, t2, t3, t4, t5); - - public static Action - Curry(Action f, C c) => - (t1, t2, t3, t4, t5, t6) => f(c, t1, t2, t3, t4, t5, t6); - } - -#region Collect Bound Constants - - /// Helps to identify constants as the one to be put into the Closure - public static bool IsClosureBoundConstant(object value, TypeInfo type) => - value is Delegate || - !type.IsPrimitive && !type.IsEnum && value is string == false && value is Type == false && value is decimal == false; - - // @paramExprs is required for nested lambda compilation - private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression expr, - IReadOnlyList paramExprs, bool isNestedLambda, ref ClosureInfo rootClosure) - { - while (true) - { - if (expr == null) - return false; - - switch (expr.NodeType) - { - case ExpressionType.Constant: - var constantExpr = (ConstantExpression)expr; - var value = constantExpr.Value; - if (value != null && IsClosureBoundConstant(value, value.GetType().GetTypeInfo())) - closure.AddConstant(value); - return true; - - case ExpressionType.Quote: - //var operand = ((UnaryExpression)expr).Operand; - //if (operand != null && IsClosureBoundConstant(operand, expr.Type.GetTypeInfo())) - // closure.AddConstant(operand); - return false; - - case ExpressionType.Parameter: - // if parameter is used BUT is not in passed parameters and not in local variables, - // it means parameter is provided by outer lambda and should be put in closure for current lambda - var p = paramExprs.Count - 1; - while (p != -1 && !ReferenceEquals(paramExprs[p], expr)) --p; - if (p == -1 && !closure.IsLocalVar(expr)) - { - if (!isNestedLambda) - return false; - closure.AddNonPassedParam((ParameterExpression)expr); - } - return true; - - case ExpressionType.Call: - var callExpr = (MethodCallExpression)expr; - var callObjectExpr = callExpr.Object; - - var fewCallArgCount = callExpr.FewArgumentCount; - if (fewCallArgCount == 0) - { - if (callObjectExpr != null) - { - expr = callObjectExpr; - continue; - } - - return true; - } - - if (fewCallArgCount > 0) - { - if (callObjectExpr != null && - !TryCollectBoundConstants(ref closure, callObjectExpr, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - if (fewCallArgCount == 1) - { - expr = ((OneArgumentMethodCallExpression)callExpr).Argument; - continue; - } - - if (fewCallArgCount == 2) - { - var twoArgsExpr = (TwoArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, twoArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = twoArgsExpr.Argument1; - continue; - } - - if (fewCallArgCount == 3) - { - var threeArgsExpr = (ThreeArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, threeArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, threeArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = threeArgsExpr.Argument2; - continue; - } - - if (fewCallArgCount == 4) - { - var fourArgsExpr = (FourArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, fourArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fourArgsExpr.Argument3; - continue; - } - - var fiveArgsExpr = (FiveArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument3, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fiveArgsExpr.Argument4; - continue; - } - - var methodArgs = callExpr.Arguments; - var methodArgCount = methodArgs.Count; - if (methodArgCount == 0) - { - if (callObjectExpr != null) - { - expr = callObjectExpr; - continue; - } - - return true; - } - - if (callObjectExpr != null && - !TryCollectBoundConstants(ref closure, callExpr.Object, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - for (var i = 0; i < methodArgCount - 1; i++) - if (!TryCollectBoundConstants(ref closure, methodArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - - expr = methodArgs[methodArgCount - 1]; - continue; - - case ExpressionType.MemberAccess: - var memberExpr = ((MemberExpression)expr).Expression; - if (memberExpr == null) - return true; - expr = memberExpr; - continue; - - case ExpressionType.New: - var newExpr = (NewExpression)expr; - - var fewArgCount = newExpr.FewArgumentCount; - if (fewArgCount == 0) - return true; - - if (fewArgCount > 0) - { - if (fewArgCount == 1) - { - expr = ((OneArgumentNewExpression)newExpr).Argument; - continue; - } - - if (fewArgCount == 2) - { - var twoArgsExpr = (TwoArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, twoArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = twoArgsExpr.Argument1; - continue; - } - - if (fewArgCount == 3) - { - var threeArgsExpr = (ThreeArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, threeArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, threeArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = threeArgsExpr.Argument2; - continue; - } - - if (fewArgCount == 4) - { - var fourArgsExpr = (FourArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, fourArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fourArgsExpr.Argument3; - continue; - } - - if (fewArgCount == 4) - { - var fourArgsExpr = (FourArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, fourArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fourArgsExpr.Argument3; - continue; - } - - var fiveArgsExpr = (FiveArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument3, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fiveArgsExpr.Argument4; - continue; - } - - var ctorArgs = ((NewExpression)expr).Arguments; - var ctorLastArgIndex = ctorArgs.Count - 1; - if (ctorLastArgIndex == -1) - return true; - - for (var i = 0; i < ctorLastArgIndex; i++) - if (!TryCollectBoundConstants(ref closure, ctorArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = ctorArgs[ctorLastArgIndex]; - continue; - - case ExpressionType.NewArrayBounds: - case ExpressionType.NewArrayInit: - var elemExprs = ((NewArrayExpression)expr).Expressions; - var elemExprsCount = elemExprs.Count; - if (elemExprsCount == 0) - return true; - - for (var i = 0; i < elemExprsCount - 1; i++) - if (!TryCollectBoundConstants(ref closure, elemExprs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - - expr = elemExprs[elemExprsCount - 1]; - continue; - - case ExpressionType.MemberInit: - return TryCollectMemberInitExprConstants( - ref closure, (MemberInitExpression)expr, paramExprs, isNestedLambda, ref rootClosure); - - case ExpressionType.Lambda: - var nestedLambdaExpr = (LambdaExpression)expr; - - // Look for the already collected lambdas and if we have the same lambda, start from the root - var nestedLambdas = rootClosure.NestedLambdas; - if (nestedLambdas.Length != 0) - { - var foundLambdaInfo = FindAlreadyCollectedNestedLambdaInfo(nestedLambdas, nestedLambdaExpr, out var foundInLambdas); - if (foundLambdaInfo != null) - { - // if the lambda is not found on the same level, then add it - if (foundInLambdas == closure.NestedLambdas) - { - ++foundLambdaInfo.UsageCountOrVarIndex; - } - else - { - closure.AddNestedLambda(foundLambdaInfo); - var foundLambdaNonPassedParams = foundLambdaInfo.ClosureInfo.NonPassedParameters; - if (foundLambdaNonPassedParams.Length != 0) - PropagateNonPassedParamsToOuterLambda(ref closure, paramExprs, nestedLambdaExpr.Parameters, foundLambdaNonPassedParams); - } - - return true; - } - } - - var nestedLambdaInfo = new NestedLambdaInfo(nestedLambdaExpr); - if (!TryCollectBoundConstants(ref nestedLambdaInfo.ClosureInfo, - nestedLambdaExpr.Body, nestedLambdaExpr.Parameters, true, ref rootClosure)) - return false; - - closure.AddNestedLambda(nestedLambdaInfo); - var nestedNonPassedParams = nestedLambdaInfo.ClosureInfo.NonPassedParameters; - if (nestedNonPassedParams.Length != 0) - PropagateNonPassedParamsToOuterLambda(ref closure, paramExprs, nestedLambdaExpr.Parameters, nestedNonPassedParams); - - return true; - - case ExpressionType.Invoke: - var invokeExpr = (InvocationExpression)expr; - var invokeArgs = invokeExpr.Arguments; - var invokeArgsCount = invokeArgs.Count; - if (invokeArgsCount == 0) - { - // optimization #138: we inline the invoked lambda body (only for lambdas without arguments) - // therefore we skipping collecting the lambda and invocation arguments and got directly to lambda body. - // This approach is repeated in `TryEmitInvoke` - expr = (invokeExpr.Expression as LambdaExpression)?.Body ?? invokeExpr.Expression; - continue; - } - else if (!TryCollectBoundConstants(ref closure, invokeExpr.Expression, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - for (var i = 0; i < invokeArgs.Count - 1; i++) - if (!TryCollectBoundConstants(ref closure, invokeArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - - expr = invokeArgs[invokeArgsCount - 1]; - continue; - - case ExpressionType.Conditional: - var condExpr = (ConditionalExpression)expr; - if (!TryCollectBoundConstants(ref closure, condExpr.Test, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, condExpr.IfFalse, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = condExpr.IfTrue; - continue; - - case ExpressionType.Block: - - if (expr is OneVariableTwoExpressionBlockExpression simpleBlock) - { - closure.PushBlockWithVars(simpleBlock.Variable); - if (!TryCollectBoundConstants(ref closure, simpleBlock.Expression1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, simpleBlock.Expression2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - closure.PopBlock(); - return true; - } - - var blockExpr = (BlockExpression)expr; - var blockVarExprs = blockExpr.Variables; - var blockExprs = blockExpr.Expressions; - - if (blockVarExprs.Count == 0) - { - for (var i = 0; i < blockExprs.Count - 1; i++) - if (!TryCollectBoundConstants(ref closure, blockExprs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = blockExprs[blockExprs.Count - 1]; - continue; - } - else - { - if (blockVarExprs.Count == 1) - closure.PushBlockWithVars(blockVarExprs[0]); - else - closure.PushBlockWithVars(blockVarExprs); - - for (var i = 0; i < blockExprs.Count; i++) - if (!TryCollectBoundConstants(ref closure, blockExprs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - closure.PopBlock(); - } - - return true; - - case ExpressionType.Loop: - var loopExpr = (LoopExpression)expr; - closure.AddLabel(loopExpr.BreakLabel); - closure.AddLabel(loopExpr.ContinueLabel); - expr = loopExpr.Body; - continue; - - case ExpressionType.Index: - var indexExpr = (IndexExpression)expr; - var indexArgs = indexExpr.Arguments; - for (var i = 0; i < indexArgs.Count; i++) - if (!TryCollectBoundConstants(ref closure, indexArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - if (indexExpr.Object == null) - return true; - expr = indexExpr.Object; - continue; - - case ExpressionType.Try: - return TryCollectTryExprConstants(ref closure, (TryExpression)expr, paramExprs, isNestedLambda, ref rootClosure); - - case ExpressionType.Label: - var labelExpr = (LabelExpression)expr; - var defaultValueExpr = labelExpr.DefaultValue; - closure.AddLabel(labelExpr.Target); - if (defaultValueExpr == null) - return true; - expr = defaultValueExpr; - continue; - - case ExpressionType.Goto: - var gotoExpr = (GotoExpression)expr; - if (gotoExpr.Kind == GotoExpressionKind.Return) - closure.MarkAsContainsReturnGotoExpression(); - - if (gotoExpr.Value == null) - return true; - - expr = gotoExpr.Value; - continue; - - case ExpressionType.Switch: - var switchExpr = ((SwitchExpression)expr); - if (!TryCollectBoundConstants(ref closure, switchExpr.SwitchValue, paramExprs, isNestedLambda, ref rootClosure) || - switchExpr.DefaultBody != null && - !TryCollectBoundConstants(ref closure, switchExpr.DefaultBody, paramExprs, isNestedLambda, ref rootClosure)) - return false; - var switchCases = switchExpr.Cases; - for (var i = 0; i < switchCases.Count - 1; i++) - if (!TryCollectBoundConstants(ref closure, switchCases[i].Body, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = switchCases[switchCases.Count - 1].Body; - continue; - - case ExpressionType.Extension: - expr = expr.Reduce(); - continue; - - case ExpressionType.Default: - return true; - - default: - if (expr is UnaryExpression unaryExpr) - { - expr = unaryExpr.Operand; - continue; - } - - if (expr is BinaryExpression binaryExpr) - { - if (!TryCollectBoundConstants(ref closure, binaryExpr.Left, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = binaryExpr.Right; - continue; - } - - if (expr is TypeBinaryExpression typeBinaryExpr) - { - expr = typeBinaryExpr.Expression; - continue; - } - - return false; - } - } - } - - private static void PropagateNonPassedParamsToOuterLambda( - ref ClosureInfo closure, IReadOnlyList paramExprs, - IReadOnlyList nestedLambdaParamExprs, ParameterExpression[] nestedNonPassedParams) - { - // If nested non passed parameter is not matched with any outer passed parameter, - // then ensure it goes to outer non passed parameter. - // But check that having a non-passed parameter in root expression is invalid. - for (var i = 0; i < nestedNonPassedParams.Length; i++) - { - var nestedNonPassedParam = nestedNonPassedParams[i]; - - var isInNestedLambda = false; - if (nestedLambdaParamExprs.Count != 0) - for (var p = 0; !isInNestedLambda && p < nestedLambdaParamExprs.Count; ++p) - isInNestedLambda = ReferenceEquals(nestedLambdaParamExprs[p], nestedNonPassedParam); - - var isInOuterLambda = false; - if (paramExprs.Count != 0) - for (var p = 0; !isInOuterLambda && p < paramExprs.Count; ++p) - isInOuterLambda = ReferenceEquals(paramExprs[p], nestedNonPassedParam); - - if (!isInNestedLambda && !isInOuterLambda) - closure.AddNonPassedParam(nestedNonPassedParam); - } - } - - private static NestedLambdaInfo FindAlreadyCollectedNestedLambdaInfo( - NestedLambdaInfo[] nestedLambdas, LambdaExpression nestedLambdaExpr, out NestedLambdaInfo[] foundInLambdas) - { - for (var i = 0; i < nestedLambdas.Length; i++) - { - var lambdaInfo = nestedLambdas[i]; - if (ReferenceEquals(lambdaInfo.LambdaExpression, nestedLambdaExpr)) - { - foundInLambdas = nestedLambdas; - return lambdaInfo; - } - - var deeperNestedLambdas = lambdaInfo.ClosureInfo.NestedLambdas; - if (deeperNestedLambdas.Length != 0) - { - var deeperLambdaInfo = FindAlreadyCollectedNestedLambdaInfo(deeperNestedLambdas, nestedLambdaExpr, out foundInLambdas); - if (deeperLambdaInfo != null) - return deeperLambdaInfo; - } - } - - foundInLambdas = null; - return null; - } - - private static bool TryCompileNestedLambda(ref ClosureInfo outerClosureInfo, int nestedLambdaIndex) - { - // 1. Try to compile nested lambda in place - // 2. Check that parameters used in compiled lambda are passed or closed by outer lambda - // 3. Add the compiled lambda to closure of outer lambda for later invocation - var nestedLambdaInfo = outerClosureInfo.NestedLambdas[nestedLambdaIndex]; - if (nestedLambdaInfo.Lambda != null) - return true; - - var nestedLambdaExpr = nestedLambdaInfo.LambdaExpression; - ref var nestedLambdaClosureInfo = ref nestedLambdaInfo.ClosureInfo; - - var nestedLambdaParamExprs = nestedLambdaExpr.Parameters; - var nestedLambdaNestedLambdas = nestedLambdaClosureInfo.NestedLambdas; - if (nestedLambdaNestedLambdas.Length != 0) - for (var i = 0; i < nestedLambdaNestedLambdas.Length; ++i) - if (!TryCompileNestedLambda(ref nestedLambdaClosureInfo, i)) - return false; - - ArrayClosure nestedLambdaClosure = null; - if (nestedLambdaClosureInfo.NonPassedParameters.Length == 0) - { - if ((nestedLambdaClosureInfo.Status & ClosureStatus.HasClosure) == 0) - nestedLambdaClosure = EmptyArrayClosure; - else - nestedLambdaClosure = new ArrayClosure(nestedLambdaClosureInfo.GetArrayOfConstantsAndNestedLambdas()); - } - - var nestedReturnType = nestedLambdaExpr.ReturnType; - var closurePlusParamTypes = GetClosureTypeToParamTypes(nestedLambdaParamExprs); - - var method = new DynamicMethod(string.Empty, - nestedReturnType, closurePlusParamTypes, typeof(ArrayClosure), true); - - var il = method.GetILGenerator(); - - if ((nestedLambdaClosureInfo.Status & ClosureStatus.HasClosure) != 0) - EmittingVisitor.EmitLoadConstantsAndNestedLambdasIntoVars(il, ref nestedLambdaClosureInfo); - - var parentFlags = nestedReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(nestedLambdaExpr.Body, nestedLambdaParamExprs, il, ref nestedLambdaClosureInfo, parentFlags)) - return false; - il.Emit(OpCodes.Ret); - - if (nestedLambdaClosure != null) - { - nestedLambdaInfo.Lambda = method.CreateDelegate(nestedLambdaExpr.Type, nestedLambdaClosure); - } - else - { - // Otherwise create a static or an open delegate to pass closure later with `TryEmitNestedLambda`, - // constructing the new closure with non-passed arguments and the rest of items - nestedLambdaInfo.Lambda = method.CreateDelegate( - Tools.GetFuncOrActionType(closurePlusParamTypes, nestedReturnType), - null); - } - - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return true; - } - - private static bool TryCollectMemberInitExprConstants(ref ClosureInfo closure, MemberInitExpression expr, - IReadOnlyList paramExprs, bool isNestedLambda, ref ClosureInfo rootClosure) - { - var newExpr = expr.NewExpression - ?? expr.Expression - ; - if (!TryCollectBoundConstants(ref closure, newExpr, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - var memberBindings = expr.Bindings; - for (var i = 0; i < memberBindings.Count; ++i) - { - var memberBinding = memberBindings[i]; - if (memberBinding.BindingType == MemberBindingType.Assignment && - !TryCollectBoundConstants( - ref closure, ((MemberAssignment)memberBinding).Expression, paramExprs, isNestedLambda, ref rootClosure)) - return false; - } - - return true; - } - - private static bool TryCollectTryExprConstants(ref ClosureInfo closure, TryExpression tryExpr, - IReadOnlyList paramExprs, bool isNestedLambda, ref ClosureInfo rootClosure) - { - closure.AddTryCatchFinallyInfo(); - - if (!TryCollectBoundConstants(ref closure, tryExpr.Body, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - var catchBlocks = tryExpr.Handlers; - for (var i = 0; i < catchBlocks.Count; i++) - { - var catchBlock = catchBlocks[i]; - var catchExVar = catchBlock.Variable; - if (catchExVar != null) - { - closure.PushBlockWithVars(catchExVar); - if (!TryCollectBoundConstants(ref closure, catchExVar, paramExprs, isNestedLambda, ref rootClosure)) - return false; - } - - if (catchBlock.Filter != null && - !TryCollectBoundConstants(ref closure, catchBlock.Filter, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - if (!TryCollectBoundConstants(ref closure, catchBlock.Body, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - if (catchExVar != null) - closure.PopBlock(); - } - - if (tryExpr.Finally != null && - !TryCollectBoundConstants(ref closure, tryExpr.Finally, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - --closure.CurrentTryCatchFinallyIndex; - return true; - } - -#endregion - - // The minimal context-aware flags set by parent - [Flags] - internal enum ParentFlags - { - Empty = 0, - IgnoreResult = 1 << 1, - Call = 1 << 2, - MemberAccess = 1 << 3, // Any Parent Expression is a MemberExpression - Arithmetic = 1 << 4, - Coalesce = 1 << 5, - InstanceAccess = 1 << 6, - DupMemberOwner = 1 << 7, - TryCatch = 1 << 8, - InstanceCall = Call | InstanceAccess - } - - internal static bool IgnoresResult(this ParentFlags parent) => (parent & ParentFlags.IgnoreResult) != 0; - - /// Supports emitting of selected expressions, e.g. lambdaExpr are not supported yet. - /// When emitter find not supported expression it will return false from , so I could fallback - /// to normal and slow Expression.Compile. - private static class EmittingVisitor - { -#if NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 - private static readonly MethodInfo _getTypeFromHandleMethod = - typeof(Type).GetTypeInfo().GetDeclaredMethod("GetTypeFromHandle"); - - private static readonly MethodInfo _objectEqualsMethod = GetObjectEquals(); - private static MethodInfo GetObjectEquals() - { - var ms = typeof(object).GetTypeInfo().GetDeclaredMethods("Equals"); - foreach (var m in ms) - if (m.GetParameters().Length == 2) - return m; - throw new InvalidOperationException("object.Equals is not found"); - } -#else - private static readonly MethodInfo _getTypeFromHandleMethod = - ((Func)Type.GetTypeFromHandle).Method; - - private static readonly MethodInfo _objectEqualsMethod = - ((Func)object.Equals).Method; -#endif - - public static bool TryEmit(Expression expr, IReadOnlyList paramExprs, - ILGenerator il, ref ClosureInfo closure, ParentFlags parent, int byRefIndex = -1) - { - while (true) - { - closure.LastEmitIsAddress = false; - - switch (expr.NodeType) - { - case ExpressionType.Parameter: - return (parent & ParentFlags.IgnoreResult) != 0 || - TryEmitParameter((ParameterExpression)expr, paramExprs, il, ref closure, parent, byRefIndex); - - case ExpressionType.TypeAs: - case ExpressionType.IsTrue: - case ExpressionType.IsFalse: - case ExpressionType.Increment: - case ExpressionType.Decrement: - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - case ExpressionType.OnesComplement: - case ExpressionType.UnaryPlus: - case ExpressionType.Unbox: - return TryEmitSimpleUnaryExpression((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Quote: - //return TryEmitNotNullConstant(true, expr.Type, ((UnaryExpression)expr).Operand, il, ref closure); - return false; - - case ExpressionType.TypeIs: - return TryEmitTypeIs((TypeBinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Not: - return TryEmitNot((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Convert: - case ExpressionType.ConvertChecked: - return TryEmitConvert((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.ArrayIndex: - var arrIndexExpr = (BinaryExpression)expr; - return TryEmit(arrIndexExpr.Left, paramExprs, il, ref closure, parent) && - TryEmit(arrIndexExpr.Right, paramExprs, il, ref closure, parent) && - TryEmitArrayIndex(expr.Type, il); - - case ExpressionType.ArrayLength: - var arrLengthExpr = (UnaryExpression)expr; - return TryEmitArrayLength(arrLengthExpr, paramExprs, il, ref closure, parent); - - case ExpressionType.Constant: - var constExpr = (ConstantExpression)expr; - if ((parent & ParentFlags.IgnoreResult) != 0) - return true; - - if (constExpr.Value == null) - { - il.Emit(OpCodes.Ldnull); - return true; - } - - return TryEmitNotNullConstant(true, constExpr.Type, constExpr.Value, il, ref closure); - - case ExpressionType.Call: - return TryEmitMethodCall(expr, paramExprs, il, ref closure, parent); - - case ExpressionType.MemberAccess: - return TryEmitMemberAccess((MemberExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.New: - return TryEmitNew(expr, paramExprs, il, ref closure, parent); - - case ExpressionType.NewArrayBounds: - case ExpressionType.NewArrayInit: - return EmitNewArray((NewArrayExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.MemberInit: - return EmitMemberInit((MemberInitExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Lambda: - return TryEmitNestedLambda((LambdaExpression)expr, paramExprs, il, ref closure); - - case ExpressionType.Invoke: - // optimization #138: we inline the invoked lambda body (only for lambdas without arguments) - if (((InvocationExpression)expr).Expression is LambdaExpression lambdaExpr && lambdaExpr.Parameters.Count == 0) - { - expr = lambdaExpr.Body; - continue; - } - - return TryEmitInvoke((InvocationExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.Equal: - case ExpressionType.NotEqual: - var binaryExpr = (BinaryExpression)expr; - return TryEmitComparison(binaryExpr.Left, binaryExpr.Right, binaryExpr.NodeType, - paramExprs, il, ref closure, parent); - - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.Divide: - case ExpressionType.Modulo: - case ExpressionType.Power: - case ExpressionType.And: - case ExpressionType.Or: - case ExpressionType.ExclusiveOr: - case ExpressionType.LeftShift: - case ExpressionType.RightShift: - var arithmeticExpr = (BinaryExpression)expr; - return TryEmitArithmetic(arithmeticExpr, expr.NodeType, paramExprs, il, ref closure, parent); - - case ExpressionType.AndAlso: - case ExpressionType.OrElse: - return TryEmitLogicalOperator((BinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Coalesce: - return TryEmitCoalesceOperator((BinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Conditional: - return TryEmitConditional((ConditionalExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.PostIncrementAssign: - case ExpressionType.PreIncrementAssign: - case ExpressionType.PostDecrementAssign: - case ExpressionType.PreDecrementAssign: - return TryEmitIncDecAssign((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.AddAssign: - case ExpressionType.AddAssignChecked: - case ExpressionType.SubtractAssign: - case ExpressionType.SubtractAssignChecked: - case ExpressionType.MultiplyAssign: - case ExpressionType.MultiplyAssignChecked: - case ExpressionType.DivideAssign: - case ExpressionType.ModuloAssign: - case ExpressionType.PowerAssign: - case ExpressionType.AndAssign: - case ExpressionType.OrAssign: - case ExpressionType.ExclusiveOrAssign: - case ExpressionType.LeftShiftAssign: - case ExpressionType.RightShiftAssign: - case ExpressionType.Assign: - return TryEmitAssign((BinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Block: - if (expr is OneVariableTwoExpressionBlockExpression simpleBlockExpr) - { - closure.PushBlockWithVars(simpleBlockExpr.Variable, il.GetNextLocalVarIndex(simpleBlockExpr.Variable.Type)); - if (!TryEmit(simpleBlockExpr.Expression1, paramExprs, il, ref closure, parent | ParentFlags.IgnoreResult) || - !TryEmit(simpleBlockExpr.Expression2, paramExprs, il, ref closure, parent)) - return false; - closure.PopBlock(); - return true; - } - - var blockExpr = (BlockExpression)expr; - var blockVarExprs = blockExpr.Variables; - var blockVarCount = blockVarExprs.Count; - if (blockVarCount == 1) - closure.PushBlockWithVars(blockVarExprs[0], il.GetNextLocalVarIndex(blockVarExprs[0].Type)); - else if (blockVarCount > 1) - closure.PushBlockAndConstructLocalVars(blockVarExprs, il); - - var statementExprs = blockExpr.Expressions; // Trim the expressions after the Throw - #196 - var statementCount = statementExprs.Count; - expr = statementExprs[statementCount - 1]; // The last (result) statement in block will provide the result - - // Try to trim the statements up to the Throw (if any) - if (statementCount > 1) - { - var throwIndex = statementCount - 1; - while (throwIndex != -1 && statementExprs[throwIndex].NodeType != ExpressionType.Throw) - --throwIndex; - - // If we have a Throw and it is not the last one - if (throwIndex != -1 && throwIndex != statementCount - 1) - { - // Change the Throw return type to match the one for the Block, and adjust the statement count - expr = Expression.Throw(((UnaryExpression)statementExprs[throwIndex]).Operand, blockExpr.Type); - statementCount = throwIndex + 1; - } - } - - // handle the all statements in block excluding the last one - if (statementCount > 1) - for (var i = 0; i < statementCount - 1; i++) - if (!TryEmit(statementExprs[i], paramExprs, il, ref closure, parent | ParentFlags.IgnoreResult)) - return false; - - if (blockVarCount == 0) - continue; // OMG, no recursion, continue with last expression - - if (!TryEmit(expr, paramExprs, il, ref closure, parent)) - return false; - - closure.PopBlock(); - return true; - - case ExpressionType.Loop: - return TryEmitLoop((LoopExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Try: - return TryEmitTryCatchFinallyBlock((TryExpression)expr, paramExprs, il, ref closure, - parent | ParentFlags.TryCatch); - - case ExpressionType.Throw: - { - if (!TryEmit(((UnaryExpression)expr).Operand, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - il.Emit(OpCodes.Throw); - return true; - } - - case ExpressionType.Default: - if (expr.Type != typeof(void) && (parent & ParentFlags.IgnoreResult) == 0) - EmitDefault(expr.Type, il); - return true; - - case ExpressionType.Index: - return TryEmitIndex((IndexExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Goto: - return TryEmitGoto((GotoExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Label: - return TryEmitLabel((LabelExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Switch: - return TryEmitSwitch((SwitchExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Extension: - expr = expr.Reduce(); - continue; - - default: - return false; - } - } - } - - private static bool TryEmitNew(Expression expr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - var newExpr = (NewExpression)expr; - - var argCount = newExpr.FewArgumentCount; - if (argCount > 0) - { - var args = newExpr.Constructor.GetParameters(); - if (argCount == 1) - { - var argExpr = ((OneArgumentNewExpression)newExpr).Argument; - if (!TryEmit(argExpr, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1)) - return false; - } - else if (argCount == 2) - { - var twoArgsExpr = (TwoArgumentsNewExpression)newExpr; - if (!TryEmit(twoArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(twoArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1)) - return false; - } - else if (argCount == 3) - { - var threeArgsExpr = (ThreeArgumentsNewExpression)newExpr; - if (!TryEmit(threeArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(threeArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(threeArgsExpr.Argument2, paramExprs, il, ref closure, parent, args[2].ParameterType.IsByRef ? 2 : -1)) - return false; - } - else if (argCount == 4) - { - var fourArgsExpr = (FourArgumentsNewExpression)newExpr; - if (!TryEmit(fourArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fourArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fourArgsExpr.Argument2, paramExprs, il, ref closure, parent, args[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fourArgsExpr.Argument3, paramExprs, il, ref closure, parent, args[3].ParameterType.IsByRef ? 3 : -1)) - return false; - } - else if (argCount == 5) - { - var fourArgsExpr = (FiveArgumentsNewExpression)newExpr; - if (!TryEmit(fourArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fourArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fourArgsExpr.Argument2, paramExprs, il, ref closure, parent, args[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fourArgsExpr.Argument3, paramExprs, il, ref closure, parent, args[3].ParameterType.IsByRef ? 3 : -1) || - !TryEmit(fourArgsExpr.Argument4, paramExprs, il, ref closure, parent, args[4].ParameterType.IsByRef ? 4 : -1)) - return false; - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (newExpr.Constructor != null) - il.Emit(OpCodes.Newobj, newExpr.Constructor); - else if (newExpr.Type.IsValueType()) - EmitLoadLocalVariable(il, InitValueTypeVariable(il, newExpr.Type)); - else - return false; - return true; - } - - var argExprs = newExpr.Arguments; - if (argExprs.Count != 0) - { - var args = newExpr.Constructor.GetParameters(); - for (var i = 0; i < args.Length; ++i) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent, args[i].ParameterType.IsByRef ? i : -1)) - return false; - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (newExpr.Constructor != null) - il.Emit(OpCodes.Newobj, newExpr.Constructor); - else if (newExpr.Type.IsValueType()) - EmitLoadLocalVariable(il, InitValueTypeVariable(il, newExpr.Type)); - else - return false; - return true; - } - - private static bool TryEmitArrayLength(UnaryExpression arrLengthExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - if (!TryEmit(arrLengthExpr.Operand, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) == 0) - { - il.Emit(OpCodes.Ldlen); - il.Emit(OpCodes.Conv_I4); - } - - return true; - } - - private static bool TryEmitLoop(LoopExpression loopExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - // Mark the start of the loop body: - var loopBodyLabel = il.DefineLabel(); - il.MarkLabel(loopBodyLabel); - - if (loopExpr.ContinueLabel != null) - il.MarkLabel(closure.GetOrCreateLabel(loopExpr.ContinueLabel, il)); - - if (!TryEmit(loopExpr.Body, paramExprs, il, ref closure, parent)) - return false; - - // If loop hasn't exited, jump back to start of its body: - il.Emit(OpCodes.Br, loopBodyLabel); - - if (loopExpr.BreakLabel != null) - il.MarkLabel(closure.GetOrCreateLabel(loopExpr.BreakLabel, il)); - - return true; - } - - private static bool TryEmitIndex(IndexExpression indexExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - if (indexExpr.Object != null && - !TryEmit(indexExpr.Object, paramExprs, il, ref closure, parent)) - return false; - - var indexArgExprs = indexExpr.Arguments; - for (var i = 0; i < indexArgExprs.Count; i++) - if (!TryEmit(indexArgExprs[i], paramExprs, il, ref closure, parent, - indexArgExprs[i].Type.IsByRef ? i : -1)) - return false; - - var indexerProp = indexExpr.Indexer; - if (indexerProp != null) - return EmitMethodCall(il, indexerProp.DeclaringType.FindPropertyGetMethod(indexerProp.Name)); - - if (indexExpr.Arguments.Count == 1) // one dimensional array - return TryEmitArrayIndex(indexExpr.Type, il); - - // multi dimensional array - return EmitMethodCall(il, indexExpr.Object?.Type.FindMethod("Get")); - } - - private static bool TryEmitLabel(LabelExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var index = closure.GetLabelIndex(expr.Target); - if (index == -1) - return false; // should be found in first collecting constants round - - if (closure.IsTryReturnLabel(index)) - return true; // label will be emitted by TryEmitTryCatchFinallyBlock - - // define a new label or use the label provided by the preceding GoTo expression - var label = closure.GetOrCreateLabel(index, il); - - il.MarkLabel(label); - - return expr.DefaultValue == null || TryEmit(expr.DefaultValue, paramExprs, il, ref closure, parent); - } - - private static bool TryEmitGoto(GotoExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var index = closure.GetLabelIndex(expr.Target); - if (index == -1) - { - if ((closure.Status & ClosureStatus.ToBeCollected) == 0) - return false; // if no collection cycle then the labels may be not collected - throw new InvalidOperationException("Cannot jump, no labels found"); - } - - if (expr.Value != null && - !TryEmit(expr.Value, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - - switch (expr.Kind) - { - case GotoExpressionKind.Break: - case GotoExpressionKind.Continue: - // use label defined by Label expression or define its own to use by subsequent Label - il.Emit(OpCodes.Br, closure.GetOrCreateLabel(index, il)); - return true; - - case GotoExpressionKind.Goto: - if (expr.Value != null) - goto case GotoExpressionKind.Return; - - // use label defined by Label expression or define its own to use by subsequent Label - il.Emit(OpCodes.Br, closure.GetOrCreateLabel(index, il)); - return true; - - case GotoExpressionKind.Return: - - // check that we are inside the Try-Catch-Finally block - if ((parent & ParentFlags.TryCatch) != 0) - { - // Can't emit a Return inside a Try/Catch, so leave it to TryEmitTryCatchFinallyBlock - // to emit the Leave instruction, return label and return result - closure.MarkReturnLabelIndex(index); - } - else - { - // use label defined by Label expression or define its own to use by subsequent Label - il.Emit(OpCodes.Ret, closure.GetOrCreateLabel(index, il)); - } - - return true; - - default: - return false; - } - } - - private static bool TryEmitCoalesceOperator(BinaryExpression exprObj, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var labelFalse = il.DefineLabel(); - var labelDone = il.DefineLabel(); - - var left = exprObj.Left; - var right = exprObj.Right; - - if (!TryEmit(left, paramExprs, il, ref closure, parent | ParentFlags.Coalesce)) - return false; - - var leftType = left.Type; - if (leftType.IsValueType()) // Nullable -> It's the only ValueType comparable to null - { - var varIndex = EmitStoreLocalVariableAndLoadItsAddress(il, leftType); - il.Emit(OpCodes.Call, leftType.FindNullableHasValueGetterMethod()); - - il.Emit(OpCodes.Brfalse, labelFalse); - EmitLoadLocalVariableAddress(il, varIndex); - il.Emit(OpCodes.Call, leftType.FindNullableGetValueOrDefaultMethod()); - - il.Emit(OpCodes.Br, labelDone); - il.MarkLabel(labelFalse); - if (!TryEmit(right, paramExprs, il, ref closure, parent | ParentFlags.Coalesce)) - return false; - - il.MarkLabel(labelDone); - return true; - } - - il.Emit(OpCodes.Dup); // duplicate left, if it's not null, after the branch this value will be on the top of the stack - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Brfalse, labelFalse); - - il.Emit(OpCodes.Pop); // left is null, pop its value from the stack - - if (!TryEmit(right, paramExprs, il, ref closure, parent | ParentFlags.Coalesce)) - return false; - - if (right.Type != exprObj.Type) - { - if (right.Type.IsValueType()) - il.Emit(OpCodes.Box, right.Type); - } - - if (left.Type == exprObj.Type) - il.MarkLabel(labelFalse); - else - { - il.Emit(OpCodes.Br, labelDone); - il.MarkLabel(labelFalse); - il.Emit(OpCodes.Castclass, exprObj.Type); - il.MarkLabel(labelDone); - } - - return true; - } - - private static void EmitDefault(Type type, ILGenerator il) - { - if (!type.GetTypeInfo().IsValueType) - { - il.Emit(OpCodes.Ldnull); - } - else if ( - type == typeof(bool) || - type == typeof(byte) || - type == typeof(char) || - type == typeof(sbyte) || - type == typeof(int) || - type == typeof(uint) || - type == typeof(short) || - type == typeof(ushort)) - { - il.Emit(OpCodes.Ldc_I4_0); - } - else if ( - type == typeof(long) || - type == typeof(ulong)) - { - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Conv_I8); - } - else if (type == typeof(float)) - il.Emit(OpCodes.Ldc_R4, default(float)); - else if (type == typeof(double)) - il.Emit(OpCodes.Ldc_R8, default(double)); - else - EmitLoadLocalVariable(il, InitValueTypeVariable(il, type)); - } - - private static bool TryEmitTryCatchFinallyBlock(TryExpression tryExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var containsReturnGotoExpression = closure.TryCatchFinallyContainsReturnGotoExpression(); - il.BeginExceptionBlock(); - - if (!TryEmit(tryExpr.Body, paramExprs, il, ref closure, parent)) - return false; - - var exprType = tryExpr.Type; - var returnsResult = exprType != typeof(void) && (containsReturnGotoExpression || !parent.IgnoresResult()); - var resultVarIndex = -1; - - if (returnsResult) - EmitStoreLocalVariable(il, resultVarIndex = il.GetNextLocalVarIndex(exprType)); - - var catchBlocks = tryExpr.Handlers; - for (var i = 0; i < catchBlocks.Count; i++) - { - var catchBlock = catchBlocks[i]; - if (catchBlock.Filter != null) - return false; // todo: Add support for filters in catch expression - - il.BeginCatchBlock(catchBlock.Test); - - // at the beginning of catch the Exception value is on the stack, - // we will store into local variable. - var exVarExpr = catchBlock.Variable; - if (exVarExpr != null) - { - var exVarIndex = il.GetNextLocalVarIndex(exVarExpr.Type); - closure.PushBlockWithVars(exVarExpr, exVarIndex); - EmitStoreLocalVariable(il, exVarIndex); - } - - if (!TryEmit(catchBlock.Body, paramExprs, il, ref closure, parent)) - return false; - - if (exVarExpr != null) - closure.PopBlock(); - - if (returnsResult) - EmitStoreLocalVariable(il, resultVarIndex); - } - - var finallyExpr = tryExpr.Finally; - if (finallyExpr != null) - { - il.BeginFinallyBlock(); - if (!TryEmit(finallyExpr, paramExprs, il, ref closure, parent)) - return false; - } - - il.EndExceptionBlock(); - - if (returnsResult) - EmitLoadLocalVariable(il, resultVarIndex); - - --closure.CurrentTryCatchFinallyIndex; - return true; - } - - private static bool TryEmitParameter(ParameterExpression paramExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent, int byRefIndex = -1) - { - // if parameter is passed through, then just load it on stack - var paramType = paramExpr.Type; - - var paramIndex = paramExprs.Count - 1; - while (paramIndex != -1 && !ReferenceEquals(paramExprs[paramIndex], paramExpr)) - --paramIndex; - if (paramIndex != -1) - { - if ((closure.Status & ClosureStatus.ShouldBeStaticMethod) == 0) - ++paramIndex; // shift parameter index by one, because the first one will be closure - - closure.LastEmitIsAddress = !paramExpr.IsByRef && paramType.IsValueType() && - ((parent & ParentFlags.InstanceCall) == ParentFlags.InstanceCall || - (parent & ParentFlags.MemberAccess) != 0); - - if (closure.LastEmitIsAddress) - il.Emit(OpCodes.Ldarga_S, (byte)paramIndex); - else - { - if (paramIndex == 0) - il.Emit(OpCodes.Ldarg_0); - else if (paramIndex == 1) - il.Emit(OpCodes.Ldarg_1); - else if (paramIndex == 2) - il.Emit(OpCodes.Ldarg_2); - else if (paramIndex == 3) - il.Emit(OpCodes.Ldarg_3); - else - il.Emit(OpCodes.Ldarg_S, (byte)paramIndex); - } - - if (paramExpr.IsByRef) - { - if ((parent & ParentFlags.MemberAccess) != 0 && paramType.IsClass() || - (parent & ParentFlags.Coalesce) != 0) - il.Emit(OpCodes.Ldind_Ref); - else if ((parent & ParentFlags.Arithmetic) != 0) - EmitDereference(il, paramType); - } - - return true; - } - - // If parameter isn't passed, then it is passed into some outer lambda or it is a local variable, - // so it should be loaded from closure or from the locals. Then the closure is null will be an invalid state. - // Parameter may represent a variable, so first look if this is the case - var varIndex = closure.GetDefinedLocalVarOrDefault(paramExpr); - if (varIndex != -1) - { - if (byRefIndex != -1 || - paramType.IsValueType() && (parent & (ParentFlags.MemberAccess | ParentFlags.InstanceAccess)) != 0) - EmitLoadLocalVariableAddress(il, varIndex); - else - EmitLoadLocalVariable(il, varIndex); - return true; - } - - if (paramExpr.IsByRef) - { - EmitLoadLocalVariableAddress(il, byRefIndex); - return true; - } - - // the only possibility that we are here is because we are in the nested lambda, - // and it uses the parameter or variable from the outer lambda - var nonPassedParams = closure.NonPassedParameters; - var nonPassedParamIndex = nonPassedParams.Length - 1; - while (nonPassedParamIndex != -1 && !ReferenceEquals(nonPassedParams[nonPassedParamIndex], paramExpr)) - --nonPassedParamIndex; - if (nonPassedParamIndex == -1) - return false; // what??? no chance - - // Load non-passed argument from Closure - closure object is always a first argument - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, nonPassedParamIndex); - il.Emit(OpCodes.Ldelem_Ref); - - // source type is object, NonPassedParams is object array - if (paramType.IsValueType()) - il.Emit(OpCodes.Unbox_Any, paramType); - - return true; - } - - private static void EmitDereference(ILGenerator il, Type type) - { - if (type == typeof(Int32)) - il.Emit(OpCodes.Ldind_I4); - else if (type == typeof(Int64)) - il.Emit(OpCodes.Ldind_I8); - else if (type == typeof(Int16)) - il.Emit(OpCodes.Ldind_I2); - else if (type == typeof(SByte)) - il.Emit(OpCodes.Ldind_I1); - else if (type == typeof(Single)) - il.Emit(OpCodes.Ldind_R4); - else if (type == typeof(Double)) - il.Emit(OpCodes.Ldind_R8); - else if (type == typeof(IntPtr)) - il.Emit(OpCodes.Ldind_I); - else if (type == typeof(UIntPtr)) - il.Emit(OpCodes.Ldind_I); - else if (type == typeof(Byte)) - il.Emit(OpCodes.Ldind_U1); - else if (type == typeof(UInt16)) - il.Emit(OpCodes.Ldind_U2); - else if (type == typeof(UInt32)) - il.Emit(OpCodes.Ldind_U4); - else - il.Emit(OpCodes.Ldobj, type); - //todo: UInt64 as there is no OpCodes? Ldind_Ref? - } - - private static bool TryEmitSimpleUnaryExpression(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - var exprType = expr.Type; - - // todo: support decimal here - if (exprType == typeof(decimal)) - return false; - - if (!TryEmit(expr.Operand, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - else - { - if (expr.NodeType == ExpressionType.TypeAs) - { - il.Emit(OpCodes.Isinst, exprType); - } - else if (expr.NodeType == ExpressionType.IsFalse) - { - var falseLabel = il.DefineLabel(); - var continueLabel = il.DefineLabel(); - il.Emit(OpCodes.Brfalse, falseLabel); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Br, continueLabel); - il.MarkLabel(falseLabel); - il.Emit(OpCodes.Ldc_I4_1); - il.MarkLabel(continueLabel); - } - else if (expr.NodeType == ExpressionType.Increment) - { - if (!TryEmitNumberOne(il, exprType)) - return false; - il.Emit(OpCodes.Add); - } - else if (expr.NodeType == ExpressionType.Decrement) - { - if (!TryEmitNumberOne(il, exprType)) - return false; - il.Emit(OpCodes.Sub); - } - else if (expr.NodeType == ExpressionType.Negate || expr.NodeType == ExpressionType.NegateChecked) - { - il.Emit(OpCodes.Neg); - } - else if (expr.NodeType == ExpressionType.OnesComplement) - { - il.Emit(OpCodes.Not); - } - else if (expr.NodeType == ExpressionType.Unbox) - { - il.Emit(OpCodes.Unbox_Any, exprType); - } - else if (expr.NodeType == ExpressionType.IsTrue) - { } - else if (expr.NodeType == ExpressionType.UnaryPlus) - { } - } - return true; - } - - private static bool TryEmitTypeIs(TypeBinaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - if (!TryEmit(expr.Expression, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - else - { - il.Emit(OpCodes.Isinst, expr.TypeOperand); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Cgt_Un); - } - - return true; - } - - private static bool TryEmitNot(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - if (!TryEmit(expr.Operand, paramExprs, il, ref closure, parent)) - return false; - if ((parent & ParentFlags.IgnoreResult) > 0) - il.Emit(OpCodes.Pop); - else - { - if (expr.Type == typeof(bool)) - { - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - { - il.Emit(OpCodes.Not); - } - } - return true; - } - - private static bool TryEmitConvert(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var opExpr = expr.Operand; - var method = expr.Method; - if (method != null && method.Name != "op_Implicit" && method.Name != "op_Explicit") - { - if (!TryEmit(opExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult | ParentFlags.InstanceCall, 0)) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - if ((parent & ParentFlags.IgnoreResult) != 0 && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - return true; - } - - var sourceType = opExpr.Type; - var sourceTypeIsNullable = sourceType.IsNullable(); - var underlyingNullableSourceType = Nullable.GetUnderlyingType(sourceType); - var targetType = expr.Type; - - if (sourceTypeIsNullable && targetType == underlyingNullableSourceType) - { - if (!TryEmit(opExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult | ParentFlags.InstanceAccess)) - return false; - - if (!closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - return true; - } - - if (!TryEmit(opExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) - return false; - - var targetTypeIsNullable = targetType.IsNullable(); - var underlyingNullableTargetType = Nullable.GetUnderlyingType(targetType); - if (targetTypeIsNullable && sourceType == underlyingNullableTargetType) - { - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - return true; - } - - if (sourceType == targetType || targetType == typeof(object)) - { - if (targetType == typeof(object) && sourceType.IsValueType()) - il.Emit(OpCodes.Box, sourceType); - if (IgnoresResult(parent)) - il.Emit(OpCodes.Pop); - return true; - } - - // check implicit / explicit conversion operators on source and target types - // for non-primitives and for non-primitive nullable - #73 - if (!sourceTypeIsNullable && !sourceType.IsPrimitive()) - { - var actualTargetType = targetTypeIsNullable ? underlyingNullableTargetType : targetType; - var convertOpMethod = method ?? sourceType.FindConvertOperator(sourceType, actualTargetType); - if (convertOpMethod != null) - { - il.Emit(OpCodes.Call, convertOpMethod); - - if (targetTypeIsNullable) - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - else if (!targetTypeIsNullable) - { - var actualSourceType = sourceTypeIsNullable ? underlyingNullableSourceType : sourceType; - - var convertOpMethod = method ?? actualSourceType.FindConvertOperator(actualSourceType, targetType); - if (convertOpMethod != null) - { - if (sourceTypeIsNullable) - { - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - } - - il.Emit(OpCodes.Call, convertOpMethod); - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - - if (!targetTypeIsNullable && !targetType.IsPrimitive()) - { - var actualSourceType = sourceTypeIsNullable ? underlyingNullableSourceType : sourceType; - - // ReSharper disable once ConstantNullCoalescingCondition - var convertOpMethod = method ?? targetType.FindConvertOperator(actualSourceType, targetType); - if (convertOpMethod != null) - { - if (sourceTypeIsNullable) - { - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - } - - il.Emit(OpCodes.Call, convertOpMethod); - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - else if (!sourceTypeIsNullable) - { - var actualTargetType = targetTypeIsNullable ? underlyingNullableTargetType : targetType; - var convertOpMethod = method ?? actualTargetType.FindConvertOperator(sourceType, actualTargetType); - if (convertOpMethod != null) - { - il.Emit(OpCodes.Call, convertOpMethod); - - if (targetTypeIsNullable) - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - - if (sourceType == typeof(object) && targetType.IsValueType()) - { - il.Emit(OpCodes.Unbox_Any, targetType); - } - else if (targetTypeIsNullable) - { - // Conversion to Nullable: `new Nullable(T val);` - if (!sourceTypeIsNullable) - { - if (!TryEmitValueConvert(underlyingNullableTargetType, il, isChecked: false)) - return false; - - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - } - else - { - var sourceVarIndex = EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindNullableHasValueGetterMethod()); - - var labelSourceHasValue = il.DefineLabel(); - il.Emit(OpCodes.Brtrue_S, labelSourceHasValue); // jump where source has a value - - // otherwise, emit and load a `new Nullable()` struct (that's why a Init instead of New) - EmitLoadLocalVariable(il, InitValueTypeVariable(il, targetType)); - - // jump to completion - var labelDone = il.DefineLabel(); - il.Emit(OpCodes.Br_S, labelDone); - - // if source nullable has a value: - il.MarkLabel(labelSourceHasValue); - EmitLoadLocalVariableAddress(il, sourceVarIndex); - il.Emit(OpCodes.Call, sourceType.FindNullableGetValueOrDefaultMethod()); - - if (!TryEmitValueConvert(underlyingNullableTargetType, il, - expr.NodeType == ExpressionType.ConvertChecked)) - { - var convertOpMethod = method ?? underlyingNullableTargetType.FindConvertOperator(underlyingNullableSourceType, underlyingNullableTargetType); - if (convertOpMethod == null) - return false; // nor conversion nor conversion operator is found - il.Emit(OpCodes.Call, convertOpMethod); - } - - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - il.MarkLabel(labelDone); - } - } - else - { - if (targetType.GetTypeInfo().IsEnum) - targetType = Enum.GetUnderlyingType(targetType); - - // fixes #159 - if (sourceTypeIsNullable) - { - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - } - - // cast as the last resort and let's it fail if unlucky - if (!TryEmitValueConvert(targetType, il, expr.NodeType == ExpressionType.ConvertChecked)) - il.Emit(OpCodes.Castclass, targetType); - } - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - - private static bool TryEmitValueConvert(Type targetType, ILGenerator il, bool isChecked) - { - if (targetType == typeof(int)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I4 : OpCodes.Conv_I4); - else if (targetType == typeof(float)) - il.Emit(OpCodes.Conv_R4); - else if (targetType == typeof(uint)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U4 : OpCodes.Conv_U4); - else if (targetType == typeof(sbyte)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I1 : OpCodes.Conv_I1); - else if (targetType == typeof(byte)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U1 : OpCodes.Conv_U1); - else if (targetType == typeof(short)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I2 : OpCodes.Conv_I2); - else if (targetType == typeof(ushort) || targetType == typeof(char)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U2 : OpCodes.Conv_U2); - else if (targetType == typeof(long)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I8 : OpCodes.Conv_I8); - else if (targetType == typeof(ulong)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U8 : OpCodes.Conv_U8); - else if (targetType == typeof(double)) - il.Emit(OpCodes.Conv_R8); - else - return false; - return true; - } - - private static bool TryEmitNotNullConstant( - bool considerClosure, Type exprType, object constantValue, ILGenerator il, ref ClosureInfo closure) - { - var constValueType = constantValue.GetType(); - if (considerClosure && IsClosureBoundConstant(constantValue, constValueType.GetTypeInfo())) - { - var constItems = closure.Constants.Items; - var constIndex = closure.Constants.Count - 1; - while (constIndex != -1 && !ReferenceEquals(constItems[constIndex], constantValue)) - --constIndex; - if (constIndex == -1) - return false; - - var varIndex = closure.ConstantUsage.Items[constIndex] - 1; - if (varIndex > 0) - EmitLoadLocalVariable(il, varIndex); - else - { - il.Emit(OpCodes.Ldloc_0); // load constants array from variable - EmitLoadConstantInt(il, constIndex); - il.Emit(OpCodes.Ldelem_Ref); - if (exprType.IsValueType()) - il.Emit(OpCodes.Unbox_Any, exprType); - } - } - else - { - if (constantValue is string s) - { - il.Emit(OpCodes.Ldstr, s); - return true; - } - - if (constantValue is Type t) - { - il.Emit(OpCodes.Ldtoken, t); - il.Emit(OpCodes.Call, _getTypeFromHandleMethod); - return true; - } - - // get raw enum type to light - if (constValueType.GetTypeInfo().IsEnum) - constValueType = Enum.GetUnderlyingType(constValueType); - - if (!TryEmitNumberConstant(il, constantValue, constValueType)) - return false; - } - - var underlyingNullableType = Nullable.GetUnderlyingType(exprType); - if (underlyingNullableType != null) - il.Emit(OpCodes.Newobj, exprType.GetTypeInfo().DeclaredConstructors.GetFirst()); - - // boxing the value type, otherwise we can get a strange result when 0 is treated as Null. - else if (exprType == typeof(object) && constValueType.IsValueType()) - il.Emit(OpCodes.Box, constantValue.GetType()); // using normal type for Enum instead of underlying type - - return true; - } - - // todo: can we do something about boxing? - private static bool TryEmitNumberConstant(ILGenerator il, object constantValue, Type constValueType) - { - if (constValueType == typeof(int)) - { - EmitLoadConstantInt(il, (int)constantValue); - } - else if (constValueType == typeof(char)) - { - EmitLoadConstantInt(il, (char)constantValue); - } - else if (constValueType == typeof(short)) - { - EmitLoadConstantInt(il, (short)constantValue); - } - else if (constValueType == typeof(byte)) - { - EmitLoadConstantInt(il, (byte)constantValue); - } - else if (constValueType == typeof(ushort)) - { - EmitLoadConstantInt(il, (ushort)constantValue); - } - else if (constValueType == typeof(sbyte)) - { - EmitLoadConstantInt(il, (sbyte)constantValue); - } - else if (constValueType == typeof(uint)) - { - unchecked - { - EmitLoadConstantInt(il, (int)(uint)constantValue); - } - } - else if (constValueType == typeof(long)) - { - il.Emit(OpCodes.Ldc_I8, (long)constantValue); - } - else if (constValueType == typeof(ulong)) - { - unchecked - { - il.Emit(OpCodes.Ldc_I8, (long)(ulong)constantValue); - } - } - else if (constValueType == typeof(float)) - { - il.Emit(OpCodes.Ldc_R4, (float)constantValue); - } - else if (constValueType == typeof(double)) - { - il.Emit(OpCodes.Ldc_R8, (double)constantValue); - } - else if (constValueType == typeof(bool)) - { - il.Emit((bool)constantValue ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - } - else if (constValueType == typeof(IntPtr)) - { - il.Emit(OpCodes.Ldc_I8, ((IntPtr)constantValue).ToInt64()); - } - else if (constValueType == typeof(UIntPtr)) - { - unchecked - { - il.Emit(OpCodes.Ldc_I8, (long)((UIntPtr)constantValue).ToUInt64()); - } - } - else if (constValueType == typeof(decimal)) - { - EmitDecimalConstant((decimal)constantValue, il); - } - else - { - return false; - } - - return true; - } - - internal static bool TryEmitNumberOne(ILGenerator il, Type type) - { - if (type == typeof(int) || type == typeof(char) || type == typeof(short) || - type == typeof(byte) || type == typeof(ushort) || type == typeof(sbyte) || - type == typeof(uint)) - { - il.Emit(OpCodes.Ldc_I4_1); - } - else if (type == typeof(long) || type == typeof(ulong) || - type == typeof(IntPtr) || type == typeof(UIntPtr)) - { - il.Emit(OpCodes.Ldc_I8, (long)1); - } - else if (type == typeof(float)) - { - il.Emit(OpCodes.Ldc_R4, 1f); - } - else if (type == typeof(double)) - { - il.Emit(OpCodes.Ldc_R8, 1d); - } - else - { - return false; - } - - return true; - } - - internal static void EmitLoadConstantsAndNestedLambdasIntoVars(ILGenerator il, ref ClosureInfo closure) - { - // Load constants array field from Closure and store it into the variable - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, ArrayClosureArrayField); - EmitStoreLocalVariable(il, il.GetNextLocalVarIndex(typeof(object[]))); - - var constItems = closure.Constants.Items; - var constCount = closure.Constants.Count; - var constUsage = closure.ConstantUsage.Items; - - int varIndex; - for (var i = 0; i < constCount; i++) - { - if (constUsage[i] > 1) - { - il.Emit(OpCodes.Ldloc_0);// load array field variable on a stack - EmitLoadConstantInt(il, i); - il.Emit(OpCodes.Ldelem_Ref); - - var varType = constItems[i].GetType(); - if (varType.IsValueType()) - il.Emit(OpCodes.Unbox_Any, varType); - - varIndex = il.GetNextLocalVarIndex(varType); - constUsage[i] = varIndex + 1; // to distinguish from the default 1 - EmitStoreLocalVariable(il, varIndex); - } - } - - var nestedLambdas = closure.NestedLambdas; - for (var i = 0; i < nestedLambdas.Length; i++) - { - var nestedLambda = nestedLambdas[i]; - if (nestedLambda.UsageCountOrVarIndex > 1) - { - il.Emit(OpCodes.Ldloc_0);// load array field variable on a stack - EmitLoadConstantInt(il, constCount + i); - il.Emit(OpCodes.Ldelem_Ref); - varIndex = il.GetNextLocalVarIndex(nestedLambda.Lambda.GetType()); - nestedLambda.UsageCountOrVarIndex = varIndex + 1; - EmitStoreLocalVariable(il, varIndex); - } - } - } - - private static void EmitDecimalConstant(decimal value, ILGenerator il) - { - //check if decimal has decimal places, if not use shorter IL code (constructor from int or long) - if (value % 1 == 0) - { - if (value >= int.MinValue && value <= int.MaxValue) - { - EmitLoadConstantInt(il, decimal.ToInt32(value)); - il.Emit(OpCodes.Newobj, typeof(decimal).FindSingleParamConstructor(typeof(int))); - return; - } - - if (value >= long.MinValue && value <= long.MaxValue) - { - il.Emit(OpCodes.Ldc_I8, decimal.ToInt64(value)); - il.Emit(OpCodes.Newobj, typeof(decimal).FindSingleParamConstructor(typeof(long))); - return; - } - } - - if (value == decimal.MinValue) - { - il.Emit(OpCodes.Ldsfld, typeof(decimal).GetTypeInfo().GetDeclaredField(nameof(decimal.MinValue))); - return; - } - - if (value == decimal.MaxValue) - { - il.Emit(OpCodes.Ldsfld, typeof(decimal).GetTypeInfo().GetDeclaredField(nameof(decimal.MaxValue))); - return; - } - - var parts = decimal.GetBits(value); - var sign = (parts[3] & 0x80000000) != 0; - var scale = (byte)((parts[3] >> 16) & 0x7F); - - EmitLoadConstantInt(il, parts[0]); - EmitLoadConstantInt(il, parts[1]); - EmitLoadConstantInt(il, parts[2]); - - il.Emit(sign ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - EmitLoadConstantInt(il, scale); - - il.Emit(OpCodes.Conv_U1); - - il.Emit(OpCodes.Newobj, _decimalCtor.Value); - } - - private static readonly Lazy _decimalCtor = new Lazy(() => - { - foreach (var ctor in typeof(decimal).GetTypeInfo().DeclaredConstructors) - if (ctor.GetParameters().Length == 5) - return ctor; - return null; - }); - - private static int InitValueTypeVariable(ILGenerator il, Type exprType) - { - var locVarIndex = il.GetNextLocalVarIndex(exprType); - EmitLoadLocalVariableAddress(il, locVarIndex); - il.Emit(OpCodes.Initobj, exprType); - return locVarIndex; - } - - private static bool EmitNewArray(NewArrayExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var arrayType = expr.Type; - var elems = expr.Expressions; - var elemType = arrayType.GetElementType(); - if (elemType == null) - return false; - - var rank = arrayType.GetArrayRank(); - if (rank == 1) // one dimensional - { - EmitLoadConstantInt(il, elems.Count); - } - else // multi dimensional - { - for (var i = 0; i < elems.Count; i++) - if (!TryEmit(elems[i], paramExprs, il, ref closure, parent, i)) - return false; - - il.Emit(OpCodes.Newobj, arrayType.GetTypeInfo().DeclaredConstructors.GetFirst()); - return true; - } - - il.Emit(OpCodes.Newarr, elemType); - - var isElemOfValueType = elemType.IsValueType(); - - for (int i = 0, n = elems.Count; i < n; i++) - { - il.Emit(OpCodes.Dup); - EmitLoadConstantInt(il, i); - - // loading element address for later copying of value into it. - if (isElemOfValueType) - il.Emit(OpCodes.Ldelema, elemType); - - if (!TryEmit(elems[i], paramExprs, il, ref closure, parent)) - return false; - - if (isElemOfValueType) - il.Emit(OpCodes.Stobj, elemType); // store element of value type by array element address - else - il.Emit(OpCodes.Stelem_Ref); - } - - return true; - } - - private static bool TryEmitArrayIndex(Type exprType, ILGenerator il) - { - if (exprType.IsValueType()) - il.Emit(OpCodes.Ldelem, exprType); - else - il.Emit(OpCodes.Ldelem_Ref); - return true; - } - - private static bool EmitMemberInit(MemberInitExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var valueVarIndex = -1; - if (expr.Type.IsValueType()) - valueVarIndex = il.GetNextLocalVarIndex(expr.Type); - - var newExpr = expr.NewExpression; - if (newExpr == null) - { - if (!TryEmit(expr.Expression, paramExprs, il, ref closure, parent)) - return false; - } - else - { - var argExprs = newExpr.Arguments; - for (var i = 0; i < argExprs.Count; i++) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent, i)) - return false; - - var ctor = newExpr.Constructor; - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (ctor != null) - il.Emit(OpCodes.Newobj, ctor); - else if (newExpr.Type.IsValueType()) - { - if (valueVarIndex == -1) - valueVarIndex = il.GetNextLocalVarIndex(expr.Type); - EmitLoadLocalVariableAddress(il, valueVarIndex); - il.Emit(OpCodes.Initobj, newExpr.Type); - } - else - return false; // null constructor and not a value type, better to fallback - } - - var bindings = expr.Bindings; - for (var i = 0; i < bindings.Count; i++) - { - var binding = bindings[i]; - if (binding.BindingType != MemberBindingType.Assignment) - return false; - - if (valueVarIndex != -1) // load local value address, to set its members - EmitLoadLocalVariableAddress(il, valueVarIndex); - else - il.Emit(OpCodes.Dup); // duplicate member owner on stack - - if (!TryEmit(((MemberAssignment)binding).Expression, paramExprs, il, ref closure, parent) || - !EmitMemberAssign(il, binding.Member)) - return false; - } - - if (valueVarIndex != -1) - EmitLoadLocalVariable(il, valueVarIndex); - return true; - } - - private static bool EmitMemberAssign(ILGenerator il, MemberInfo member) - { - if (member is PropertyInfo prop) - { - var method = prop.DeclaringType.FindPropertySetMethod(prop.Name); - if (method == null) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - return true; - } - - if (member is FieldInfo field) - { - il.Emit(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field); - return true; - } - - return false; - } - - private static bool TryEmitIncDecAssign(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var operandExpr = expr.Operand; - - MemberExpression memberAccess; - var useLocalVar = false; - int localVarIndex, paramIndex = -1; - - var isParameterOrVariable = operandExpr.NodeType == ExpressionType.Parameter; - var usesResult = (parent & ParentFlags.IgnoreResult) == 0; - if (isParameterOrVariable) - { - localVarIndex = closure.GetDefinedLocalVarOrDefault((ParameterExpression)operandExpr); - if (localVarIndex != -1) - { - EmitLoadLocalVariable(il, localVarIndex); - useLocalVar = true; - } - else - { - paramIndex = paramExprs.Count - 1; - while (paramIndex != -1 && !ReferenceEquals(paramExprs[paramIndex], operandExpr)) - --paramIndex; - if (paramIndex == -1) - return false; - il.Emit(OpCodes.Ldarg, paramIndex + 1); - } - - memberAccess = null; - - } - else if (operandExpr.NodeType == ExpressionType.MemberAccess) - { - memberAccess = (MemberExpression)operandExpr; - - if (!TryEmitMemberAccess(memberAccess, paramExprs, il, ref closure, parent | ParentFlags.DupMemberOwner)) - return false; - - useLocalVar = memberAccess.Expression != null && (usesResult || memberAccess.Member is PropertyInfo); - localVarIndex = useLocalVar ? il.GetNextLocalVarIndex(operandExpr.Type) : -1; - } - else - return false; - - switch (expr.NodeType) - { - case ExpressionType.PreIncrementAssign: - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Add); - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - break; - - case ExpressionType.PostIncrementAssign: - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Add); - break; - - case ExpressionType.PreDecrementAssign: - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Sub); - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - break; - - case ExpressionType.PostDecrementAssign: - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Sub); - break; - } - - if (isParameterOrVariable && paramIndex != -1) - il.Emit(OpCodes.Starg_S, paramIndex + 1); - else if (isParameterOrVariable || useLocalVar && !usesResult) - EmitStoreLocalVariable(il, localVarIndex); - - if (isParameterOrVariable) - return true; - - if (useLocalVar && !usesResult) - EmitLoadLocalVariable(il, localVarIndex); - - if (!EmitMemberAssign(il, memberAccess.Member)) - return false; - - if (useLocalVar && usesResult) - EmitLoadLocalVariable(il, localVarIndex); - - return true; - } - - private static void StoreIncDecValue(ILGenerator il, bool usesResult, bool isVar, int localVarIndex) - { - if (!usesResult) - return; - - if (isVar || localVarIndex == -1) - il.Emit(OpCodes.Dup); - else - { - EmitStoreLocalVariable(il, localVarIndex); - EmitLoadLocalVariable(il, localVarIndex); - } - } - - private static bool TryEmitAssign(BinaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var left = expr.Left; - var right = expr.Right; - var leftNodeType = expr.Left.NodeType; - var nodeType = expr.NodeType; - - // if this assignment is part of a single body-less expression or the result of a block - // we should put its result to the evaluation stack before the return, otherwise we are - // somewhere inside the block, so we shouldn't return with the result - var flags = parent & ~ParentFlags.IgnoreResult; - switch (leftNodeType) - { - case ExpressionType.Parameter: - var leftParamExpr = (ParameterExpression)left; - - var paramIndex = paramExprs.Count - 1; - while (paramIndex != -1 && !ReferenceEquals(paramExprs[paramIndex], leftParamExpr)) - --paramIndex; - - var arithmeticNodeType = nodeType; - switch (nodeType) - { - case ExpressionType.AddAssign: - arithmeticNodeType = ExpressionType.Add; - break; - case ExpressionType.AddAssignChecked: - arithmeticNodeType = ExpressionType.AddChecked; - break; - case ExpressionType.SubtractAssign: - arithmeticNodeType = ExpressionType.Subtract; - break; - case ExpressionType.SubtractAssignChecked: - arithmeticNodeType = ExpressionType.SubtractChecked; - break; - case ExpressionType.MultiplyAssign: - arithmeticNodeType = ExpressionType.Multiply; - break; - case ExpressionType.MultiplyAssignChecked: - arithmeticNodeType = ExpressionType.MultiplyChecked; - break; - case ExpressionType.DivideAssign: - arithmeticNodeType = ExpressionType.Divide; - break; - case ExpressionType.ModuloAssign: - arithmeticNodeType = ExpressionType.Modulo; - break; - case ExpressionType.PowerAssign: - arithmeticNodeType = ExpressionType.Power; - break; - case ExpressionType.AndAssign: - arithmeticNodeType = ExpressionType.And; - break; - case ExpressionType.OrAssign: - arithmeticNodeType = ExpressionType.Or; - break; - case ExpressionType.ExclusiveOrAssign: - arithmeticNodeType = ExpressionType.ExclusiveOr; - break; - case ExpressionType.LeftShiftAssign: - arithmeticNodeType = ExpressionType.LeftShift; - break; - case ExpressionType.RightShiftAssign: - arithmeticNodeType = ExpressionType.RightShift; - break; - } - - if (paramIndex != -1) - { - // shift parameter index by one, because the first one will be closure - if ((closure.Status & ClosureStatus.ShouldBeStaticMethod) == 0) - ++paramIndex; - - if (leftParamExpr.IsByRef) - { - if (paramIndex == 0) - il.Emit(OpCodes.Ldarg_0); - else if (paramIndex == 1) - il.Emit(OpCodes.Ldarg_1); - else if (paramIndex == 2) - il.Emit(OpCodes.Ldarg_2); - else if (paramIndex == 3) - il.Emit(OpCodes.Ldarg_3); - else - il.Emit(OpCodes.Ldarg_S, (byte)paramIndex); - } - - if (arithmeticNodeType == nodeType) - { - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - } - else if (!TryEmitArithmetic(expr, arithmeticNodeType, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) == 0) - il.Emit(OpCodes.Dup); // duplicate value to assign and return - - if (leftParamExpr.IsByRef) - EmitByRefStore(il, leftParamExpr.Type); - else - il.Emit(OpCodes.Starg_S, paramIndex); - - return true; - } - else if (arithmeticNodeType != nodeType) - { - var localVarIdx = closure.GetDefinedLocalVarOrDefault(leftParamExpr); - if (localVarIdx != -1) - { - if (!TryEmitArithmetic(expr, arithmeticNodeType, paramExprs, il, ref closure, parent)) - return false; - - EmitStoreLocalVariable(il, localVarIdx); - return true; - } - } - - // if parameter isn't passed, then it is passed into some outer lambda or it is a local variable, - // so it should be loaded from closure or from the locals. Then the closure is null will be an invalid state. - // if it's a local variable, then store the right value in it - var localVariableIdx = closure.GetDefinedLocalVarOrDefault(leftParamExpr); - if (localVariableIdx != -1) - { - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - if ((right as ParameterExpression)?.IsByRef == true) - il.Emit(OpCodes.Ldind_I4); - - if ((parent & ParentFlags.IgnoreResult) == 0) // if we have to push the result back, duplicate the right value - il.Emit(OpCodes.Dup); - - EmitStoreLocalVariable(il, localVariableIdx); - return true; - } - - // check that it's a captured parameter by closure - var nonPassedParams = closure.NonPassedParameters; - var nonPassedParamIndex = nonPassedParams.Length - 1; - while (nonPassedParamIndex != -1 && - !ReferenceEquals(nonPassedParams[nonPassedParamIndex], leftParamExpr)) - --nonPassedParamIndex; - if (nonPassedParamIndex == -1) - return false; // what??? no chance - - il.Emit(OpCodes.Ldarg_0); // closure is always a first argument - - if ((parent & ParentFlags.IgnoreResult) == 0) - { - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - var valueVarIndex = il.GetNextLocalVarIndex(expr.Type); // store left value in variable - EmitStoreLocalVariable(il, valueVarIndex); - - // load array field and param item index - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, nonPassedParamIndex); - EmitLoadLocalVariable(il, valueVarIndex); - if (expr.Type.IsValueType()) - il.Emit(OpCodes.Box, expr.Type); - il.Emit(OpCodes.Stelem_Ref); // put the variable into array - EmitLoadLocalVariable(il, valueVarIndex); - } - else - { - // load array field and param item index - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, nonPassedParamIndex); - - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - if (expr.Type.IsValueType()) - il.Emit(OpCodes.Box, expr.Type); - il.Emit(OpCodes.Stelem_Ref); // put the variable into array - } - - return true; - - case ExpressionType.MemberAccess: - var assignFromLocalVar = right.NodeType == ExpressionType.Try; - - var resultLocalVarIndex = -1; - if (assignFromLocalVar) - { - resultLocalVarIndex = il.GetNextLocalVarIndex(right.Type); - - if (!TryEmit(right, paramExprs, il, ref closure, ParentFlags.Empty)) - return false; - - EmitStoreLocalVariable(il, resultLocalVarIndex); - } - - var memberExpr = (MemberExpression)left; - var objExpr = memberExpr.Expression; - if (objExpr != null && - !TryEmit(objExpr, paramExprs, il, ref closure, flags | ParentFlags.MemberAccess | ParentFlags.InstanceAccess)) - return false; - - if (assignFromLocalVar) - EmitLoadLocalVariable(il, resultLocalVarIndex); - else if (!TryEmit(right, paramExprs, il, ref closure, ParentFlags.Empty)) - return false; - - var member = memberExpr.Member; - if ((parent & ParentFlags.IgnoreResult) != 0) - return EmitMemberAssign(il, member); - - il.Emit(OpCodes.Dup); - - var rightVarIndex = il.GetNextLocalVarIndex(expr.Type); // store right value in variable - EmitStoreLocalVariable(il, rightVarIndex); - - if (!EmitMemberAssign(il, member)) - return false; - - EmitLoadLocalVariable(il, rightVarIndex); - return true; - - case ExpressionType.Index: - var indexExpr = (IndexExpression)left; - - var obj = indexExpr.Object; - if (obj != null && !TryEmit(obj, paramExprs, il, ref closure, flags)) - return false; - - var indexArgExprs = indexExpr.Arguments; - for (var i = 0; i < indexArgExprs.Count; i++) - if (!TryEmit(indexArgExprs[i], paramExprs, il, ref closure, flags, i)) - return false; - - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - return TryEmitIndexAssign(indexExpr, obj?.Type, expr.Type, il); - - var varIndex = il.GetNextLocalVarIndex(expr.Type); // store value in variable to return - il.Emit(OpCodes.Dup); - EmitStoreLocalVariable(il, varIndex); - - if (!TryEmitIndexAssign(indexExpr, obj?.Type, expr.Type, il)) - return false; - - EmitLoadLocalVariable(il, varIndex); - return true; - - default: // todo: not yet support assignment targets - return false; - } - } - - private static void EmitByRefStore(ILGenerator il, Type type) - { - if (type == typeof(int) || type == typeof(uint)) - il.Emit(OpCodes.Stind_I4); - else if (type == typeof(byte)) - il.Emit(OpCodes.Stind_I1); - else if (type == typeof(short) || type == typeof(ushort)) - il.Emit(OpCodes.Stind_I2); - else if (type == typeof(long) || type == typeof(ulong)) - il.Emit(OpCodes.Stind_I8); - else if (type == typeof(float)) - il.Emit(OpCodes.Stind_R4); - else if (type == typeof(double)) - il.Emit(OpCodes.Stind_R8); - else if (type == typeof(object)) - il.Emit(OpCodes.Stind_Ref); - else if (type == typeof(IntPtr) || type == typeof(UIntPtr)) - il.Emit(OpCodes.Stind_I); - else - il.Emit(OpCodes.Stobj, type); - } - - private static bool TryEmitIndexAssign(IndexExpression indexExpr, Type instType, Type elementType, ILGenerator il) - { - if (indexExpr.Indexer != null) - return EmitMemberAssign(il, indexExpr.Indexer); - - if (indexExpr.Arguments.Count == 1) // one dimensional array - { - if (elementType.IsValueType()) - il.Emit(OpCodes.Stelem, elementType); - else - il.Emit(OpCodes.Stelem_Ref); - return true; - } - - // multi dimensional array - return EmitMethodCall(il, instType?.FindMethod("Set")); - } - - private static bool TryEmitMethodCall(Expression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var flags = parent & ~ParentFlags.IgnoreResult | ParentFlags.Call; - var callExpr = (MethodCallExpression)expr; - var objExpr = callExpr.Object; - var method = callExpr.Method; - var methodParams = method.GetParameters(); - var objIsValueType = false; - if (objExpr != null) - { - if (!TryEmit(objExpr, paramExprs, il, ref closure, flags | ParentFlags.InstanceAccess)) - return false; - - objIsValueType = objExpr.Type.IsValueType(); - if (objIsValueType && objExpr.NodeType != ExpressionType.Parameter && !closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, objExpr.Type); - } - - var fewArgCount = callExpr.FewArgumentCount; - if (fewArgCount >= 0) - { - if (fewArgCount == 1) - { - if (!TryEmit(((OneArgumentMethodCallExpression)callExpr).Argument, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1)) - return false; - } - else if (fewArgCount == 2) - { - var twoArgsExpr = (TwoArgumentsMethodCallExpression)callExpr; - if (!TryEmit(twoArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(twoArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1)) - return false; - } - else if (fewArgCount == 3) - { - var threeArgsExpr = (ThreeArgumentsMethodCallExpression)callExpr; - if (!TryEmit(threeArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(threeArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(threeArgsExpr.Argument2, paramExprs, il, ref closure, flags, methodParams[2].ParameterType.IsByRef ? 2 : -1)) - return false; - } - else if (fewArgCount == 4) - { - var fourArgsExpr = (FourArgumentsMethodCallExpression)callExpr; - if (!TryEmit(fourArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fourArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fourArgsExpr.Argument2, paramExprs, il, ref closure, flags, methodParams[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fourArgsExpr.Argument3, paramExprs, il, ref closure, flags, methodParams[3].ParameterType.IsByRef ? 3 : -1)) - return false; - } - else if (fewArgCount == 5) - { - var fiveArgsExpr = (FiveArgumentsMethodCallExpression)callExpr; - if (!TryEmit(fiveArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fiveArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fiveArgsExpr.Argument2, paramExprs, il, ref closure, flags, methodParams[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fiveArgsExpr.Argument3, paramExprs, il, ref closure, flags, methodParams[3].ParameterType.IsByRef ? 3 : -1) || - !TryEmit(fiveArgsExpr.Argument4, paramExprs, il, ref closure, flags, methodParams[4].ParameterType.IsByRef ? 4 : -1)) - return false; - } - - if (objIsValueType && method.IsVirtual) - il.Emit(OpCodes.Constrained, objExpr.Type); - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - if (parent.IgnoresResult() && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - closure.LastEmitIsAddress = false; - return true; - } - - var args = callExpr.Arguments; - for (var i = 0; i < methodParams.Length; i++) - if (!TryEmit(args[i], paramExprs, il, ref closure, flags, methodParams[i].ParameterType.IsByRef ? i : -1)) - return false; - - if (objIsValueType && method.IsVirtual) - il.Emit(OpCodes.Constrained, objExpr.Type); - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - if (parent.IgnoresResult() && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - - closure.LastEmitIsAddress = false; - return true; - } - - private static bool TryEmitMemberAccess(MemberExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - if (expr.Member is PropertyInfo prop) - { - var instanceExpr = expr.Expression; - if (instanceExpr != null) - { - if (!TryEmit(instanceExpr, paramExprs, il, ref closure, - ~ParentFlags.IgnoreResult & ~ParentFlags.DupMemberOwner & - (parent | ParentFlags.Call | ParentFlags.MemberAccess | ParentFlags.InstanceAccess))) - return false; - - if ((parent & ParentFlags.DupMemberOwner) != 0) - il.Emit(OpCodes.Dup); - - // Value type special treatment to load address of value instance in order to access a field or call a method. - // Parameter should be excluded because it already loads an address via `LDARGA`, and you don't need to. - // And for field access no need to load address, cause the field stored on stack nearby - if (!closure.LastEmitIsAddress && - instanceExpr.NodeType != ExpressionType.Parameter && instanceExpr.Type.IsValueType()) - EmitStoreLocalVariableAndLoadItsAddress(il, instanceExpr.Type); - } - - closure.LastEmitIsAddress = false; - var propGetter = prop.DeclaringType.FindPropertyGetMethod(prop.Name); - if (propGetter == null) - return false; - - il.Emit(propGetter.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, propGetter); - return true; - } - - if (expr.Member is FieldInfo field) - { - var instanceExpr = expr.Expression; - if (instanceExpr != null) - { - if (!TryEmit(instanceExpr, paramExprs, il, ref closure, - ~ParentFlags.IgnoreResult & ~ParentFlags.DupMemberOwner & - (parent | ParentFlags.MemberAccess | ParentFlags.InstanceAccess))) - return false; - - if ((parent & ParentFlags.DupMemberOwner) != 0) - il.Emit(OpCodes.Dup); - - closure.LastEmitIsAddress = field.FieldType.IsValueType() && (parent & ParentFlags.InstanceAccess) != 0; - il.Emit(closure.LastEmitIsAddress ? OpCodes.Ldflda : OpCodes.Ldfld, field); - } - else if (field.IsLiteral) - { - var fieldValue = field.GetValue(null); - if (fieldValue != null) - return TryEmitNotNullConstant(false, field.FieldType, fieldValue, il, ref closure); - - il.Emit(OpCodes.Ldnull); - } - else - { - il.Emit(OpCodes.Ldsfld, field); - } - - return true; - } - - return false; - } - - // ReSharper disable once FunctionComplexityOverflow - private static bool TryEmitNestedLambda(LambdaExpression lambdaExpr, - IReadOnlyList outerParamExprs, ILGenerator il, ref ClosureInfo closure) - { - // First, find in closed compiled lambdas the one corresponding to the current lambda expression. - // Situation with not found lambda is not possible/exceptional, - // it means that we somehow skipped the lambda expression while collecting closure info. - var outerNestedLambdas = closure.NestedLambdas; - var outerNestedLambdaIndex = outerNestedLambdas.Length - 1; - while (outerNestedLambdaIndex != -1 && - !ReferenceEquals(outerNestedLambdas[outerNestedLambdaIndex].LambdaExpression, lambdaExpr)) - --outerNestedLambdaIndex; - if (outerNestedLambdaIndex == -1) - return false; - - var nestedLambdaInfo = closure.NestedLambdas[outerNestedLambdaIndex]; - var nestedLambda = nestedLambdaInfo.Lambda; - var nestedLambdaInClosureIndex = outerNestedLambdaIndex + closure.Constants.Count; - - var varIndex = nestedLambdaInfo.UsageCountOrVarIndex - 1; - if (varIndex > 0) - EmitLoadLocalVariable(il, varIndex); - else - { - il.Emit(OpCodes.Ldloc_0); - EmitLoadConstantInt(il, nestedLambdaInClosureIndex); - il.Emit(OpCodes.Ldelem_Ref); // load the array item object - } - - // If lambda does not use any outer parameters to be set in closure, then we're done - ref var nestedClosureInfo = ref nestedLambdaInfo.ClosureInfo; - var nestedNonPassedParams = nestedClosureInfo.NonPassedParameters; - if (nestedNonPassedParams.Length == 0) - return true; - - //------------------------------------------------------------------- - // For the lambda with non-passed parameters (or variables) in closure - // we have loaded `NestedLambdaWithConstantsAndNestedLambdas` pair. - if (varIndex > 0) - { - // we are already have variable loaded - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.NestedLambdaField); - EmitLoadLocalVariable(il, varIndex); // load the variable for the second time - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.ConstantsAndNestedLambdasField); - } - else - { - var nestedLambdaAndClosureItemsVarIndex = il.GetNextLocalVarIndex(typeof(NestedLambdaWithConstantsAndNestedLambdas)); - EmitStoreLocalVariable(il, nestedLambdaAndClosureItemsVarIndex); - - // - load the `NestedLambda` field - EmitLoadLocalVariable(il, nestedLambdaAndClosureItemsVarIndex); - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.NestedLambdaField); - - // - load the `ConstantsAndNestedLambdas` field - EmitLoadLocalVariable(il, nestedLambdaAndClosureItemsVarIndex); - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.ConstantsAndNestedLambdasField); - } - - // - create `NonPassedParameters` array - EmitLoadConstantInt(il, nestedNonPassedParams.Length); // size of array - il.Emit(OpCodes.Newarr, typeof(object)); - - // - populate the `NonPassedParameters` array - var outerNonPassedParams = closure.NonPassedParameters; - for (var nestedParamIndex = 0; nestedParamIndex < nestedNonPassedParams.Length; ++nestedParamIndex) - { - var nestedParam = nestedNonPassedParams[nestedParamIndex]; - - // Duplicate nested array on stack to store the item, and load index to where to store - il.Emit(OpCodes.Dup); - EmitLoadConstantInt(il, nestedParamIndex); - - var outerParamIndex = outerParamExprs.Count - 1; - while (outerParamIndex != -1 && !ReferenceEquals(outerParamExprs[outerParamIndex], nestedParam)) - --outerParamIndex; - if (outerParamIndex != -1) // load parameter from input outer params - { - // Add `+1` to index because the `0` index is for the closure argument - if (outerParamIndex == 0) - il.Emit(OpCodes.Ldarg_1); - else if (outerParamIndex == 1) - il.Emit(OpCodes.Ldarg_2); - else if (outerParamIndex == 2) - il.Emit(OpCodes.Ldarg_3); - else - il.Emit(OpCodes.Ldarg_S, (byte)(1 + outerParamIndex)); - - if (nestedParam.Type.IsValueType()) - il.Emit(OpCodes.Box, nestedParam.Type); - } - else // load parameter from outer closure or from the local variables - { - if (outerNonPassedParams.Length == 0) - return false; // impossible, better to throw? - - var variableIdx = closure.GetDefinedLocalVarOrDefault(nestedParam); - if (variableIdx != -1) // it's a local variable - { - EmitLoadLocalVariable(il, variableIdx); - } - else // it's a parameter from the outer closure - { - var outerNonPassedParamIndex = outerNonPassedParams.Length - 1; - while (outerNonPassedParamIndex != -1 && !ReferenceEquals(outerNonPassedParams[outerNonPassedParamIndex], nestedParam)) - --outerNonPassedParamIndex; - if (outerNonPassedParamIndex == -1) - return false; // impossible - - // Load the parameter from outer closure `Items` array - il.Emit(OpCodes.Ldarg_0); // closure is always a first argument - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, outerNonPassedParamIndex); - il.Emit(OpCodes.Ldelem_Ref); - } - } - - // Store the item into nested lambda array - il.Emit(OpCodes.Stelem_Ref); - } - - // - create `ArrayClosureWithNonPassedParams` out of the both above - il.Emit(OpCodes.Newobj, ArrayClosureWithNonPassedParamsConstructor); - - // - call `Curry` method with nested lambda and array closure to produce a closed lambda with the expected signature - var lambdaTypeArgs = nestedLambda.GetType().GetTypeInfo().GenericTypeArguments; - - var closureMethod = nestedLambdaInfo.LambdaExpression.ReturnType == typeof(void) - ? CurryClosureActions.Methods[lambdaTypeArgs.Length - 1].MakeGenericMethod(lambdaTypeArgs) - : CurryClosureFuncs.Methods[lambdaTypeArgs.Length - 2].MakeGenericMethod(lambdaTypeArgs); - - il.Emit(OpCodes.Call, closureMethod); - return true; - } - - private static bool TryEmitInvoke(InvocationExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var lambda = expr.Expression; - if (!TryEmit(lambda, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - - var argExprs = expr.Arguments; - for (var i = 0; i < argExprs.Count; i++) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, - parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess, - argExprs[i].Type.IsByRef ? i : -1)) - return false; - - var delegateInvokeMethod = lambda.Type.FindDelegateInvokeMethod(); - il.Emit(OpCodes.Call, delegateInvokeMethod); - - if ((parent & ParentFlags.IgnoreResult) != 0 && delegateInvokeMethod.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - - return true; - } - - private static bool TryEmitSwitch(SwitchExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - // todo: - //- use switch statement for int comparison (if int difference is less or equal 3 -> use IL switch) - //- TryEmitComparison should not emit "CEQ" so we could use Beq_S instead of Brtrue_S (not always possible (nullable)) - //- if switch SwitchValue is a nullable parameter, we should call getValue only once and store the result. - //- use comparison methods (when defined) - - var endLabel = il.DefineLabel(); - var labels = new Label[expr.Cases.Count]; - for (var index = 0; index < expr.Cases.Count; index++) - { - var switchCase = expr.Cases[index]; - labels[index] = il.DefineLabel(); - - foreach (var switchCaseTestValue in switchCase.TestValues) - { - if (!TryEmitComparison(expr.SwitchValue, switchCaseTestValue, ExpressionType.Equal, paramExprs, il, - ref closure, parent)) - return false; - il.Emit(OpCodes.Brtrue, labels[index]); - } - } - - if (expr.DefaultBody != null) - { - if (!TryEmit(expr.DefaultBody, paramExprs, il, ref closure, parent)) - return false; - il.Emit(OpCodes.Br, endLabel); - } - - for (var index = 0; index < expr.Cases.Count; index++) - { - var switchCase = expr.Cases[index]; - il.MarkLabel(labels[index]); - if (!TryEmit(switchCase.Body, paramExprs, il, ref closure, parent)) - return false; - - if (index != expr.Cases.Count - 1) - il.Emit(OpCodes.Br, endLabel); - } - - il.MarkLabel(endLabel); - - return true; - } - - private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, ExpressionType expressionType, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var leftOpType = exprLeft.Type; - var leftIsNullable = leftOpType.IsNullable(); - var rightOpType = exprRight.Type; - if (exprRight is ConstantExpression c && c.Value == null && exprRight.Type == typeof(object)) - rightOpType = leftOpType; - - int lVarIndex = -1, rVarIndex = -1; - if (!TryEmit(exprLeft, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) - return false; - - if (leftIsNullable) - { - lVarIndex = EmitStoreLocalVariableAndLoadItsAddress(il, leftOpType); - il.Emit(OpCodes.Call, leftOpType.FindNullableGetValueOrDefaultMethod()); - leftOpType = Nullable.GetUnderlyingType(leftOpType); - } - - if (!TryEmit(exprRight, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) - return false; - - if (leftOpType != rightOpType) - { - if (leftOpType.IsClass() && rightOpType.IsClass() && - (leftOpType == typeof(object) || rightOpType == typeof(object))) - { - if (expressionType == ExpressionType.Equal) - { - il.Emit(OpCodes.Ceq); - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - } - else if (expressionType == ExpressionType.NotEqual) - { - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - - if (rightOpType.IsNullable()) - { - rVarIndex = EmitStoreLocalVariableAndLoadItsAddress(il, rightOpType); - il.Emit(OpCodes.Call, rightOpType.FindNullableGetValueOrDefaultMethod()); - // ReSharper disable once AssignNullToNotNullAttribute - rightOpType = Nullable.GetUnderlyingType(rightOpType); - } - - var leftOpTypeInfo = leftOpType.GetTypeInfo(); - if (!leftOpTypeInfo.IsPrimitive && !leftOpTypeInfo.IsEnum) - { - var methodName - = expressionType == ExpressionType.Equal ? "op_Equality" - : expressionType == ExpressionType.NotEqual ? "op_Inequality" - : expressionType == ExpressionType.GreaterThan ? "op_GreaterThan" - : expressionType == ExpressionType.GreaterThanOrEqual ? "op_GreaterThanOrEqual" - : expressionType == ExpressionType.LessThan ? "op_LessThan" - : expressionType == ExpressionType.LessThanOrEqual ? "op_LessThanOrEqual" - : null; - - if (methodName == null) - return false; - - // todo: for now handling only parameters of the same type - var methods = leftOpTypeInfo.DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && m.IsStatic && m.Name == methodName && - IsComparisonOperatorSignature(leftOpType, m.GetParameters())) - { - il.Emit(OpCodes.Call, m); - return true; - } - } - - if (expressionType != ExpressionType.Equal && expressionType != ExpressionType.NotEqual) - return false; - - il.Emit(OpCodes.Call, _objectEqualsMethod); - - if (expressionType == ExpressionType.NotEqual) // invert result for not equal - { - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - - if (leftIsNullable) - goto nullCheck; - - if ((parent & ParentFlags.IgnoreResult) > 0) - il.Emit(OpCodes.Pop); - - return true; - } - - // handle primitives comparison - switch (expressionType) - { - case ExpressionType.Equal: - il.Emit(OpCodes.Ceq); - break; - - case ExpressionType.NotEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - - case ExpressionType.LessThan: - il.Emit(OpCodes.Clt); - break; - - case ExpressionType.GreaterThan: - il.Emit(OpCodes.Cgt); - break; - - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LessThanOrEqual: - var ifTrueLabel = il.DefineLabel(); - if (rightOpType == typeof(uint) || rightOpType == typeof(ulong) || - rightOpType == typeof(ushort) || rightOpType == typeof(byte)) - il.Emit(expressionType == ExpressionType.GreaterThanOrEqual ? OpCodes.Bge_Un_S : OpCodes.Ble_Un_S, ifTrueLabel); - else - il.Emit(expressionType == ExpressionType.GreaterThanOrEqual ? OpCodes.Bge_S : OpCodes.Ble_S, ifTrueLabel); - - il.Emit(OpCodes.Ldc_I4_0); - var doneLabel = il.DefineLabel(); - il.Emit(OpCodes.Br_S, doneLabel); - - il.MarkLabel(ifTrueLabel); - il.Emit(OpCodes.Ldc_I4_1); - - il.MarkLabel(doneLabel); - break; - - default: - return false; - } - - nullCheck: - if (leftIsNullable) - { - var leftNullableHasValueGetterMethod = exprLeft.Type.FindNullableHasValueGetterMethod(); - - EmitLoadLocalVariableAddress(il, lVarIndex); - il.Emit(OpCodes.Call, leftNullableHasValueGetterMethod); - - // ReSharper disable once AssignNullToNotNullAttribute - EmitLoadLocalVariableAddress(il, rVarIndex); - il.Emit(OpCodes.Call, leftNullableHasValueGetterMethod); - - switch (expressionType) - { - case ExpressionType.Equal: - il.Emit(OpCodes.Ceq); // compare both HasValue calls - il.Emit(OpCodes.And); // both results need to be true - break; - - case ExpressionType.NotEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Or); - break; - - case ExpressionType.LessThan: - case ExpressionType.GreaterThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.GreaterThanOrEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.And); - break; - - default: - return false; - } - } - - if ((parent & ParentFlags.IgnoreResult) > 0) - il.Emit(OpCodes.Pop); - - return true; - } - - private static bool IsComparisonOperatorSignature(Type t, ParameterInfo[] pars) => - pars.Length == 2 && pars[0].ParameterType == t && pars[1].ParameterType == t; - - private static bool TryEmitArithmetic(BinaryExpression expr, ExpressionType exprNodeType, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - var flags = parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceCall | ParentFlags.Arithmetic; - - var leftNoValueLabel = default(Label); - var leftExpr = expr.Left; - var lefType = leftExpr.Type; - var leftIsNullable = lefType.IsNullable(); - if (leftIsNullable) - { - leftNoValueLabel = il.DefineLabel(); - if (!TryEmit(leftExpr, paramExprs, il, ref closure, flags | ParentFlags.InstanceCall)) - return false; - - if (!closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, lefType); - - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Call, lefType.FindNullableHasValueGetterMethod()); - - il.Emit(OpCodes.Brfalse, leftNoValueLabel); - il.Emit(OpCodes.Call, lefType.FindNullableGetValueOrDefaultMethod()); - } - else if (!TryEmit(leftExpr, paramExprs, il, ref closure, flags)) - return false; - - var rightNoValueLabel = default(Label); - var rightExpr = expr.Right; - var rightType = rightExpr.Type; - var rightIsNullable = rightType.IsNullable(); - if (rightIsNullable) - { - rightNoValueLabel = il.DefineLabel(); - if (!TryEmit(rightExpr, paramExprs, il, ref closure, flags | ParentFlags.InstanceCall)) - return false; - - if (!closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, rightType); - - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Call, rightType.FindNullableHasValueGetterMethod()); - il.Emit(OpCodes.Brfalse, rightNoValueLabel); - il.Emit(OpCodes.Call, rightType.FindNullableGetValueOrDefaultMethod()); - } - else if (!TryEmit(rightExpr, paramExprs, il, ref closure, flags)) - return false; - - var exprType = expr.Type; - if (!TryEmitArithmeticOperation(expr, exprNodeType, exprType, il)) - return false; - - if (leftIsNullable || rightIsNullable) - { - var valueLabel = il.DefineLabel(); - il.Emit(OpCodes.Br, valueLabel); - - if (rightIsNullable) - il.MarkLabel(rightNoValueLabel); - il.Emit(OpCodes.Pop); - - if (leftIsNullable) - il.MarkLabel(leftNoValueLabel); - il.Emit(OpCodes.Pop); - - if (exprType.IsNullable()) - { - var endL = il.DefineLabel(); - var locIndex = InitValueTypeVariable(il, exprType); - EmitLoadLocalVariable(il, locIndex); - il.Emit(OpCodes.Br_S, endL); - il.MarkLabel(valueLabel); - il.Emit(OpCodes.Newobj, exprType.GetTypeInfo().DeclaredConstructors.GetFirst()); - il.MarkLabel(endL); - } - else - { - il.Emit(OpCodes.Ldc_I4_0); - il.MarkLabel(valueLabel); - } - } - - return true; - } - - private static bool TryEmitArithmeticOperation(BinaryExpression expr, - ExpressionType exprNodeType, Type exprType, ILGenerator il) - { - if (!exprType.IsPrimitive()) - { - if (exprType.IsNullable()) - exprType = Nullable.GetUnderlyingType(exprType); - - if (!exprType.IsPrimitive()) - { - MethodInfo method = null; - if (exprType == typeof(string)) - { - var paraType = typeof(string); - if (expr.Left.Type != expr.Right.Type || expr.Left.Type != typeof(string)) - paraType = typeof(object); - - var methods = typeof(string).GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsStatic && m.Name == "Concat" && - m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == paraType) - { - method = m; - break; - } - } - } - else - { - var methodName - = exprNodeType == ExpressionType.Add ? "op_Addition" - : exprNodeType == ExpressionType.AddChecked ? "op_Addition" - : exprNodeType == ExpressionType.Subtract ? "op_Subtraction" - : exprNodeType == ExpressionType.SubtractChecked ? "op_Subtraction" - : exprNodeType == ExpressionType.Multiply ? "op_Multiply" - : exprNodeType == ExpressionType.MultiplyChecked ? "op_Multiply" - : exprNodeType == ExpressionType.Divide ? "op_Division" - : exprNodeType == ExpressionType.Modulo ? "op_Modulus" - : null; - - if (methodName != null) - { - var methods = exprType.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; method == null && i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && m.IsStatic && m.Name == methodName) - method = m; - } - } - } - - if (method == null) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - return true; - } - } - - switch (exprNodeType) - { - case ExpressionType.Add: - case ExpressionType.AddAssign: - il.Emit(OpCodes.Add); - return true; - - case ExpressionType.AddChecked: - case ExpressionType.AddAssignChecked: - il.Emit(exprType.IsUnsigned() ? OpCodes.Add_Ovf_Un : OpCodes.Add_Ovf); - return true; - - case ExpressionType.Subtract: - case ExpressionType.SubtractAssign: - il.Emit(OpCodes.Sub); - return true; - - case ExpressionType.SubtractChecked: - case ExpressionType.SubtractAssignChecked: - il.Emit(exprType.IsUnsigned() ? OpCodes.Sub_Ovf_Un : OpCodes.Sub_Ovf); - return true; - - case ExpressionType.Multiply: - case ExpressionType.MultiplyAssign: - il.Emit(OpCodes.Mul); - return true; - - case ExpressionType.MultiplyChecked: - case ExpressionType.MultiplyAssignChecked: - il.Emit(exprType.IsUnsigned() ? OpCodes.Mul_Ovf_Un : OpCodes.Mul_Ovf); - return true; - - case ExpressionType.Divide: - case ExpressionType.DivideAssign: - il.Emit(OpCodes.Div); - return true; - - case ExpressionType.Modulo: - case ExpressionType.ModuloAssign: - il.Emit(OpCodes.Rem); - return true; - - case ExpressionType.And: - case ExpressionType.AndAssign: - il.Emit(OpCodes.And); - return true; - - case ExpressionType.Or: - case ExpressionType.OrAssign: - il.Emit(OpCodes.Or); - return true; - - case ExpressionType.ExclusiveOr: - case ExpressionType.ExclusiveOrAssign: - il.Emit(OpCodes.Xor); - return true; - - case ExpressionType.LeftShift: - case ExpressionType.LeftShiftAssign: - il.Emit(OpCodes.Shl); - return true; - - case ExpressionType.RightShift: - case ExpressionType.RightShiftAssign: - il.Emit(OpCodes.Shr); - return true; - - case ExpressionType.Power: - il.Emit(OpCodes.Call, typeof(Math).FindMethod("Pow")); - return true; - } - - return false; - } - - private static bool TryEmitLogicalOperator(BinaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - if (!TryEmit(expr.Left, paramExprs, il, ref closure, parent)) - return false; - - var labelSkipRight = il.DefineLabel(); - il.Emit(expr.NodeType == ExpressionType.AndAlso ? OpCodes.Brfalse : OpCodes.Brtrue, labelSkipRight); - - if (!TryEmit(expr.Right, paramExprs, il, ref closure, parent)) - return false; - - var labelDone = il.DefineLabel(); - il.Emit(OpCodes.Br, labelDone); - - il.MarkLabel(labelSkipRight); // label the second branch - il.Emit(expr.NodeType == ExpressionType.AndAlso ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1); - il.MarkLabel(labelDone); - - return true; - } - - private static bool TryEmitConditional(ConditionalExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var testExpr = TryReduceCondition(expr.Test); - - // detect a special simplistic case of comparison with `null` - var comparedWithNull = false; - if (testExpr is BinaryExpression b) - { - if (b.NodeType == ExpressionType.Equal || b.NodeType == ExpressionType.NotEqual || - !b.Left.Type.IsNullable() && !b.Right.Type.IsNullable()) - { - if (b.Right is ConstantExpression r && r.Value == null) - { - // the null comparison for nullable is actually a `nullable.HasValue` check, - // which implies member access on nullable struct - therefore loading it by address - if (b.Left.Type.IsNullable()) - parent |= ParentFlags.MemberAccess; - comparedWithNull = TryEmit(b.Left, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult); - } - else if (b.Left is ConstantExpression l && l.Value == null) - { - // the null comparison for nullable is actually a `nullable.HasValue` check, - // which implies member access on nullable struct - therefore loading it by address - if (b.Right.Type.IsNullable()) - parent |= ParentFlags.MemberAccess; - comparedWithNull = TryEmit(b.Right, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult); - } - } - } - - if (!comparedWithNull) - { - if (!TryEmit(testExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - } - - var labelIfFalse = il.DefineLabel(); - il.Emit(comparedWithNull && testExpr.NodeType == ExpressionType.Equal ? OpCodes.Brtrue : OpCodes.Brfalse, labelIfFalse); - - var ifTrueExpr = expr.IfTrue; - if (!TryEmit(ifTrueExpr, paramExprs, il, ref closure, parent & ParentFlags.IgnoreResult)) - return false; - - var ifFalseExpr = expr.IfFalse; - if (ifFalseExpr.NodeType == ExpressionType.Default && ifFalseExpr.Type == typeof(void)) - { - il.MarkLabel(labelIfFalse); - return true; - } - - var labelDone = il.DefineLabel(); - il.Emit(OpCodes.Br, labelDone); - - il.MarkLabel(labelIfFalse); - if (!TryEmit(ifFalseExpr, paramExprs, il, ref closure, parent & ParentFlags.IgnoreResult)) - return false; - - il.MarkLabel(labelDone); - return true; - } - - private static Expression TryReduceCondition(Expression testExpr) - { - if (testExpr is BinaryExpression b) - { - if (b.NodeType == ExpressionType.OrElse || b.NodeType == ExpressionType.Or) - { - if (b.Left is ConstantExpression l && l.Value is bool lb) - return lb ? b.Left : TryReduceCondition(b.Right); - - if (b.Right is ConstantExpression r && r.Value is bool rb && rb == false) - return TryReduceCondition(b.Left); - } - else if (b.NodeType == ExpressionType.AndAlso || b.NodeType == ExpressionType.And) - { - if (b.Left is ConstantExpression l && l.Value is bool lb) - return !lb ? b.Left : TryReduceCondition(b.Right); - - if (b.Right is ConstantExpression r && r.Value is bool rb && rb) - return TryReduceCondition(b.Left); - } - } - - return testExpr; - } - - private static bool EmitMethodCall(ILGenerator il, MethodInfo method, ParentFlags parent = ParentFlags.Empty) - { - if (method == null) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - - if ((parent & ParentFlags.IgnoreResult) != 0 && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - return true; - } - - private static void EmitLoadConstantInt(ILGenerator il, int i) - { - switch (i) - { - case -1: - il.Emit(OpCodes.Ldc_I4_M1); - break; - case 0: - il.Emit(OpCodes.Ldc_I4_0); - break; - case 1: - il.Emit(OpCodes.Ldc_I4_1); - break; - case 2: - il.Emit(OpCodes.Ldc_I4_2); - break; - case 3: - il.Emit(OpCodes.Ldc_I4_3); - break; - case 4: - il.Emit(OpCodes.Ldc_I4_4); - break; - case 5: - il.Emit(OpCodes.Ldc_I4_5); - break; - case 6: - il.Emit(OpCodes.Ldc_I4_6); - break; - case 7: - il.Emit(OpCodes.Ldc_I4_7); - break; - case 8: - il.Emit(OpCodes.Ldc_I4_8); - break; - default: - if (i > -129 && i < 128) - il.Emit(OpCodes.Ldc_I4_S, (sbyte)i); - else - il.Emit(OpCodes.Ldc_I4, i); - break; - } - } - - private static void EmitLoadLocalVariableAddress(ILGenerator il, int location) - { - if (location < 256) - il.Emit(OpCodes.Ldloca_S, (byte)location); - else - il.Emit(OpCodes.Ldloca, location); - } - - private static void EmitLoadLocalVariable(ILGenerator il, int location) - { - if (location == 0) - il.Emit(OpCodes.Ldloc_0); - else if (location == 1) - il.Emit(OpCodes.Ldloc_1); - else if (location == 2) - il.Emit(OpCodes.Ldloc_2); - else if (location == 3) - il.Emit(OpCodes.Ldloc_3); - else if (location < 256) - il.Emit(OpCodes.Ldloc_S, (byte)location); - else - il.Emit(OpCodes.Ldloc, location); - } - - private static void EmitStoreLocalVariable(ILGenerator il, int location) - { - if (location == 0) - il.Emit(OpCodes.Stloc_0); - else if (location == 1) - il.Emit(OpCodes.Stloc_1); - else if (location == 2) - il.Emit(OpCodes.Stloc_2); - else if (location == 3) - il.Emit(OpCodes.Stloc_3); - else if (location < 256) - il.Emit(OpCodes.Stloc_S, (byte)location); - else - il.Emit(OpCodes.Stloc, location); - } - - private static int EmitStoreLocalVariableAndLoadItsAddress(ILGenerator il, Type type) - { - var varIndex = il.GetNextLocalVarIndex(type); - if (varIndex == 0) - { - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloca_S, (byte)0); - } - else if (varIndex == 1) - { - il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Ldloca_S, (byte)1); - } - else if (varIndex == 2) - { - il.Emit(OpCodes.Stloc_2); - il.Emit(OpCodes.Ldloca_S, (byte)2); - } - else if (varIndex == 3) - { - il.Emit(OpCodes.Stloc_3); - il.Emit(OpCodes.Ldloca_S, (byte)3); - } - else if (varIndex < 256) - { - il.Emit(OpCodes.Stloc_S, (byte)varIndex); - il.Emit(OpCodes.Ldloca_S, (byte)varIndex); - } - else - { - il.Emit(OpCodes.Stloc, varIndex); - il.Emit(OpCodes.Ldloca, varIndex); - } - - return varIndex; - } - } - } - - // Helpers targeting the performance. Extensions method names may be a bit funny (non standard), - // in order to prevent conflicts with YOUR helpers with standard names - internal static class Tools - { - internal static bool IsValueType(this Type type) => type.GetTypeInfo().IsValueType; - internal static bool IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive; - internal static bool IsClass(this Type type) => type.GetTypeInfo().IsClass; - - internal static bool IsUnsigned(this Type type) => - type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong); - - internal static bool IsNullable(this Type type) => - type.GetTypeInfo().IsGenericType && type.GetTypeInfo().GetGenericTypeDefinition() == typeof(Nullable<>); - - internal static MethodInfo FindMethod(this Type type, string methodName) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - if (methods[i].Name == methodName) - return methods[i]; - - return type.GetTypeInfo().BaseType?.FindMethod(methodName); - } - - internal static MethodInfo DelegateTargetGetterMethod = typeof(Delegate).FindPropertyGetMethod("Target"); - - internal static MethodInfo FindDelegateInvokeMethod(this Type type) => type.FindMethod("Invoke"); - - internal static MethodInfo FindNullableGetValueOrDefaultMethod(this Type type) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.GetParameters().Length == 0 && m.Name == "GetValueOrDefault") - return m; - } - - return null; - } - - internal static MethodInfo FindValueGetterMethod(this Type type) => - type.FindPropertyGetMethod("Value"); - - internal static MethodInfo FindNullableHasValueGetterMethod(this Type type) => - type.FindPropertyGetMethod("HasValue"); - - internal static MethodInfo FindPropertyGetMethod(this Type propHolderType, string propName) - { - var methods = propHolderType.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var method = methods[i]; - if (method.IsSpecialName) - { - var methodName = method.Name; - if (methodName.Length == propName.Length + 4 && methodName[0] == 'g' && methodName[3] == '_') - { - var j = propName.Length - 1; - while (j != -1 && propName[j] == methodName[j + 4]) --j; - if (j == -1) - return method; - } - } - } - - return propHolderType.GetTypeInfo().BaseType?.FindPropertyGetMethod(propName); - } - - internal static MethodInfo FindPropertySetMethod(this Type propHolderType, string propName) - { - var methods = propHolderType.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var method = methods[i]; - if (method.IsSpecialName) - { - var methodName = method.Name; - if (methodName.Length == propName.Length + 4 && methodName[0] == 's' && methodName[3] == '_') - { - var j = propName.Length - 1; - while (j != -1 && propName[j] == methodName[j + 4]) --j; - if (j == -1) - return method; - } - } - } - - return propHolderType.GetTypeInfo().BaseType?.FindPropertySetMethod(propName); - } - - internal static MethodInfo FindConvertOperator(this Type type, Type sourceType, Type targetType) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsStatic && m.IsSpecialName && m.ReturnType == targetType) - { - var n = m.Name; - // n == "op_Implicit" || n == "op_Explicit" - if (n.Length == 11 && - n[2] == '_' && n[5] == 'p' && n[6] == 'l' && n[7] == 'i' && n[8] == 'c' && n[9] == 'i' && n[10] == 't' && - m.GetParameters()[0].ParameterType == sourceType) - return m; - } - } - - return null; - } - - internal static ConstructorInfo FindSingleParamConstructor(this Type type, Type paramType) - { - var ctors = type.GetTypeInfo().DeclaredConstructors.AsArray(); - for (var i = 0; i < ctors.Length; i++) - { - var ctor = ctors[i]; - var parameters = ctor.GetParameters(); - if (parameters.Length == 1 && parameters[0].ParameterType == paramType) - return ctor; - } - - return null; - } - - public static T[] AsArray(this IEnumerable xs) - { - if (xs is T[] array) - return array; - return xs == null ? null : xs.ToArray(); - } - - private static class EmptyArray - { - public static readonly T[] Value = new T[0]; - } - - public static T[] Empty() => EmptyArray.Value; - - public static T[] WithLast(this T[] source, T value) - { - if (source == null || source.Length == 0) - return new[] { value }; - if (source.Length == 1) - return new[] { source[0], value }; - if (source.Length == 2) - return new[] { source[0], source[1], value }; - var sourceLength = source.Length; - var result = new T[sourceLength + 1]; - Array.Copy(source, 0, result, 0, sourceLength); - result[sourceLength] = value; - return result; - } - - public static Type[] GetParamTypes(IReadOnlyList paramExprs) - { - if (paramExprs == null || paramExprs.Count == 0) - return Empty(); - - if (paramExprs.Count == 1) - return new[] { paramExprs[0].IsByRef ? paramExprs[0].Type.MakeByRefType() : paramExprs[0].Type }; - - var paramTypes = new Type[paramExprs.Count]; - for (var i = 0; i < paramTypes.Length; i++) - { - var parameterExpr = paramExprs[i]; - paramTypes[i] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type; - } - - return paramTypes; - } - - public static Type GetFuncOrActionType(Type[] paramTypes, Type returnType) - { - if (returnType == typeof(void)) - { - switch (paramTypes.Length) - { - case 0: return typeof(Action); - case 1: return typeof(Action<>).MakeGenericType(paramTypes); - case 2: return typeof(Action<,>).MakeGenericType(paramTypes); - case 3: return typeof(Action<,,>).MakeGenericType(paramTypes); - case 4: return typeof(Action<,,,>).MakeGenericType(paramTypes); - case 5: return typeof(Action<,,,,>).MakeGenericType(paramTypes); - case 6: return typeof(Action<,,,,,>).MakeGenericType(paramTypes); - case 7: return typeof(Action<,,,,,,>).MakeGenericType(paramTypes); - default: - throw new NotSupportedException( - $"Action with so many ({paramTypes.Length}) parameters is not supported!"); - } - } - - switch (paramTypes.Length) - { - case 0: return typeof(Func<>).MakeGenericType(returnType); - case 1: return typeof(Func<,>).MakeGenericType(paramTypes[0], returnType); - case 2: return typeof(Func<,,>).MakeGenericType(paramTypes[0], paramTypes[1], returnType); - case 3: return typeof(Func<,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], returnType); - case 4: return typeof(Func<,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], returnType); - case 5: return typeof(Func<,,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], paramTypes[4], returnType); - case 6: return typeof(Func<,,,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], paramTypes[4], paramTypes[5], returnType); - case 7: return typeof(Func<,,,,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], paramTypes[4], paramTypes[5], paramTypes[6], returnType); - default: - throw new NotSupportedException( - $"Func with so many ({paramTypes.Length}) parameters is not supported!"); - } - } - - public static T GetFirst(this IEnumerable source) - { - // This is pretty much Linq.FirstOrDefault except it does not need to check - // if source is IPartition (but should it?) - - if (source is IList list) - return list.Count == 0 ? default : list[0]; - using (var items = source.GetEnumerator()) - return items.MoveNext() ? items.Current : default; - } - - public static T GetFirst(this IList source) - { - return source.Count == 0 ? default : source[0]; - } - - public static T GetFirst(this T[] source) - { - return source.Length == 0 ? default : source[0]; - } - } - - /// Hey - public static class ILGeneratorHacks - { - /* - // Original methods from ILGenerator.cs - - public virtual LocalBuilder DeclareLocal(Type localType) - { - return this.DeclareLocal(localType, false); - } - - public virtual LocalBuilder DeclareLocal(Type localType, bool pinned) - { - MethodBuilder methodBuilder = this.m_methodBuilder as MethodBuilder; - if ((MethodInfo)methodBuilder == (MethodInfo)null) - throw new NotSupportedException(); - if (methodBuilder.IsTypeCreated()) - throw new InvalidOperationException(SR.InvalidOperation_TypeHasBeenCreated); - if (localType == (Type)null) - throw new ArgumentNullException(nameof(localType)); - if (methodBuilder.m_bIsBaked) - throw new InvalidOperationException(SR.InvalidOperation_MethodBaked); - this.m_localSignature.AddArgument(localType, pinned); - LocalBuilder localBuilder = new LocalBuilder(this.m_localCount, localType, (MethodInfo)methodBuilder, pinned); - ++this.m_localCount; - return localBuilder; - } - */ - - /// Not allocating the LocalBuilder class - /// emitting this: - /// il.m_localSignature.AddArgument(type); - /// return PostInc(ref il.LocalCount); - public static Func CompileGetNextLocalVarIndex() - { - if (LocalCountField == null || LocalSignatureField == null || AddArgumentMethod == null) - return (i, t) => i.DeclareLocal(t).LocalIndex; - - var method = new DynamicMethod(string.Empty, - typeof(int), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(ILGenerator), typeof(Type) }, - typeof(ExpressionCompiler.ArrayClosure), skipVisibility: true); - - var il = method.GetILGenerator(); - - il.Emit(OpCodes.Ldarg_1); // load `il` argument - il.Emit(OpCodes.Ldfld, LocalSignatureField); - il.Emit(OpCodes.Ldarg_2); // load `type` argument - il.Emit(OpCodes.Ldc_I4_0); // load `pinned: false` argument - il.Emit(OpCodes.Call, AddArgumentMethod); - - il.Emit(OpCodes.Ldarg_1); // load `il` argument - il.Emit(OpCodes.Ldflda, LocalCountField); - il.Emit(OpCodes.Call, PostIncMethod); - - il.Emit(OpCodes.Ret); - - return (Func)method.CreateDelegate(typeof(Func), ExpressionCompiler.EmptyArrayClosure); - } - - internal static int PostInc(ref int i) => i++; - - public static readonly MethodInfo PostIncMethod = typeof(ILGeneratorHacks).GetTypeInfo() - .GetDeclaredMethod(nameof(PostInc)); - - /// Get via reflection - public static readonly FieldInfo LocalSignatureField = typeof(ILGenerator).GetTypeInfo() - .GetDeclaredField("m_localSignature"); - - /// Get via reflection - public static readonly FieldInfo LocalCountField = typeof(ILGenerator).GetTypeInfo() - .GetDeclaredField("m_localCount"); - - /// Get via reflection - public static readonly MethodInfo AddArgumentMethod = typeof(SignatureHelper).GetTypeInfo() - .GetDeclaredMethods(nameof(SignatureHelper.AddArgument)).First(m => m.GetParameters().Length == 2); - - private static readonly Func _getNextLocalVarIndex = CompileGetNextLocalVarIndex(); - - /// Does the job - public static int GetNextLocalVarIndex(this ILGenerator il, Type t) => _getNextLocalVarIndex(il, t); - } - - internal struct LiveCountArray - { - public int Count; - public T[] Items; - - public LiveCountArray(T[] items) - { - Items = items; - Count = items.Length; - } - - public ref T PushSlot() - { - if (++Count > Items.Length) - Items = Expand(Items); - return ref Items[Count - 1]; - } - - public void PushSlot(T item) - { - if (++Count > Items.Length) - Items = Expand(Items); - Items[Count - 1] = item; - } - - public void Pop() => --Count; - - private static T[] Expand(T[] items) - { - if (items.Length == 0) - return new T[4]; - - var count = items.Length; - var newItems = new T[count << 1]; // count x 2 - Array.Copy(items, 0, newItems, 0, count); - return newItems; - } - } -} -#endif diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/ImTools.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/ImTools.cs deleted file mode 100644 index 0775e3b..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/DryIoc/ImTools.cs +++ /dev/null @@ -1,6198 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2016-2020 Maksim Volkau - -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 -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. -*/ - -// ReSharper disable once InconsistentNaming - -#if !NET35 && !PCL -#define SUPPORTS_SPIN_WAIT -#endif - -namespace ImTools -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading; - using System.Diagnostics; - using System.Runtime.CompilerServices; // For [MethodImpl(AggressiveInlining)] - - /// Helpers for functional composition - public static class Fun - { - /// - /// Always a true condition. - /// - public static bool Always(T _) => true; - - /// - /// Identity function returning passed argument as result. - /// - public static T Id(T x) => x; - - /// - /// Forward pipe operator (`|>` in F#) - /// - public static R To(this T x, Func map) => map(x); - - /// - /// Forward pipe operator (`|>` in F#) with the additional state A for two arguments function - /// - public static R To(this T x, S state, Func map) => map(x, state); - - /// - /// Cast to the R type with the forward pipe operator (`|>` in F#) - /// - public static R To(this object x) => (R)x; - - /// - /// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value - /// - public static T Do(this T x, Action effect) - { - effect(x); - return x; - } - - /// - /// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value and the state object - /// - public static T Do(this T x, S state, Action effect) - { - effect(x, state); - return x; - } - - /// - /// Lifts argument to Func without allocations ignoring the first argument. - /// For example if you have `Func{T, R} = _ => instance`, - /// you may rewrite it without allocations as `instance.ToFunc{A, R}` - /// - public static R ToFunc(this R result, T ignoredArg) => result; - } - - /// Helpers for lazy instantiations - public static class Lazy - { - /// Provides result type inference for creation of lazy. - public static Lazy Of(Func valueFactory) => new Lazy(valueFactory); - } - - /// Replacement for `Void` type which can be used as a type argument and value. - /// In traditional functional languages this type is a singleton empty record type, - /// e.g. `()` in Haskell https://en.wikipedia.org/wiki/Unit_type - public struct Unit : IEquatable - { - /// Singleton unit value - making it a lower-case so you could import `using static ImTools.Unit;` and write `return unit;` - public static readonly Unit unit = new Unit(); - - /// - public override string ToString() => "(unit)"; - - /// Equals to any other Unit - public bool Equals(Unit other) => true; - - /// - public override bool Equals(object obj) => obj is Unit; - - /// Using type hash code for the value - public override int GetHashCode() => typeof(Unit).GetHashCode(); - } - - /// Simple value provider interface - useful for the type pattern matching via `case I{T} x: ...` - public interface I - { - /// The value in this case ;) - T Value { get; } - } - - /// Helpers for `Is` and `Union` - public static class UnionTools - { - /// Pretty prints the Union using the type information - internal static string ToString(T value, string prefix = "case(", string suffix = ")") - { - if (typeof(TName) == typeof(Unit)) - return prefix + value + suffix; - - var typeName = typeof(TName).Name; - var i = typeName.IndexOf('`'); - var name = i == -1 ? typeName : typeName.Substring(0, i); - return name + prefix + value + suffix; - } - } - - /// Wraps the `T` in a typed `TData` struct value in a one-line declaration, - /// so the ]]> - /// is different from the ]]> - public abstract class Item where TItem : Item - { - /// Creation method for the consistency with other types - public static item Of(T x) => new item(x); - - /// Nested structure that hosts a value. - /// All nested types by convention here are lowercase - public readonly struct item : IEquatable, I - { - /// - public T Value { [MethodImpl((MethodImplOptions)256)] get => Item; } - - /// The value - public readonly T Item; - - /// Constructor - public item(T x) => Item = x; - - /// - public bool Equals(item other) => EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is item c && Equals(c); - - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value); - } - } - - /// Item without the data payload - public abstract class Item where TItem : Item - { - /// Single item value - public static readonly item Single = new item(); - - /// Nested structure that hosts a value. - /// All nested types by convention here are lowercase - public readonly struct item : IEquatable - { - /// - public bool Equals(item other) => true; - - /// - public override bool Equals(object obj) => obj is item; - - /// - public override int GetHashCode() => typeof(TItem).GetHashCode(); - - /// - public override string ToString() => "(" + typeof(TItem).Name + ")"; - } - } - - /// Wraps the `T` in a named `TBox` class in a one-line declaration, - /// so the ]]> - /// is different from the ]]> - public abstract class Box : I, IEquatable> - where TBox : Box, new() - { - /// Wraps the value - public static TBox Of(T x) => new TBox { Value = x }; - - /// - public T Value { get; private set; } - - /// - public bool Equals(Box other) => - other != null && EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is Box c && Equals(c); - - // ReSharper disable once NonReadonlyMemberInGetHashCode - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value, "data("); - } - - /// Unnamed discriminated union (with Empty name), shorter name for simplified inline usage - public class U : Union { } - - /// Discriminated union - public abstract class Union - { - /// To tag the cases with enum value for efficient pattern matching of required - - /// otherwise we need to use `is CaseN` pattern or similar which is less efficient - public enum Tag : byte - { - /// Tags Case1 - Case1, - /// Tags Case2 - Case2 - } - - /// The base interface for the cases to operate. - /// The naming is selected to start from the lower letter, cause we need to use the nested type. - /// It is an unusual case, that's why using the __union__ will be fine to highlight this. - // ReSharper disable once InconsistentNaming - public interface union - { - /// The tag - Tag Tag { get; } - - /// Matches the union cases to the R value - R Match(Func map1, Func map2); - } - - /// Creates the respective case - public static union Of(T1 x) => new case1(x); - - /// Creates the respective case - public static union Of(T2 x) => new case2(x); - - /// Wraps the respective case - public readonly struct case1 : union, IEquatable, I - { - /// Implicit conversion - public static implicit operator case1(T1 x) => new case1(x); - - /// - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - /// - public R Match(Func map1, Func map2) => map1(Case); - - /// - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - /// The case value - public readonly T1 Case; - - /// Wraps the value - public case1(T1 x) => Case = x; - - /// - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is case1 x && Equals(x); - - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value); - } - - /// Wraps the respective case - public readonly struct case2 : union, IEquatable, I - { - /// Conversion - public static implicit operator case2(T2 x) => new case2(x); - - /// - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - - /// - public R Match(Func map1, Func map2) => map2(Value); - - /// - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - /// The case value - public readonly T2 Case; - - /// Wraps the value - public case2(T2 x) => Case = x; - - /// - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is case2 x && Equals(x); - - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value); - } - } - -#pragma warning disable 1591 - public class U : Union { } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - public static union Of(T8 x) => new case8(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case8 : union, IEquatable, I - { - public static implicit operator case8(T8 x) => new case8(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map8(Case); - - public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T8 Case; - public case8(T8 x) => Case = x; - - public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case8 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8, Case9 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - public static union Of(T8 x) => new case8(x); - public static union Of(T9 x) => new case9(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case8 : union, IEquatable, I - { - public static implicit operator case8(T8 x) => new case8(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map8(Case); - - public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T8 Case; - public case8(T8 x) => Case = x; - - public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case8 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case9 : union, IEquatable, I - { - public static implicit operator case9(T9 x) => new case9(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case9; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map9(Case); - - public T9 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T9 Case; - public case9(T9 x) => Case = x; - - public bool Equals(case9 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case9 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8, Case9, Case10 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - public static union Of(T8 x) => new case8(x); - public static union Of(T9 x) => new case9(x); - public static union Of(T10 x) => new case10(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case8 : union, IEquatable, I - { - public static implicit operator case8(T8 x) => new case8(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map8(Case); - - public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T8 Case; - public case8(T8 x) => Case = x; - - public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case8 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case9 : union, IEquatable, I - { - public static implicit operator case9(T9 x) => new case9(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case9; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map9(Case); - - public T9 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T9 Case; - public case9(T9 x) => Case = x; - - public bool Equals(case9 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case9 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case10 : union, IEquatable, I - { - public static implicit operator case10(T10 x) => new case10(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case10; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map10(Case); - - public T10 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T10 Case; - public case10(T10 x) => Case = x; - - public bool Equals(case10 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case10 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - -#pragma warning restore 1591 - - /// Methods to work with immutable arrays and some sugar. - public static class ArrayTools - { - private static class EmptyArray - { - public static readonly T[] Value = new T[0]; - } - - /// Returns singleton empty array of provided type. - /// Array item type. Empty array. - public static T[] Empty() => EmptyArray.Value; - - /// Wraps item in array. - public static T[] One(this T one) => new[] { one }; - - /// Returns true if array is null or have no items. Type of array item. - /// Source array to check. True if null or has no items, false otherwise. - public static bool IsNullOrEmpty(this T[] source) => source == null || source.Length == 0; - - /// Returns empty array instead of null, or source array otherwise. Type of array item. - public static T[] EmptyIfNull(this T[] source) => source ?? Empty(); - - /// Returns source enumerable if it is array, otherwise converts source to array or an empty array if null. - public static T[] ToArrayOrSelf(this IEnumerable source) => - source == null ? Empty() : (source as T[] ?? source.ToArray()); - - /// Returns source enumerable if it is list, otherwise converts source to IList or an empty array if null. - public static IList ToListOrSelf(this IEnumerable source) => - source == null ? Empty() : source as IList ?? source.ToList(); - - /// - /// Array copy - /// - public static T[] Copy(this T[] items) - { - if (items == null) - return null; - var copy = new T[items.Length]; - for (var i = 0; i < copy.Length; i++) - copy[i] = items[i]; - return copy; - } - - /// Returns new array consisting from all items from source array then all items from added array. - /// If source is null or empty, then added array will be returned. - /// If added is null or empty, then source will be returned. - /// Array item type. - /// Array with leading items. - /// Array with following items. - /// New array with items of source and added arrays. - public static T[] Append(this T[] source, params T[] added) - { - if (added == null || added.Length == 0) - return source; - if (source == null || source.Length == 0) - return added; - - var result = new T[source.Length + added.Length]; - Array.Copy(source, 0, result, 0, source.Length); - if (added.Length == 1) - result[source.Length] = added[0]; - else - Array.Copy(added, 0, result, source.Length, added.Length); - return result; - } - - /// Performant concat of enumerables in case of arrays. - /// But performance will degrade if you use Concat().Where(). - /// Type of item. - /// goes first. - /// appended to source. - /// empty array or concat of source and other. - public static T[] Append(this IEnumerable source, IEnumerable other) => - source.ToArrayOrSelf().Append(other.ToArrayOrSelf()); - - /// Returns new array with appended, - /// or at , if specified. - /// If source array could be null or empty, then single value item array will be created despite any index. - /// Array item type. - /// Array to append value to. - /// Value to append. - /// (optional) Index of value to update. - /// New array with appended or updated value. - public static T[] AppendOrUpdate(this T[] source, T value, int index = -1) - { - if (source == null || source.Length == 0) - return new[] { value }; - var sourceLength = source.Length; - index = index < 0 ? sourceLength : index; - var result = new T[index < sourceLength ? sourceLength : sourceLength + 1]; - Array.Copy(source, result, sourceLength); - result[index] = value; - return result; - } - - /// Calls predicate on each item in array until predicate returns true, - /// then method will return this item index, or if predicate returns false for each item, method will return -1. - /// Type of array items. - /// Source array: if null or empty, then method will return -1. - /// Delegate to evaluate on each array item until delegate returns true. - /// Index of item for which predicate returns true, or -1 otherwise. - public static int IndexOf(this T[] source, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (predicate(source[i])) - return i; - return -1; - } - - /// Minimizes the allocations for closure in predicate lambda with the provided - public static int IndexOf(this T[] source, S state, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (predicate(state, source[i])) - return i; - return -1; - } - - /// Looks up for item in source array equal to provided value, and returns its index, or -1 if not found. - /// Type of array items. - /// Source array: if null or empty, then method will return -1. - /// Value to look up. - /// Index of item equal to value, or -1 item is not found. - public static int IndexOf(this T[] source, T value) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (Equals(source[i], value)) - return i; - - return -1; - } - - /// The same as `IndexOf` but searching the item by reference - public static int IndexOfReference(this T[] source, T reference) where T : class - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (ReferenceEquals(source[i], reference)) - return i; - - return -1; - } - - /// Produces new array without item at specified . - /// Will return array if index is out of bounds, or source is null/empty. - /// Type of array item. - /// Input array. Index if item to remove. - /// New array with removed item at index, or input source array if index is not in array. - public static T[] RemoveAt(this T[] source, int index) - { - if (source == null || source.Length == 0 || index < 0 || index >= source.Length) - return source; - if (index == 0 && source.Length == 1) - return new T[0]; - var result = new T[source.Length - 1]; - if (index != 0) - Array.Copy(source, 0, result, 0, index); - if (index != result.Length) - Array.Copy(source, index + 1, result, index, result.Length - index); - return result; - } - - /// Looks for item in array using equality comparison, and returns new array with found item remove, or original array if not item found. - /// Type of array item. - /// Input array. Value to find and remove. - /// New array with value removed or original array if value is not found. - public static T[] Remove(this T[] source, T value) => - source.RemoveAt(source.IndexOf(value)); - - /// Returns first item matching the , or default item value. - /// item type - /// items collection to search - /// condition to evaluate for each item. - /// First item matching condition or default value. - public static T FindFirst(this T[] source, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - { - var item = source[i]; - if (predicate(item)) - return item; - } - - return default(T); - } - - /// Version of FindFirst with the fixed state used by predicate to prevent allocations by predicate lambda closure - public static T FindFirst(this T[] source, S state, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - { - var item = source[i]; - if (predicate(state, item)) - return item; - } - - return default(T); - } - - /// Returns first item matching the , or default item value. - /// item type - /// items collection to search - /// condition to evaluate for each item. - /// First item matching condition or default value. - public static T FindFirst(this IEnumerable source, Func predicate) => - source is T[] sourceArr ? sourceArr.FindFirst(predicate) : source.FirstOrDefault(predicate); - - /// Returns element if collection consist on single element, otherwise returns default value. - /// It does not throw for collection with many elements - public static T SingleOrDefaultIfMany(this IEnumerable source) - { - if (source is IList list) - return list.Count == 1 ? list[0] : default(T); - - if (source == null) - return default(T); - - using (var e = source.GetEnumerator()) - { - if (!e.MoveNext()) - return default(T); - var it = e.Current; - return !e.MoveNext() ? it : default(T); - } - } - - /// Does for each item - public static void ForEach(this T[] source, Action action) - { - if (!source.IsNullOrEmpty()) - for (var i = 0; i < source.Length; i++) - action(source[i]); - } - - /// Appends source to results - public static T[] AppendTo(T[] source, int sourcePos, int count, T[] results = null) - { - if (results == null) - { - var newResults = new T[count]; - if (count == 1) - newResults[0] = source[sourcePos]; - else - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = source[j]; - return newResults; - } - - var matchCount = results.Length; - var appendedResults = new T[matchCount + count]; - if (matchCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, matchCount); - - if (count == 1) - appendedResults[matchCount] = source[sourcePos]; - else - Array.Copy(source, sourcePos, appendedResults, matchCount, count); - - return appendedResults; - } - - private static R[] AppendTo(T[] source, int sourcePos, int count, Func map, R[] results = null) - { - if (results == null || results.Length == 0) - { - var newResults = new R[count]; - if (count == 1) - newResults[0] = map(source[sourcePos]); - else - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = map(source[j]); - return newResults; - } - - var oldResultsCount = results.Length; - var appendedResults = new R[oldResultsCount + count]; - if (oldResultsCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, oldResultsCount); - - if (count == 1) - appendedResults[oldResultsCount] = map(source[sourcePos]); - else - { - for (int i = oldResultsCount, j = sourcePos; i < appendedResults.Length; ++i, ++j) - appendedResults[i] = map(source[j]); - } - - return appendedResults; - } - - private static R[] AppendTo(T[] source, S state, int sourcePos, int count, Func map, R[] results = null) - { - if (results == null || results.Length == 0) - { - var newResults = new R[count]; - if (count == 1) - newResults[0] = map(state, source[sourcePos]); - else - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = map(state, source[j]); - return newResults; - } - - var oldResultsCount = results.Length; - var appendedResults = new R[oldResultsCount + count]; - if (oldResultsCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, oldResultsCount); - - if (count == 1) - appendedResults[oldResultsCount] = map(state, source[sourcePos]); - else - { - for (int i = oldResultsCount, j = sourcePos; i < appendedResults.Length; ++i, ++j) - appendedResults[i] = map(state, source[j]); - } - - return appendedResults; - } - - /// Where method similar to Enumerable.Where but more performant and non necessary allocating. - /// It returns source array and does Not create new one if all items match the condition. - /// Type of source items. - /// If null, the null will be returned. - /// Condition to keep items. - /// New array if some items are filter out. Empty array if all items are filtered out. Original array otherwise. - public static T[] Match(this T[] source, Func condition) - { - if (source == null || source.Length == 0) - return source; - - if (source.Length == 1) - return condition(source[0]) ? source : Empty(); - - if (source.Length == 2) - { - var condition0 = condition(source[0]); - var condition1 = condition(source[1]); - return condition0 && condition1 ? new[] { source[0], source[1] } - : condition0 ? new[] { source[0] } - : condition1 ? new[] { source[1] } - : Empty(); - } - - var matchStart = 0; - T[] matches = null; - var matchFound = false; - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, matches); - - return matches ?? (matchStart != 0 ? Empty() : source); - } - - /// The same as `Match` but assumes that is not null and not empty - public static T[] MatchUnsafe(this T[] source, Func condition) - { - var matchStart = 0; - T[] matches = null; - var matchFound = false; - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, matches); - - return matches ?? (matchStart != 0 ? Empty() : source); - } - - /// Match with the additional state to use in to minimize the allocations in lambda closure - public static T[] Match(this T[] source, S state, Func condition) - { - if (source == null || source.Length == 0) - return source; - - if (source.Length == 1) - return condition(state, source[0]) ? source : Empty(); - - if (source.Length == 2) - { - var condition0 = condition(state, source[0]); - var condition1 = condition(state, source[1]); - return condition0 && condition1 ? new[] { source[0], source[1] } - : condition0 ? new[] { source[0] } - : condition1 ? new[] { source[1] } - : Empty(); - } - - var matchStart = 0; - T[] matches = null; - var matchFound = false; - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(state, source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, matches); - - return matches ?? (matchStart != 0 ? Empty() : source); - } - - /// Where method similar to Enumerable.Where but more performant and non necessary allocating. - /// It returns source array and does Not create new one if all items match the condition. - /// Type of source items. Type of result items. - /// If null, the null will be returned. - /// Condition to keep items. Converter from source to result item. - /// New array of result items. - public static R[] Match(this T[] source, Func condition, Func map) - { - if (source == null) - return null; - - if (source.Length == 0) - return Empty(); - - if (source.Length == 1) - { - var item = source[0]; - return condition(item) ? new[] { map(item) } : Empty(); - } - - if (source.Length == 2) - { - var condition0 = condition(source[0]); - var condition1 = condition(source[1]); - return condition0 && condition1 ? new[] { map(source[0]), map(source[1]) } - : condition0 ? new[] { map(source[0]) } - : condition1 ? new[] { map(source[1]) } - : Empty(); - } - - var matchStart = 0; - R[] matches = null; - var matchFound = false; - - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, map, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, map, matches); - - return matches ?? (matchStart == 0 ? AppendTo(source, 0, source.Length, map) : Empty()); - } - - /// Match with the additional state to use in and to minimize the allocations in lambda closure - public static R[] Match(this T[] source, S state, Func condition, Func map) - { - if (source == null) - return null; - - if (source.Length == 0) - return Empty(); - - if (source.Length == 1) - { - var item = source[0]; - return condition(state, item) ? new[] { map(state, item) } : Empty(); - } - - if (source.Length == 2) - { - var condition0 = condition(state, source[0]); - var condition1 = condition(state, source[1]); - return condition0 && condition1 ? new[] { map(state, source[0]), map(state, source[1]) } - : condition0 ? new[] { map(state, source[0]) } - : condition1 ? new[] { map(state, source[1]) } - : Empty(); - } - - var matchStart = 0; - R[] matches = null; - var matchFound = false; - - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(state, source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, state, matchStart, i - matchStart, map, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, state, matchStart, i - matchStart, map, matches); - - return matches ?? (matchStart == 0 ? AppendTo(source, state, 0, source.Length, map) : Empty()); - } - - /// Maps all items from source to result array. - /// Source item type Result item type - /// Source items Function to convert item from source to result. - /// Converted items - public static R[] Map(this T[] source, Func map) - { - if (source == null) - return null; - - var sourceCount = source.Length; - if (sourceCount == 0) - return Empty(); - - if (sourceCount == 1) - return new[] { map(source[0]) }; - - if (sourceCount == 2) - return new[] { map(source[0]), map(source[1]) }; - - var results = new R[sourceCount]; - for (var i = 0; i < source.Length; i++) - results[i] = map(source[i]); - return results; - } - - /// Map with additional state to use in to minimize allocations in lambda closure - public static R[] Map(this T[] source, S state, Func map) - { - if (source == null) - return null; - - var sourceCount = source.Length; - if (sourceCount == 0) - return Empty(); - - if (sourceCount == 1) - return new[] { map(state, source[0]) }; - - if (sourceCount == 2) - return new[] { map(state, source[0]), map(state, source[1]) }; - - var results = new R[sourceCount]; - for (var i = 0; i < source.Length; i++) - results[i] = map(state, source[i]); - return results; - } - - /// Maps all items from source to result collection. - /// If possible uses fast array Map otherwise Enumerable.Select. - /// Source item type Result item type - /// Source items Function to convert item from source to result. - /// Converted items - public static IEnumerable Map(this IEnumerable source, Func map) => - source is T[] arr ? arr.Map(map) : source?.Select(map); - - /// If is array uses more effective Match for array, otherwise just calls Where - /// Type of source items. - /// If null, the null will be returned. - /// Condition to keep items. - /// Result items, may be an array. - public static IEnumerable Match(this IEnumerable source, Func condition) => - source is T[] arr ? arr.Match(condition) : source?.Where(condition); - - /// If is array uses more effective Match for array, - /// otherwise just calls Where, Select - /// Type of source items. Type of result items. - /// If null, the null will be returned. - /// Condition to keep items. Converter from source to result item. - /// Result items, may be an array. - public static IEnumerable Match(this IEnumerable source, Func condition, Func map) => - source is T[] arr ? arr.Match(condition, map) : source?.Where(condition).Select(map); - } - - /// Wrapper that provides optimistic-concurrency Swap operation implemented using . - /// Type of object to wrap. - public sealed class Ref where T : class - { - /// Gets the wrapped value. - public T Value => _value; - private T _value; - - /// Creates ref to object, optionally with initial value provided. - /// (optional) Initial value. - public Ref(T initialValue = default) => _value = initialValue; - - /// Exchanges currently hold object with - see for details. - /// Delegate to produce new object value from current one passed as parameter. - /// Returns old object value the same way as - /// Important: May be called multiple times to retry update with value concurrently changed by other code. - public T Swap(Func getNewValue) => - Ref.Swap(ref _value, getNewValue); - - // todo: A good candidate to implement - // The same as `Swap` but instead of old known value it returns the new one - //public T SwapAndGetNewValue(Func getNewValue) => - // Ref.Swap(ref _value, getNewValue); - - /// Option without allocation for capturing `a` in closure of `getNewValue` - public T Swap(A a, Func getNewValue) => Ref.Swap(ref _value, a, getNewValue); - - /// Option without allocation for capturing `a` and `b` in closure of `getNewValue` - public T Swap(A a, B b, Func getNewValue) => Ref.Swap(ref _value, a, b, getNewValue); - - /// Just sets new value ignoring any intermingled changes and returns the original value - /// old value - public T Swap(T newValue) => Interlocked.Exchange(ref _value, newValue); - - /// Directly sets the value and returns the new value - public T SetNonAtomic(T newValue) => _value = newValue; - - /// Compares current Referred value with and if equal replaces current with - /// - /// True if current value was replaced with new value, and false if current value is outdated (already changed by other party). - /// [!CDATA[ - /// var value = SomeRef.Value; - /// if (!SomeRef.TrySwapIfStillCurrent(value, Update(value)) - /// SomeRef.Swap(v => Update(v)); // fallback to normal Swap with delegate allocation - /// ]] - public bool TrySwapIfStillCurrent(T currentValue, T newValue) => - Interlocked.CompareExchange(ref _value, newValue, currentValue) == currentValue; - } - - /// Provides optimistic-concurrency consistent operation. - public static class Ref - { - /// The default max retry count - can be overriden by `Swap` optional parameter - public const int RETRY_COUNT_UNTIL_THROW = 50; - - /// Factory for with type of value inference. - /// Type of value to wrap. - /// Initial value to wrap. - /// New ref. - public static Ref Of(T value) where T : class => new Ref(value); - - /// Creates new ref to the value of original ref. Ref value type. - /// Original ref. New ref to original value. - public static Ref NewRef(this Ref original) where T : class => Of(original.Value); - - /// First, it evaluates new value using function. - /// Second, it checks that original value is not changed. - /// If it is changed it will retry first step, otherwise it assigns new value and returns original (the one used for ). - /// Type of value to swap. - /// Reference to change to new value - /// Delegate to get value from old one. - /// (optional) - /// Old/original value. By analogy with . - /// Important: May be called multiple times to retry update with value concurrently changed by other code. - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - private static void ThrowRetryCountExceeded(int retryCountExceeded) => - throw new InvalidOperationException( - $"Ref retried to Update for {retryCountExceeded} times But there is always someone else intervened."); - - /// Swap with the additional state required for the delegate . - /// May prevent closure creation for the delegate - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, A a, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue, a); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - /// Swap with the additional state , required for the delegate . - /// May prevent closure creation for the delegate - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, A a, B b, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue, a, b); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); - -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - /// Swap with the additional state , , required for the delegate . - /// May prevent closure creation for the delegate - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, A a, B b, C c, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue, a, b, c); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); - -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - // todo: Func of 5 args is not available on all plats - // /// Option without allocation for capturing `a`, `b`, `c`, `d` in closure of `getNewValue` - // [MethodImpl((MethodImplOptions)256)] - // public static T Swap(ref T value, A a, B b, C c, D d, Func getNewValue, - // int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - // where T : class - // { - //#if SUPPORTS_SPIN_WAIT - // var spinWait = new SpinWait(); - //#endif - // var retryCount = 0; - // while (true) - // { - // var oldValue = value; - // var newValue = getNewValue(oldValue, a, b, c, d); - // if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - // return oldValue; - - // if (++retryCount > retryCountUntilThrow) - // ThrowRetryCountExceeded(retryCountUntilThrow); - - //#if SUPPORTS_SPIN_WAIT - // spinWait.SpinOnce(); - //#endif - // } - // } - } - - /// Printable thing via provided printer - public interface IPrintable - { - /// Print to the provided string builder via the provided printer. - StringBuilder Print(StringBuilder s, Func printer); - } - - /// Produces good enough hash codes for the fields - public static class Hasher - { - /// Combines hashes of two fields - public static int Combine(T1 a, T2 b) => - Combine(a?.GetHashCode() ?? 0, b?.GetHashCode() ?? 0); - - /// Inspired by System.Tuple.CombineHashCodes - public static int Combine(int h1, int h2) - { - if (h1 == 0) return h2; - unchecked - { - return (h1 << 5) + h1 ^ h2; - } - } - } - - /// Simple unbounded object pool - public sealed class StackPool where T : class - { - /// Give me an object - [MethodImpl((MethodImplOptions)256)] - public T RentOrDefault() => - Interlocked.Exchange(ref _s, _s?.Tail)?.Head; - - /// Give it back - [MethodImpl((MethodImplOptions)256)] - public void Return(T x) => - Interlocked.Exchange(ref _s, new Stack(x, _s)); - - private Stack _s; - - private sealed class Stack - { - public readonly T Head; - public readonly Stack Tail; - public Stack(T h, Stack t) - { - Head = h; - Tail = t; - } - } - } - - /// Immutable Key-Value pair. It is reference type (could be check for null), - /// which is different from System value type . - /// In addition provides and implementations. - /// Type of Key.Type of Value. - public class KV : IPrintable - { - /// Key. - public readonly K Key; - - /// Value. - public readonly V Value; - - /// Creates Key-Value object by providing key and value. Does Not check either one for null. - /// key.value. - public KV(K key, V value) - { - Key = key; - Value = value; - } - - /// - public StringBuilder Print(StringBuilder s, Func printer) => - s.Append("(").To(b => Key == null ? b : printer(b, Key)) - .Append(", ").To(b => Value == null ? b : printer(b, Value)) - .Append(')'); - - /// Creates nice string view.String representation. - public override string ToString() => - Print(new StringBuilder(), (s, x) => s.Append(x)).ToString(); - - /// Returns true if both key and value are equal to corresponding key-value of other object. - public override bool Equals(object obj) - { - var other = obj as KV; - return other != null - && (ReferenceEquals(other.Key, Key) || Equals(other.Key, Key)) - && (ReferenceEquals(other.Value, Value) || Equals(other.Value, Value)); - } - - /// Combines key and value hash code - public override int GetHashCode() => Hasher.Combine(Key, Value); - } - - /// Helpers for . - public static class KV - { - /// Creates the key value pair. - public static KV Of(K key, V value) => new KV(key, value); - - /// Creates the pair with the new value - public static KV WithValue(this KV kv, V value) => new KV(kv.Key, value); - } - - /// Simple helper for creation of the pair of two parts. - public static class KeyValuePair - { - /// Pairs key with value. - public static KeyValuePair Pair(this K key, V value) => new KeyValuePair(key, value); - } - - /// Helper structure which allows to distinguish null value from the default value for optional parameter. - public struct Opt - { - /// Allows to transparently convert parameter argument to opt structure. - public static implicit operator Opt(T value) => new Opt(value); - - /// Argument value. - public readonly T Value; - - /// Indicates that value is provided. - public readonly bool HasValue; - - /// Wraps passed value in structure. Sets the flag that value is present. - public Opt(T value) - { - HasValue = true; - Value = value; - } - - /// Helper to get value or default value if value is not present. - public T OrDefault(T defaultValue = default) => HasValue ? Value : defaultValue; - } - - /// Ever growing list - public struct GrowingList - { - /// Default initial capacity - public const int DefaultInitialCapacity = 2; - - /// The items array - public T[] Items; - - /// The count - public int Count; - - /// Constructs the thing - public GrowingList(T[] items, int count = 0) - { - Items = items; - Count = count; - } - - /// Push the new slot and return the ref to it - public ref T PushSlot() - { - if (Items == null) - Items = new T[DefaultInitialCapacity]; - else if (Count >= Items.Length) - Items = Expand(Items); - return ref Items[Count++]; - } - - /// Adds the new item possibly extending the item collection - public void PushSlot(T item) - { - if (Items == null) - Items = new T[DefaultInitialCapacity]; - else if (Count >= Items.Length) - Items = Expand(Items); - Items[Count++] = item; - } - - /// Pops the item - just moving the counter back - public void Pop() => --Count; - - /// Expands the items starting with 2 - private static T[] Expand(T[] items) - { - var count = items.Length; - var newItems = new T[count << 1]; // count x 2 - Array.Copy(items, 0, newItems, 0, count); - return newItems; - } - - /// - public override string ToString() => - $"Count {Count} of {(Count == 0 || Items == null || Items.Length == 0 ? "empty" : "first (" + Items[0] + ") and last (" + Items[Count - 1] + ")")}"; - } - - /// Immutable list - simplest linked list with the Head and the Tail. - public sealed class ImList - { - /// Empty list to Push to. - public static readonly ImList Empty = new ImList(); - - /// True for empty list. - public bool IsEmpty => Tail == null; - - /// First value in a list. - public readonly T Head; - - /// The rest of values or Empty if list has a single value. - public readonly ImList Tail; - - /// Prepends new value and returns new list. - public ImList Push(T head) => new ImList(head, this); - - /// Enumerates the list. - public IEnumerable Enumerate() - { - if (IsEmpty) - yield break; - for (var list = this; !list.IsEmpty; list = list.Tail) - yield return list.Head; - } - - /// String representation for debugging purposes - public override string ToString() => IsEmpty - ? "[]" : Tail.IsEmpty - ? "[" + Head + "]" : Tail.Tail.IsEmpty - ? "[" + Head + "," + Tail.Head + "]" : Tail.Tail.Tail.IsEmpty - ? "[" + Head + "," + Tail.Head + "," + Tail.Tail.Head + "]" - : "[" + Head + "," + Tail.Head + "," + Tail.Tail.Head + ", ...]"; - - private ImList() { } - - private ImList(T head, ImList tail) - { - Head = head; - Tail = tail; - } - } - - /// Extension methods providing basic operations on a list. - public static class ImList - { - /// Split list into (Head, Tail, IsEmpty) tuple - public static void Deconstruct(this ImList list, out T head, out ImList tail, out bool isEmpty) - { - head = list.Head; - tail = list.Tail; - isEmpty = list.IsEmpty; - } - - /// - /// Constructs the reversed list from the parameter array of items - /// - public static ImList List(params T[] items) - { - var l = ImList.Empty; - if (items != null) - for (var i = items.Length - 1; i >= 0; --i) - l = l.Push(items[i]); - return l; - } - - /// - /// Constructs the list as the reversed input list - /// - public static ImList ToImList(this IList source) - { - var l = ImList.Empty; - if (source != null) - for (var i = source.Count - 1; i >= 0; --i) - l = l.Push(source[i]); - return l; - } - - /// - /// Constructs the list as the reversed enumerable - /// - public static ImList ToImList(this IEnumerable source) - { - if (source is IList list) - return list.ToImList(); - var l = ImList.Empty; - - if (source != null) - foreach (var item in source) - l = l.Push(item); - return l.Reverse(); - } - - /// Constructs list of one element - public static ImList List(this T head) => ImList.Empty.Push(head); - - /// Constructs list from head and tail - public static ImList List(this T head, ImList tail) => tail.Push(head); - - /// Apples some effect action to each element - public static void ForEach(this ImList list, Action effect) - { - for (; !list.IsEmpty; list = list.Tail) - effect(list.Head); - } - - /// Fold list to a single value. The respective name for it in LINQ is Aggregate - public static S Fold(this ImList list, S state, Func reduce) - { - if (list.IsEmpty) - return state; - var result = state; - for (; !list.IsEmpty; list = list.Tail) - result = reduce(list.Head, result); - return result; - } - - /// Fold list to a single value with index of item. The respective name for it in LINQ is Aggregate. - public static S Fold(this ImList list, S state, Func reduce) - { - if (list.IsEmpty) - return state; - var result = state; - for (var i = 0; !list.IsEmpty; list = list.Tail, ++i) - result = reduce(list.Head, i, result); - return result; - } - - /// Returns new list in reverse order. - public static ImList Reverse(this ImList list) - { - if (list.IsEmpty || list.Tail.IsEmpty) - return list; - var reversed = ImList.Empty; - for (; !list.IsEmpty; list = list.Tail) - reversed = reversed.Push(list.Head); - return reversed; - } - - /// Maps the items from the first list to the result list. - public static ImList Map(this ImList list, Func map) => - list.Fold(ImList.Empty, (x, r) => List(map(x), r)).Reverse(); - - /// Maps with index - public static ImList Map(this ImList list, Func map) => - list.Fold(ImList.Empty, (x, i, r) => List(map(x, i), r)).Reverse(); - - /// Copies list to array. - public static T[] ToArray(this ImList source) => - source.IsEmpty ? ArrayTools.Empty() - : source.Tail.IsEmpty ? new[] { source.Head } : source.Enumerate().ToArray(); - } - - /// Zipper is an immutable persistent data structure, to represent collection with single focused (selected, active) element. - /// Consist of REVERSED `Left` immutable list, `Focus` element, and the `Right` immutable list. That's why a Zipper name, - /// where left and right part are joined / zipped in focus item. - public sealed class ImZipper - { - /// Empty singleton instance to start building your zipper - public static readonly ImZipper Empty = new ImZipper(); - - /// True is zipper does not contain items - public bool IsEmpty => Count == 0; - - /// Index of Focus item, from `0` to `Count-1` - public readonly int Index; - - /// Number of items - public readonly int Count; - - /// Left REVERSED list, so the Head of the list is just prior the Focus item - public readonly ImList Left; - - /// Right list, where Head is just after the Focus item - public readonly ImList Right; - - /// Single focus item - public readonly T Focus; - - /// - public override string ToString() => - IsEmpty ? "[||]" : Count + ":" + Left.Reverse() + "|" + Index + ":" + Focus + "|" + Right; - - /// Sets a new focus and pushes the old focus to the Left list. - public ImZipper Append(T focus) => PushLeft(focus); - - /// Sets a new focus and pushes the old focus to the Left list. - public ImZipper PushLeft(T focus) => - IsEmpty ? new ImZipper(ImList.Empty, focus, 0, ImList.Empty, 1) - : new ImZipper(Left.Push(Focus), focus, Index + 1, Right, Count + 1); - - /// Sets a new focus and pushes the old focus to the right list. - public ImZipper Insert(T focus) => PushRight(focus); - - /// Sets a new focus and pushes the old focus to the right list. - public ImZipper PushRight(T focus) => - IsEmpty ? new ImZipper(ImList.Empty, focus, 0, ImList.Empty, 1) - : new ImZipper(Left, focus, Index, Right.Push(Focus), Count + 1); - - /// Removes a focus, filling the hole with the item from the left list, or from the right if the left is empty - public ImZipper PopLeft() => - IsEmpty ? this - : Left.IsEmpty && Right.IsEmpty ? Empty - : !Left.IsEmpty ? new ImZipper(Left.Tail, Left.Head, Index - 1, Right, Count - 1) - : new ImZipper(Left, Right.Head, Index, Right.Tail, Count - 1); - - /// Removes a focus, filling the hole with the item from the right list, or from the left if the right is empty - public ImZipper PopRight() => - IsEmpty ? this - : Left.IsEmpty && Right.IsEmpty ? Empty - : !Right.IsEmpty ? new ImZipper(Left, Right.Head, Index, Right.Tail, Count - 1) - : new ImZipper(Left.Tail, Left.Head, Index - 1, Right, Count - 1); - - /// Shifts focus one element to the left (decrementing its Index). - public ImZipper ShiftLeft() => - IsEmpty || Left.IsEmpty ? this - : new ImZipper(Left.Tail, Left.Head, Index - 1, Right.Push(Focus), Count); - - /// Shifts focus one element to the right (incrementing its Index). - public ImZipper ShiftRight() => - IsEmpty || Right.IsEmpty ? this - : new ImZipper(Left.Push(Focus), Right.Head, Index + 1, Right.Tail, Count); - - /// Sets a new focus and returns a new zipper with the left and right lists unchanged - public ImZipper WithFocus(T focus) => - IsEmpty ? this : new ImZipper(Left, focus, Index, Right, Count); - - /// Maps over the zipper items producing a new zipper - public ImZipper Map(Func map) => - IsEmpty ? ImZipper.Empty - : new ImZipper(Left.Reverse().Fold(ImList.Empty, (x, r) => r.Push(map(x))), - map(Focus), Index, Right.Map(map), Count); - - /// Maps over the zipper items with item index, producing a new zipper - public ImZipper Map(Func map) => - IsEmpty ? ImZipper.Empty - : new ImZipper( - Left.Reverse().Fold(ImList.Empty, (x, i, r) => r.Push(map(x, i))), - map(Focus, Index), Index, Right.Map((x, i) => map(x, Index + 1 + i)), Count); - - private ImZipper() => Index = -1; - - private ImZipper(ImList left, T focus, int index, ImList right, int count) - { - Left = left; - Focus = focus; - Index = index; - Right = right; - Count = count; - } - } - - /// Other ImZipper methods - public static class ImZipper - { - /// Appends array items to zipper - public static ImZipper Zip(params T[] items) - { - if (items.IsNullOrEmpty()) - return ImZipper.Empty; - var z = ImZipper.Empty; - for (var i = 0; i < items.Length; ++i) - z = z.PushLeft(items[i]); - return z; - } - - /// Converts to array. - public static T[] ToArray(this ImZipper z) - { - if (z.IsEmpty) - return ArrayTools.Empty(); - var a = new T[z.Count]; - z.Fold(a, (x, i, xs) => - { - xs[i] = x; - return xs; - }); - return a; - } - - /// Shifts focus to a specified index, e.g. a random access - public static ImZipper ShiftTo(this ImZipper z, int i) - { - if (i < 0 || i >= z.Count || i == z.Index) - return z; - while (i < z.Index) - z = z.ShiftLeft(); - while (i > z.Index) - z = z.ShiftRight(); - return z; - } - - /// Updates a focus element if it is present, otherwise does nothing. - /// If the focus item is the equal one, then returns the same zipper back. - public static ImZipper Update(this ImZipper z, Func update) - { - if (z.IsEmpty) - return z; - var result = update(z.Focus); - if (ReferenceEquals(z.Focus, result) || result != null && result.Equals(z.Focus)) - return z; - return z.WithFocus(result); - } - - /// Update the item at random index, by shifting and updating it - public static ImZipper UpdateAt(this ImZipper z, int i, Func update) => - i < 0 || i >= z.Count ? z : z.ShiftTo(i).Update(update); - - /// Update the item at random index, by shifting and updating it - public static ImZipper RemoveAt(this ImZipper z, int i) => - i < 0 || i >= z.Count ? z : z.ShiftTo(i).PopLeft(); - - /// Folds zipper to a single value - public static S Fold(this ImZipper z, S state, Func reduce) => - z.IsEmpty ? state : - z.Right.Fold(reduce(z.Focus, z.Left.Reverse().Fold(state, reduce)), reduce); - - /// Folds zipper to a single value by using an item index - public static S Fold(this ImZipper z, S state, Func reduce) - { - if (z.IsEmpty) - return state; - var focusIndex = z.Index; - var reducedLeft = z.Left.Reverse().Fold(state, reduce); - return z.Right.Fold(reduce(z.Focus, focusIndex, reducedLeft), - (x, i, r) => reduce(x, focusIndex + i + 1, r)); - } - - /// Apply some effect action on each element - public static void ForEach(this ImZipper z, Action effect) - { - if (!z.IsEmpty) - { - if (!z.Left.IsEmpty) - z.Left.Reverse().ForEach(effect); - effect(z.Focus); - if (!z.Right.IsEmpty) - z.Right.ForEach(effect); - } - } - } - - /// Given the old value should and the new value should return result updated value. - public delegate V Update(V oldValue, V newValue); - - /// Update handler including the key - public delegate V Update(K key, V oldValue, V newValue); - - /// - /// Fold reducer. Designed as a alternative to `Func{V, S, S}` but with possibility of inlining on the call side. - /// Note: To get the advantage of inlining the can the interface should be implemented and passed as a NON-GENERIC STRUCT - /// - public interface IFoldReducer - { - /// Reduce method - S Reduce(T x, S state); - } - - /// - /// Immutable http://en.wikipedia.org/wiki/AVL_tree with integer keys and values. - /// - public class ImMap - { - /// Empty tree to start with. - public static readonly ImMap Empty = new ImMap(); - - /// Returns true if tree is empty. - public bool IsEmpty => this == Empty; - - /// Prevents multiple creation of an empty tree - protected ImMap() { } - - /// Height of the longest sub-tree/branch - 0 for the empty tree - public virtual int Height => 0; - - /// Prints "empty" - public override string ToString() => "empty"; - } - - /// Wraps the stored data with "fixed" reference semantics - when added to the tree it did not change or reconstructed in memory - public sealed class ImMapEntry : ImMap - { - /// - public override int Height => 1; - - /// The Key is basically the hash, or the Height for ImMapTree - public readonly int Key; - - /// The value - may be modified if you need a Ref{V} semantics - public V Value; - - /// Constructs the entry with the default value - public ImMapEntry(int key) => Key = key; - - /// Constructs the entry - public ImMapEntry(int key, V value) - { - Key = key; - Value = value; - } - - /// Prints the key value pair - public override string ToString() => Key + ":" + Value; - } - - /// - /// The two level - two node tree with either left or right - /// - public sealed class ImMapBranch : ImMap - { - /// Always two - public override int Height => 2; - - /// Contains the once created data node - public readonly ImMapEntry Entry; - - /// Left sub-tree/branch, or empty. - public ImMapEntry RightEntry; - - /// Constructor - public ImMapBranch(ImMapEntry entry, ImMapEntry rightEntry) - { - Entry = entry; - RightEntry = rightEntry; - } - - /// Prints the key value pair - public override string ToString() => "h2:" + Entry + "->" + RightEntry; - } - - /// - /// The tree always contains Left and Right node, for the missing leaf we have - /// - public sealed class ImMapTree : ImMap - { - /// Starts from 2 - public override int Height => TreeHeight; - - /// Starts from 2 - allows to access the field directly when you know it is a Tree - public int TreeHeight; - - /// Contains the once created data node - public readonly ImMapEntry Entry; - - /// Left sub-tree/branch, or empty. - public ImMap Left; - - /// Right sub-tree/branch, or empty.md - public ImMap Right; - - internal ImMapTree(ImMapEntry entry, ImMap left, ImMap right, int height) - { - Entry = entry; - Left = left; - Right = right; - TreeHeight = height; - } - - internal ImMapTree(ImMapEntry entry, ImMapEntry leftEntry, ImMapEntry rightEntry) - { - Entry = entry; - Left = leftEntry; - Right = rightEntry; - TreeHeight = 2; - } - - /// Outputs the brief tree info - mostly for debugging purposes - public override string ToString() => - "h" + Height + ":" + Entry - + "->(" + (Left is ImMapTree leftTree ? "h" + leftTree.TreeHeight + ":" + leftTree.Entry : "" + Left) - + ", " + (Right is ImMapTree rightTree ? "h" + rightTree.TreeHeight + ":" + rightTree.Entry : "" + Right) - + ")"; - - /// Adds or updates the left or right branch - public ImMapTree AddOrUpdateLeftOrRightEntry(int key, ImMapEntry entry) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(entry, leftTree.Left, leftTree.Right, leftTree.TreeHeight), - Right, TreeHeight); - - var newLeftTree = leftTree.AddOrUpdateLeftOrRightEntry(key, entry); - return newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), - Right, TreeHeight); - - if (key > leftBranch.Entry.Key) - { - var newLeft = - // 5 5 - // 2 ? => 3 ? - // 3 2 4 - // 4 - key > leftBranch.RightEntry.Key ? new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry) - // 5 5 - // 2 ? => 2.5 ? - // 3 2 3 - // 2.5 - : key < leftBranch.RightEntry.Key ? new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry) - : (ImMap)new ImMapBranch(leftBranch.Entry, entry); - - return new ImMapTree(Entry, newLeft, Right, TreeHeight); - } - - return new ImMapTree(Entry, - new ImMapBranch(entry, leftBranch.RightEntry), Right, TreeHeight); - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) - : key < leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3) - : new ImMapTree(Entry, entry, Right, TreeHeight); - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(entry, rightTree.Left, rightTree.Right, rightTree.TreeHeight), - TreeHeight); - - var newRightTree = rightTree.AddOrUpdateLeftOrRightEntry(key, entry); - return newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.Entry.Key) - { - var newRight = - // 5 5 - // ? 6 => ? 8 - // 8 6 ! - // ! - key > rightBranch.RightEntry.Key ? new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry) - // 5 5 - // ? 6 => ? 7 - // 8 6 8 - // 7 - : key < rightBranch.RightEntry.Key ? new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry) - : (ImMap)new ImMapBranch(rightBranch.Entry, entry); - - return new ImMapTree(Entry, Left, newRight, TreeHeight); - } - - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), - TreeHeight); - - return new ImMapTree(Entry, Left, new ImMapBranch(entry, rightBranch.RightEntry), TreeHeight); - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3) - : new ImMapTree(Entry, Left, entry, TreeHeight); - } - } - - /// Adds the left or right branch - public ImMapTree AddUnsafeLeftOrRightEntry(int key, ImMapEntry entry) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - var newLeftTree = leftTree.AddUnsafeLeftOrRightEntry(key, entry); - return newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), - Right, TreeHeight); - - var newLeft = key > leftBranch.RightEntry.Key - ? new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry) - : new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry); - - return new ImMapTree(Entry, newLeft, Right, TreeHeight); - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) - : new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3); - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - var newRightTree = rightTree.AddUnsafeLeftOrRightEntry(key, entry); - return newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.Entry.Key) - { - var newRight = key > rightBranch.RightEntry.Key - ? new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry) - : new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry); - - return new ImMapTree(Entry, Left, newRight, TreeHeight); - } - - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), - TreeHeight); - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) - : new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3); - } - } - - /// Adds to the left or right branch, or keeps the un-modified map - public ImMapTree AddOrKeepLeftOrRight(int key, V value) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return this; - - var newLeftTree = leftTree.AddOrKeepLeftOrRight(key, value); - return newLeftTree == leftTree ? this - : newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, new ImMapEntry(key, value), leftBranch.RightEntry), - Right, TreeHeight); - if (key > leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, new ImMapEntry(key, value)), - Right, TreeHeight); - if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(new ImMapEntry(key, value), leftBranch.Entry, leftBranch.RightEntry), - Right, TreeHeight); - return this; - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, new ImMapEntry(key, value)), Right, 3) - : key < leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(new ImMapEntry(key, value), leftLeaf), Right, 3) - : this; - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return this; - - var newRightTree = rightTree.AddOrKeepLeftOrRight(key, value); - return newRightTree == rightTree ? this - : newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, new ImMapEntry(key, value)), - TreeHeight); - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, new ImMapEntry(key, value), rightBranch.RightEntry), - TreeHeight); - if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(new ImMapEntry(key, value), rightBranch.Entry, rightBranch.RightEntry), - TreeHeight); - return this; - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, new ImMapEntry(key, value)), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(new ImMapEntry(key, value), rightLeaf), 3) - : this; - } - } - - /// Adds to the left or right branch, or keeps the un-modified map - public ImMapTree AddOrKeepLeftOrRight(int key) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return this; - - var newLeftTree = leftTree.AddOrKeepLeftOrRight(key); - return newLeftTree == leftTree ? this - : newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, new ImMapEntry(key), leftBranch.RightEntry), - Right, TreeHeight); - if (key > leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, new ImMapEntry(key)), - Right, TreeHeight); - if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(new ImMapEntry(key), leftBranch.Entry, leftBranch.RightEntry), - Right, TreeHeight); - return this; - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, new ImMapEntry(key)), Right, 3) - : key < leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(new ImMapEntry(key), leftLeaf), Right, 3) - : this; - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return this; - - // note: tree always contains left and right (for the missing leaf we have a Branch) - var newRightTree = rightTree.AddOrKeepLeftOrRight(key); - return newRightTree == rightTree ? this - : newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, new ImMapEntry(key)), - TreeHeight); - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, new ImMapEntry(key), rightBranch.RightEntry), - TreeHeight); - if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(new ImMapEntry(key), rightBranch.Entry, rightBranch.RightEntry), - TreeHeight); - return this; - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, new ImMapEntry(key)), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(new ImMapEntry(key), rightLeaf), 3) - : this; - } - } - - /// Adds to the left or right branch, or keeps the un-modified map - public ImMapTree AddOrKeepLeftOrRightEntry(int key, ImMapEntry entry) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return this; - - var newLeftTree = leftTree.AddOrKeepLeftOrRightEntry(key, entry); - return newLeftTree == leftTree ? this - : newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), - Right, TreeHeight); - if (key > leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry), - Right, TreeHeight); - if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry), - Right, TreeHeight); - return this; - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) - : key < leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3) - : this; - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return this; - - // note: tree always contains left and right (for the missing leaf we have a Branch) - var newRightTree = rightTree.AddOrKeepLeftOrRightEntry(key, entry); - return newRightTree == rightTree ? this - : newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry), - TreeHeight); - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), - TreeHeight); - if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry), - TreeHeight); - return this; - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3) - : this; - } - } - - private ImMapTree BalanceNewLeftTree(ImMapTree newLeftTree) - { - var rightHeight = Right.Height; - var delta = newLeftTree.TreeHeight - rightHeight; - if (delta <= 0) - return new ImMapTree(Entry, newLeftTree, Right, TreeHeight); - - if (delta == 1) - return new ImMapTree(Entry, newLeftTree, Right, newLeftTree.TreeHeight + 1); - - // here is the balancing art comes into place - if (rightHeight == 1) - { - if (newLeftTree.Left is ImMapEntry leftLeftLeaf) - { - // 30 15 - // 10 40 => 10 20 - // 5 15 5 12 30 40 - // 12 20 - if (newLeftTree.Right is ImMapTree leftRightTree) - { - newLeftTree.Right = leftRightTree.Left; - newLeftTree.TreeHeight = 2; - return new ImMapTree(leftRightTree.Entry, - newLeftTree, - new ImMapTree(Entry, leftRightTree.Right, Right, 2), - 3); - } - - // we cannot reuse the new left tree here because it is reduced into the branch - // 30 15 - // 10 40 => 5 20 - // 5 15 10 30 40 - // 20 - var leftRightBranch = (ImMapBranch)newLeftTree.Right; - return new ImMapTree(leftRightBranch.Entry, - new ImMapBranch(leftLeftLeaf, newLeftTree.Entry), - new ImMapTree(Entry, leftRightBranch.RightEntry, Right, 2), - 3); - } - - newLeftTree.Right = new ImMapTree(Entry, newLeftTree.Right, Right, 2); - newLeftTree.TreeHeight = 3; - return newLeftTree; - } - - var leftLeftHeight = (newLeftTree.Left as ImMapTree)?.TreeHeight ?? 2; - var leftRightHeight = (newLeftTree.Right as ImMapTree)?.TreeHeight ?? 2; - if (leftLeftHeight < leftRightHeight) - { - var leftRightTree = (ImMapTree)newLeftTree.Right; - - newLeftTree.Right = leftRightTree.Left; - newLeftTree.TreeHeight = leftLeftHeight + 1; - return new ImMapTree(leftRightTree.Entry, - newLeftTree, - new ImMapTree(Entry, leftRightTree.Right, Right, rightHeight + 1), - leftLeftHeight + 2); - - //return new ImMapTree(leftRightTree.Entry, - // new ImMapTree(newLeftTree.Entry, leftLeftHeight, newLeftTree.Left, leftRightTree.Left), - // new ImMapTree(Entry, leftRightTree.Right, rightHeight, Right)); - } - - newLeftTree.Right = new ImMapTree(Entry, newLeftTree.Right, Right, leftRightHeight + 1); - newLeftTree.TreeHeight = leftRightHeight + 2; - return newLeftTree; - } - - private ImMapTree BalanceNewRightTree(ImMapTree newRightTree) - { - var leftHeight = Left.Height; - var delta = newRightTree.Height - leftHeight; - if (delta <= 0) - return new ImMapTree(Entry, Left, newRightTree, TreeHeight); - if (delta == 1) - return new ImMapTree(Entry, Left, newRightTree, newRightTree.TreeHeight + 1); - - if (leftHeight == 1) - { - // here we need to re-balance by default, because the new right tree is at least 3 level (actually exactly 3 or it would be too unbalanced) - // double rotation needed if only the right-right is a leaf - if (newRightTree.Right is ImMapEntry == false) - { - newRightTree.Left = new ImMapTree(Entry, Left, newRightTree.Left, 2); - newRightTree.TreeHeight = 3; - return newRightTree; - } - - // 20 30 - // 10 40 => 20 40 - // 30 50 10 25 35 50 - // 25 35 - if (newRightTree.Left is ImMapTree rightLeftTree) - { - newRightTree.Left = rightLeftTree.Right; - newRightTree.TreeHeight = 2; - return new ImMapTree(rightLeftTree.Entry, - new ImMapTree(Entry, Left, rightLeftTree.Left, 2), - newRightTree, 3); - } - - // 20 30 - // 10 40 => 10 40 - // 30 50 20 35 50 - // 35 - var rightLeftBranch = (ImMapBranch)newRightTree.Left; - newRightTree.Left = rightLeftBranch.RightEntry; - newRightTree.TreeHeight = 2; - return new ImMapTree(rightLeftBranch.Entry, - new ImMapBranch((ImMapEntry)Left, Entry), - newRightTree, 3); - } - - var rightRightHeight = (newRightTree.Right as ImMapTree)?.TreeHeight ?? 2; - var rightLeftHeight = (newRightTree.Left as ImMapTree)?.TreeHeight ?? 2; - if (rightRightHeight < rightLeftHeight) - { - var rightLeftTree = (ImMapTree)newRightTree.Left; - newRightTree.Left = rightLeftTree.Right; - // the height now should be defined by rr - because left now is shorter by 1 - newRightTree.TreeHeight = rightRightHeight + 1; - // the whole height consequentially can be defined by `newRightTree` (rr+1) because left is consist of short Left and -2 rl.Left - return new ImMapTree(rightLeftTree.Entry, - // Left should be >= rightLeft.Left because it maybe rightLeft.Right which defines rl height - new ImMapTree(Entry, Left, rightLeftTree.Left, leftHeight + 1), - newRightTree, - rightRightHeight + 2); - - //return new ImMapTree(rightLeftTree.Entry, - // new ImMapTree(Entry, leftHeight, Left, rightLeftTree.Left), - // new ImMapTree(newRightTree.Entry, rightLeftTree.Right, rightRightHeight, newRightTree.Right)); - } - - Debug.Assert(rightLeftHeight >= leftHeight, "The whole rightHeight > leftHeight by 2, and rightRight >= leftHeight but not more than by 2"); - - // we may decide on the height because the Left smaller by 2 - newRightTree.Left = new ImMapTree(Entry, Left, newRightTree.Left, rightLeftHeight + 1); - // if rr was > rl by 1 than new rl+1 should be equal height to rr now, if rr was == rl than new rl wins anyway - newRightTree.TreeHeight = rightLeftHeight + 2; - return newRightTree; - } - } - - /// ImMap methods - public static class ImMap - { - /// Adds or updates the value by key in the map, always returns a modified map - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrUpdateEntry(this ImMap map, ImMapEntry entry) - { - if (map == ImMap.Empty) - return entry; - - var key = entry.Key; - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, entry) - : key < leaf.Key ? new ImMapBranch(entry, leaf) - : (ImMap)entry; - - if (map is ImMapBranch branch) - { - if (key > branch.Entry.Key) - // 5 10 - // 10 => 5 11 - // 11 - return key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, entry) - // 5 7 - // 10 => 5 10 - // 7 - : key < branch.RightEntry.Key // rotate if right - ? new ImMapTree(entry, branch.Entry, branch.RightEntry) - : (ImMap)new ImMapBranch(branch.Entry, entry); - - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, entry, branch.RightEntry) - : (ImMap)new ImMapBranch(entry, branch.RightEntry); - } - - var tree = (ImMapTree)map; - return key == tree.Entry.Key - ? new ImMapTree(entry, tree.Left, tree.Right, tree.TreeHeight) - : tree.AddOrUpdateLeftOrRightEntry(key, entry); - } - - /// Adds or updates the value by key in the map, always returns a modified map - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrUpdate(this ImMap map, int key, V value) => - map.AddOrUpdateEntry(new ImMapEntry(key, value)); - - /// Adds the value by key in the map - ASSUMES that the key is not in the map, always returns a modified map - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddEntryUnsafe(this ImMap map, ImMapEntry entry) - { - if (map == ImMap.Empty) - return entry; - - var key = entry.Key; - if (map is ImMapEntry leaf) - return key > leaf.Key - ? new ImMapBranch(leaf, entry) - : new ImMapBranch(entry, leaf); - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, entry, branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, entry) - : new ImMapTree(entry, branch.Entry, branch.RightEntry); - - return ((ImMapTree)map).AddUnsafeLeftOrRightEntry(key, entry); - } - - /// Adds the value for the key or returns the un-modified map if key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrKeep(this ImMap map, int key, V value) - { - if (map == ImMap.Empty) - return new ImMapEntry(key, value); - - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, new ImMapEntry(key, value)) - : key < leaf.Key ? new ImMapBranch(new ImMapEntry(key, value), leaf) - : map; - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, new ImMapEntry(key, value), branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, new ImMapEntry(key, value)) - : key > branch.Entry.Key && key < branch.RightEntry.Key - ? new ImMapTree(new ImMapEntry(key, value), branch.Entry, branch.RightEntry) - : map; - - var tree = (ImMapTree)map; - return key != tree.Entry.Key - ? tree.AddOrKeepLeftOrRight(key, value) - : map; - } - - /// Adds the entry with default value for the key or returns the un-modified map if key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrKeep(this ImMap map, int key) - { - if (map == ImMap.Empty) - return new ImMapEntry(key); - - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, new ImMapEntry(key)) - : key < leaf.Key ? new ImMapBranch(new ImMapEntry(key), leaf) - : map; - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, new ImMapEntry(key), branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, new ImMapEntry(key)) - : key > branch.Entry.Key && key < branch.RightEntry.Key - ? new ImMapTree(new ImMapEntry(key), branch.Entry, branch.RightEntry) - : map; - - var tree = (ImMapTree)map; - return key != tree.Entry.Key ? tree.AddOrKeepLeftOrRight(key) : map; - } - - /// Adds the entry for the key or returns the un-modified map if key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrKeepEntry(this ImMap map, ImMapEntry entry) - { - if (map == ImMap.Empty) - return entry; - - var key = entry.Key; - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, entry) - : key < leaf.Key ? new ImMapBranch(entry, leaf) - : map; - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, entry, branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, entry) - : key > branch.Entry.Key && key < branch.RightEntry.Key - ? new ImMapTree(entry, branch.Entry, branch.RightEntry) - : map; - - var tree = (ImMapTree)map; - return key != tree.Entry.Key ? tree.AddOrKeepLeftOrRightEntry(key, entry) : map; - } - - ///Returns the new map with the updated value for the key, or the same map if the key was not found. - [MethodImpl((MethodImplOptions)256)] - public static ImMap Update(this ImMap map, int key, V value) => - map.Contains(key) ? map.UpdateImpl(key, new ImMapEntry(key, value)) : map; - - ///Returns the new map with the updated value for the key, ASSUMES that the key is not in the map. - [MethodImpl((MethodImplOptions)256)] - public static ImMap UpdateEntryUnsafe(this ImMap map, ImMapEntry entry) => - map.UpdateImpl(entry.Key, entry); - - internal static ImMap UpdateImpl(this ImMap map, int key, ImMapEntry entry) - { - if (map is ImMapTree tree) - return key > tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left, tree.Right.UpdateImpl(key, entry), tree.TreeHeight) - : key < tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left.UpdateImpl(key, entry), tree.Right, tree.TreeHeight) - : new ImMapTree(entry, tree.Left, tree.Right, tree.TreeHeight); - - // the key was found - so it should be either entry or right entry - if (map is ImMapBranch branch) - return key == branch.Entry.Key - ? new ImMapBranch(entry, branch.RightEntry) - : new ImMapBranch(branch.Entry, entry); - - return entry; - } - - ///Returns the new map with the value set to default, or the same map if the key was not found. - [MethodImpl((MethodImplOptions)256)] - public static ImMap UpdateToDefault(this ImMap map, int key) => - map.Contains(key) ? map.UpdateToDefaultImpl(key) : map; - - internal static ImMap UpdateToDefaultImpl(this ImMap map, int key) - { - if (map is ImMapTree tree) - return key > tree.Entry.Key - ? new ImMapTree(tree.Entry, tree.Left, tree.Right.UpdateToDefaultImpl(key), tree.TreeHeight) - : key < tree.Entry.Key - ? new ImMapTree(tree.Entry, tree.Left.UpdateToDefaultImpl(key), tree.Right, tree.TreeHeight) - : new ImMapTree(new ImMapEntry(key), tree.Left, tree.Right, tree.TreeHeight); - - // the key was found - so it should be either entry or right entry - if (map is ImMapBranch branch) - return key == branch.Entry.Key - ? new ImMapBranch(new ImMapEntry(key), branch.RightEntry) - : new ImMapBranch(branch.Entry, new ImMapEntry(key)); - - return new ImMapEntry(key); - } - - /// Returns `true` if key is found or `false` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static bool Contains(this ImMap map, int key) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - return true; - } - - if (map is ImMapBranch branch) - return branch.Entry.Key == key || branch.RightEntry.Key == key; - - entry = map as ImMapEntry; - return entry != null && entry.Key == key; - } - - /// Returns the entry if key is found or null otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry GetEntryOrDefault(this ImMap map, int key) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - return entry; - } - - if (map is ImMapBranch branch) - return branch.Entry.Key == key ? branch.Entry - : branch.RightEntry.Key == key ? branch.RightEntry - : null; - - entry = map as ImMapEntry; - return entry != null && entry.Key == key ? entry : null; - } - - /// Returns the value if key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImMap map, int key) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - return entry.Value; - } - - if (map is ImMapBranch branch) - return branch.Entry.Key == key ? branch.Entry.Value - : branch.RightEntry.Key == key ? branch.RightEntry.Value - : default; - - entry = map as ImMapEntry; - if (entry != null && entry.Key == key) - return entry.Value; - - return default; - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImMap map, int key, out V value) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - { - value = entry.Value; - return true; - } - } - - if (map is ImMapBranch branch) - { - if (branch.Entry.Key == key) - { - value = branch.Entry.Value; - return true; - } - - if (branch.RightEntry.Key == key) - { - value = branch.RightEntry.Value; - return true; - } - - value = default; - return false; - } - - entry = map as ImMapEntry; - if (entry != null && entry.Key == key) - { - value = entry.Value; - return true; - } - - value = default; - return false; - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFindEntry(this ImMap map, int key, out ImMapEntry result) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - { - result = entry; - return true; - } - } - - if (map is ImMapBranch branch) - { - if (branch.Entry.Key == key) - { - result = branch.Entry; - return true; - } - - if (branch.RightEntry.Key == key) - { - result = branch.RightEntry; - return true; - } - - result = null; - return false; - } - - entry = map as ImMapEntry; - if (entry != null && entry.Key == key) - { - result = entry; - return true; - } - - result = null; - return false; - } - - /// - /// Enumerates all the map nodes from the left to the right and from the bottom to top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, - /// the content of the stack is not important and could be erased. - /// - public static IEnumerable> Enumerate(this ImMap map, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - yield break; - - if (map is ImMapEntry leaf) - yield return leaf; - else if (map is ImMapBranch branch) - { - yield return branch.Entry; - yield return branch.RightEntry; - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - yield return (ImMapEntry)tree.Left; - yield return tree.Entry; - yield return (ImMapEntry)tree.Right; - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - yield return (ImMapEntry)tree.Left; - yield return tree.Entry; - yield return (ImMapEntry)tree.Right; - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - yield return tree.Entry; - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - yield return branch.Entry; - yield return branch.RightEntry; - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - yield return tree.Entry; - map = tree.Right; - } - else - { - yield return (ImMapEntry)map; - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - yield return tree.Entry; - map = tree.Right; - } - } - } - } - } - - /// - /// Folds all the map nodes with the state from left to right and from the bottom to top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, - /// the content of the stack is not important and could be erased. - /// - public static S Fold(this ImMap map, S state, Func, S, S> reduce, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - return state; - - if (map is ImMapEntry leaf) - state = reduce(leaf, state); - else if (map is ImMapBranch branch) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - state = reduce((ImMapEntry)map, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - } - } - } - - return state; - } - - /// - /// Folds all the map nodes with the state from left to right and from the bottom to top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, - /// the content of the stack is not important and could be erased. - /// - public static S Fold(this ImMap map, S state, A a, Func, S, A, S> reduce, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - return state; - - if (map is ImMapEntry leaf) - state = reduce(leaf, state, a); - else if (map is ImMapBranch branch) - { - state = reduce(branch.Entry, state, a); - state = reduce(branch.RightEntry, state, a); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state, a); - state = reduce(tree.Entry, state, a); - state = reduce((ImMapEntry)tree.Right, state, a); - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state, a); - state = reduce(tree.Entry, state, a); - state = reduce((ImMapEntry)tree.Right, state, a); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state, a); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - state = reduce(branch.Entry, state, a); - state = reduce(branch.RightEntry, state, a); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state, a); - map = tree.Right; - } - else - { - state = reduce((ImMapEntry)map, state, a); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state, a); - map = tree.Right; - } - } - } - } - - return state; - } - - /// - /// Visits all the map nodes with from the left to the right and from the bottom to the top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map height, content is not important and could be erased. - /// - public static void Visit(this ImMap map, Action> visit, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - return; - - if (map is ImMapEntry leaf) - visit(leaf); - else if (map is ImMapBranch branch) - { - visit(branch.Entry); - visit(branch.RightEntry); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - visit((ImMapEntry)tree.Left); - visit(tree.Entry); - visit((ImMapEntry)tree.Right); - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - visit((ImMapEntry)tree.Left); - visit(tree.Entry); - visit((ImMapEntry)tree.Right); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - visit(tree.Entry); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - visit(branch.Entry); - visit(branch.RightEntry); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - visit(tree.Entry); - map = tree.Right; - } - else - { - visit((ImMapEntry)map); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - visit(tree.Entry); - map = tree.Right; - } - } - } - } - } - - /// Wraps Key and Value payload to store inside ImMapEntry - public struct KValue - { - /// The key - public K Key; - /// The value - public object Value; - - /// Constructs a pair - public KValue(K key, object value) - { - Key = key; - Value = value; - } - } - - /// Uses the user provided hash and adds or updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrUpdate(this ImMap> map, int hash, K key, object value, Update update) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(CreateKValueEntry(hash, key, value)) - : UpdateEntryOrAddOrUpdateConflict(map, hash, oldEntry, key, value, update); - } - - private static ImMap> UpdateEntryOrAddOrUpdateConflict(ImMap> map, int hash, - ImMapEntry> oldEntry, K key, object value, Update update = null) - { - if (key.Equals(oldEntry.Value.Key)) - { - value = update == null ? value : update(key, oldEntry.Value.Value, value); - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key, value)); - } - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex != -1) - { - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - value = update == null ? value : update(key, conflicts[conflictIndex].Value.Value, value); - newConflicts[conflictIndex] = CreateKValueEntry(hash, key, value); - } - else - { - // add the new conflicting value - newConflicts = new ImMapEntry>[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = CreateKValueEntry(hash, key, value); - } - } - else - { - newConflicts = new[] { oldEntry, CreateKValueEntry(hash, key, value) }; - } - - var conflictsEntry = new ImMapEntry>(hash); - conflictsEntry.Value.Value = newConflicts; - return map.UpdateEntryUnsafe(conflictsEntry); - } - - /// Efficiently creates the new entry - [MethodImpl((MethodImplOptions)256)] - private static ImMapEntry> CreateKValueEntry(int hash, K key, object value) - { - var newEntry = new ImMapEntry>(hash); - newEntry.Value.Key = key; - newEntry.Value.Value = value; - return newEntry; - } - - /// Efficiently creates the new entry - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry> CreateKValueEntry(int hash, K key) - { - var newEntry = new ImMapEntry>(hash); - newEntry.Value.Key = key; - return newEntry; - } - - /// Uses the user provided hash and adds or updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrUpdate(this ImMap> map, int hash, K key, object value) => - map.AddOrUpdate(hash, CreateKValueEntry(hash, key, value)); - - /// Uses the provided hash and adds or updates the tree with the passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrUpdate(this ImMap> map, int hash, ImMapEntry> entry) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(entry) - : UpdateEntryOrAddOrUpdateConflict(map, hash, oldEntry, entry); - } - - private static ImMap> UpdateEntryOrAddOrUpdateConflict(ImMap> map, int hash, - ImMapEntry> oldEntry, ImMapEntry> newEntry) - { - if (newEntry.Value.Key.Equals(oldEntry.Value.Key)) - return map.UpdateEntryUnsafe(newEntry); - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var key = newEntry.Value.Key; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex != -1) - { - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictIndex] = newEntry; - } - else - { - // add the new conflicting value - newConflicts = new ImMapEntry>[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = newEntry; - } - } - else - { - newConflicts = new[] { oldEntry, newEntry }; - } - - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); - } - - /// Adds the new entry or keeps the current map if entry key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrKeep(this ImMap> map, int hash, K key) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(CreateKValueEntry(hash, key)) - : AddOrKeepConflict(map, hash, oldEntry, key); - } - - /// Adds the new entry or keeps the current map if entry key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrKeep(this ImMap> map, int hash, K key, object value) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(CreateKValueEntry(hash, key, value)) - : AddOrKeepConflict(map, hash, oldEntry, key, value); - } - - private static ImMap> AddOrKeepConflict(ImMap> map, int hash, - ImMapEntry> oldEntry, K key, object value = null) - { - if (key.Equals(oldEntry.Value.Key)) - return map; - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex != -1) - return map; - - // add the new conflicting value - newConflicts = new ImMapEntry>[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = CreateKValueEntry(hash, key, value); - } - else - { - newConflicts = new[] { oldEntry, CreateKValueEntry(hash, key, value) }; - } - - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); - } - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - public static ImMap> Update(this ImMap> map, int hash, K key, object value, Update update = null) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null ? map : UpdateEntryOrReturnSelf(map, hash, oldEntry, key, value, update); - } - - private static ImMap> UpdateEntryOrReturnSelf(ImMap> map, - int hash, ImMapEntry> oldEntry, K key, object value, Update update = null) - { - if (key.Equals(oldEntry.Value.Key)) - { - value = update == null ? value : update(key, oldEntry.Value.Value, value); - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key, value)); - } - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex == -1) - return map; - - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - value = update == null ? value : update(key, conflicts[conflictIndex].Value.Value, value); - newConflicts[conflictIndex] = CreateKValueEntry(hash, key, value); - } - else - { - return map; - } - - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); - } - - /// Updates the map with the default value if the key is found, otherwise returns the same unchanged map. - public static ImMap> UpdateToDefault(this ImMap> map, int hash, K key) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null ? map : UpdateEntryOrReturnSelf(map, hash, oldEntry, key); - } - - private static ImMap> UpdateEntryOrReturnSelf(ImMap> map, - int hash, ImMapEntry> oldEntry, K key) - { - if (key.Equals(oldEntry.Value.Key)) - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key)); - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex == -1) - return map; - - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictIndex] = CreateKValueEntry(hash, key); - } - else - { - return map; - } - - var conflictsEntry = new ImMapEntry>(hash); - conflictsEntry.Value.Value = newConflicts; - return map.UpdateEntryUnsafe(conflictsEntry); - } - - /// Returns the entry if key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry> GetEntryOrDefault(this ImMap> map, int hash, K key) - { - var entry = map.GetEntryOrDefault(hash); - return entry != null - ? key.Equals(entry.Value.Key) ? entry : GetConflictedEntryOrDefault(entry, key) - : null; - } - - /// Returns the value if key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static object GetValueOrDefault(this ImMap> map, int hash, K key) => - map.GetEntryOrDefault(hash, key)?.Value.Value; - - /// Sets the value if key is found or returns false otherwise. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImMap> map, int hash, K key, out object value) - { - var entry = map.GetEntryOrDefault(hash, key); - if (entry != null) - { - value = entry.Value.Value; - return true; - } - - value = null; - return false; - } - - /// Returns the entry if key is found or `null` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry> GetEntryOrDefault(this ImMap> map, int hash, Type type) - { - var entry = map.GetEntryOrDefault(hash); - return entry != null - ? entry.Value.Key == type ? entry : GetConflictedEntryOrDefault(entry, type) - : null; - } - - /// Returns the value if the Type key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static object GetValueOrDefault(this ImMap> map, int hash, Type typeKey) => - map.GetEntryOrDefault(hash, typeKey)?.Value.Value; - - /// Returns the value if the Type key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static object GetValueOrDefault(this ImMap> map, Type typeKey) => - map.GetEntryOrDefault(RuntimeHelpers.GetHashCode(typeKey), typeKey)?.Value.Value; - - internal static ImMapEntry> GetConflictedEntryOrDefault(ImMapEntry> entry, K key) - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Value.Key)) - return conflicts[i]; - return null; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// - public static IEnumerable>> Enumerate(this ImMap> map) - { - foreach (var entry in map.Enumerate(null)) - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - yield return conflicts[i]; - else - yield return entry; - } - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public static S Fold(this ImMap> map, - S state, Func>, S, S> reduce, ImMapTree>[] parentsStack = null) => - map.Fold(state, reduce, (entry, s, r) => - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - s = r(conflicts[i], s); - else - s = r(entry, s); - return s; - }, - parentsStack); - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public static S Visit(this ImMap> map, - S state, Action>, S> effect, ImMapTree>[] parentsStack = null) => - map.Fold(state, effect, (entry, s, eff) => - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - eff(conflicts[i], s); - else - eff(entry, s); - return s; - }, - parentsStack); - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public static void Visit(this ImMap> map, - Action>> effect, ImMapTree>[] parentsStack = null) => - map.Fold(false, effect, (entry, s, eff) => - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - eff(conflicts[i]); - else - eff(entry); - return false; - }, - parentsStack); - } - - /// - /// The array of ImMap slots where the key first bits are used for FAST slot location - /// and the slot is the reference to ImMap that can be swapped with its updated value - /// - public static class ImMapSlots - { - /// Default number of slots - public const int SLOT_COUNT_POWER_OF_TWO = 32; - - /// The default mask to partition the key to the target slot - public const int KEY_MASK_TO_FIND_SLOT = SLOT_COUNT_POWER_OF_TWO - 1; - - /// Creates the array with the empty slots - [MethodImpl((MethodImplOptions)256)] - public static ImMap[] CreateWithEmpty(int slotCountPowerOfTwo = SLOT_COUNT_POWER_OF_TWO) - { - var slots = new ImMap[slotCountPowerOfTwo]; - for (var i = 0; i < slots.Length; ++i) - slots[i] = ImMap.Empty; - return slots; - } - - /// Returns a new tree with added or updated value for specified key. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImMap[] slots, int key, V value, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[key & keyMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(key, value), copy) != copy) - RefAddOrUpdateSlot(ref slot, key, value); - } - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrUpdateSlot(ref ImMap slot, int key, V value) => - Ref.Swap(ref slot, key, value, (x, k, v) => x.AddOrUpdate(k, v)); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImMap[] slots, int key, V value, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[key & keyMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(key, value), copy) != copy) - RefAddOrKeepSlot(ref slot, key, value); - } - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrKeepSlot(ref ImMap slot, int key, V value) => - Ref.Swap(ref slot, key, value, (s, k, v) => s.AddOrKeep(k, v)); - - /// Adds a default value entry for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImMap[] slots, int key, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[key & keyMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(key), copy) != copy) - RefAddOrKeepSlot(ref slot, key); - } - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrKeepSlot(ref ImMap slot, int key) => - Ref.Swap(ref slot, key, (s, k) => s.AddOrKeep(k)); - - /// Folds all map nodes without the order - public static S Fold(this ImMap[] slots, S state, Func, S, S> reduce) - { - var parentStack = ArrayTools.Empty>(); - for (var i = 0; i < slots.Length; ++i) - { - var map = slots[i]; - if (map == ImMap.Empty) - continue; - - if (map is ImMapEntry leaf) - state = reduce(leaf, state); - else if (map is ImMapBranch branch) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - } - else - { - if (parentStack.Length < tree.TreeHeight - 2) - parentStack = new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - state = reduce((ImMapEntry)map, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - } - } - } - } - - return state; - } - } - - /// Wraps the stored data with "fixed" reference semantics - when added to the tree it did not change or reconstructed in memory - public class ImHashMapEntry - { - /// Empty thingy - public static readonly ImHashMapEntry Empty = new ImHashMapEntry(); - - /// Key hash - public readonly int Hash; - - /// The key - public readonly K Key; - - /// The value - may be mutated implementing the Ref CAS semantics if needed - public V Value; - - private ImHashMapEntry() { } - - /// Constructs the data - public ImHashMapEntry(int hash, K key, V value) - { - Hash = hash; - Key = key; - Value = value; - } - - /// Constructs the data with the default value - public ImHashMapEntry(int hash, K key) - { - Hash = hash; - Key = key; - } - - /// Outputs the brief tree info - mostly for debugging purposes - public override string ToString() => Key + ": " + Value; - } - - /// Stores ALL the data in `Conflicts` array, the fields except the `hash` are just fillers. - /// This way we preserve the once created `ImHashMapData` so that client can hold the reference to it and update the Value if needed. - public sealed class ImHashMapConflicts : ImHashMapEntry - { - /// Conflicted data - public readonly ImHashMapEntry[] Conflicts; - - /// - public ImHashMapConflicts(int hash, params ImHashMapEntry[] conflicts) : base(hash, default, default) => - Conflicts = conflicts; - } - - /// Immutable http://en.wikipedia.org/wiki/AVL_tree - /// where node key is the hash code of - public sealed class ImHashMap - { - /// Empty map to start with. - public static readonly ImHashMap Empty = new ImHashMap(); - - /// Calculated key hash. - public int Hash - { - [MethodImpl((MethodImplOptions)256)] - get => Entry.Hash; - } - - /// Key of type K that should support and . - public K Key - { - [MethodImpl((MethodImplOptions)256)] - get => Entry.Key; - } - - /// Value of any type V. - public V Value - { - [MethodImpl((MethodImplOptions)256)] - get => Entry.Value; - } - - /// In case of conflicts for different keys contains conflicted keys with their values. - public ImHashMapEntry[] Conflicts - { - [MethodImpl((MethodImplOptions)256)] - get => (Entry as ImHashMapConflicts)?.Conflicts; - } - - /// Left sub-tree/branch, or empty. - public ImHashMap Left; - - /// Right sub-tree/branch, or empty. - public ImHashMap Right; - - /// Height of longest sub-tree/branch plus 1. It is 0 for empty tree, and 1 for single node tree. - public int Height; - - /// Returns true if tree is empty. - public bool IsEmpty => Height == 0; - - /// The entry which is allocated once and can be used as a "fixed" reference to the Key and Value - public readonly ImHashMapEntry Entry; - - internal ImHashMap() => Entry = ImHashMapEntry.Empty; - - /// Creates leaf node - public ImHashMap(int hash, K key, V value) - { - Entry = new ImHashMapEntry(hash, key, value); - Left = Empty; - Right = Empty; - Height = 1; - } - - /// Creates a leaf node with default value - public ImHashMap(int hash, K key) - { - Entry = new ImHashMapEntry(hash, key); - Left = Empty; - Right = Empty; - Height = 1; - } - - /// Creates a leaf node - public ImHashMap(ImHashMapEntry entry) - { - Entry = entry; - Left = Empty; - Right = Empty; - Height = 1; - } - - /// Creates the tree and calculates the height for you - public ImHashMap(ImHashMapEntry entry, ImHashMap left, ImHashMap right) - { - Entry = entry; - Left = left; - Right = right; - Height = 1 + (left.Height > right.Height ? left.Height : right.Height); - } - - /// Creates the tree with the known height - public ImHashMap(ImHashMapEntry entry, ImHashMap left, ImHashMap right, int height) - { - Entry = entry; - Left = left; - Right = right; - Height = height; - } - - /// Outputs the brief tree info - mostly for debugging purposes - public override string ToString() => Height == 0 ? "empty" - : "(" + Entry - + ") -> (" + (Left.Height == 0 ? "empty" : Left.Entry + " of height " + Left.Height) - + ", " + (Right.Height == 0 ? "empty" : Right.Entry + " of height " + Right.Height) - + ")"; - - /// Uses the user provided hash and adds and updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(int hash, K key, V value) => - Height == 0 ? new ImHashMap(hash, key, value) - : hash == Hash ? UpdateValueOrAddOrUpdateConflict(hash, key, value) - : AddOrUpdateLeftOrRight(hash, key, value); - - /// Adds and updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(K key, V value) => - AddOrUpdate(key.GetHashCode(), key, value); - - private ImHashMap UpdateValueOrAddOrUpdateConflict(int hash, K key, V value) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(key, Key) || key.Equals(Key)) - ? new ImHashMap(new ImHashMapEntry(hash, key, value), Left, Right, Height) - : AddOrUpdateConflict(conflictsData, hash, key, value); - } - - internal enum DoAddOrUpdateConflicts { AddOrUpdate, AddOrKeep, Update } - - private ImHashMap AddOrUpdateConflict(ImHashMapConflicts conflictsData, int hash, K key, V value, - Update update = null, DoAddOrUpdateConflicts doWhat = DoAddOrUpdateConflicts.AddOrUpdate) - { - if (conflictsData == null) - return doWhat == DoAddOrUpdateConflicts.Update - ? this - : new ImHashMap( - new ImHashMapConflicts(hash, Entry, new ImHashMapEntry(hash, key, value)), - Left, Right, Height); - - var conflicts = conflictsData.Conflicts; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) - --conflictIndex; - - ImHashMapEntry[] newConflicts; - if (conflictIndex != -1) - { - if (doWhat == DoAddOrUpdateConflicts.AddOrKeep) - return this; - - // update the existing conflicted value - newConflicts = new ImHashMapEntry[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - var newValue = update == null ? value : update(key, conflicts[conflictIndex].Value, value); - newConflicts[conflictIndex] = new ImHashMapEntry(hash, key, newValue); - } - else - { - if (doWhat == DoAddOrUpdateConflicts.Update) - return this; - - // add the new conflicting value - newConflicts = new ImHashMapEntry[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = new ImHashMapEntry(hash, key, value); - } - - return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); - } - - private ImHashMap AddOrUpdateLeftOrRight(int hash, K key, V value) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); - - if (Left.Hash == hash) - return new ImHashMap(Entry, Left.UpdateValueOrAddOrUpdateConflict(hash, key, value), Right, Height); - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, - new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrUpdateLeftOrRight(hash, key, value); - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); - - if (Right.Hash == hash) - return new ImHashMap(Entry, Left, Right.UpdateValueOrAddOrUpdateConflict(hash, key, value), Height); - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); - } - - var right = Right.AddOrUpdateLeftOrRight(hash, key, value); - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - private ImHashMap BalanceNewLeftTree(ImHashMap newLeftTree) - { - var leftLeft = newLeftTree.Left; - var leftLeftHeight = leftLeft.Height; - - var leftRight = newLeftTree.Right; - var leftRightHeight = leftRight.Height; - - if (leftRightHeight > leftLeftHeight) - { - newLeftTree.Right = leftRight.Left; - newLeftTree.Height = leftLeftHeight + 1; - return new ImHashMap(leftRight.Entry, - newLeftTree, - new ImHashMap(Entry, leftRight.Right, Right, Right.Height + 1), - leftLeftHeight + 2); - - //return new ImHashMap(leftRight.Entry, - // new ImHashMap(newLeftTree.Entry, leftLeft, leftRight.Left), - // new ImHashMap(Entry, leftRight.Right, Right)); - } - - newLeftTree.Right = new ImHashMap(Entry, leftRight, Right, leftRightHeight + 1); - newLeftTree.Height = leftRightHeight + 2; - return newLeftTree; - - //return new ImHashMap(newLeftTree.Entry, - // leftLeft, new ImHashMap(Entry, leftRight, Right)); - } - - // Note that Left is by 2 less deep than `newRightTree` - means that at `newRightTree.Left/Right` is at least of Left height or deeper - private ImHashMap BalanceNewRightTree(ImHashMap newRightTree) - { - var rightLeft = newRightTree.Left; - var rightLeftHeight = rightLeft.Height; - - var rightRight = newRightTree.Right; - var rightRightHeight = rightRight.Height; - - if (rightLeftHeight > rightRightHeight) // 1 greater - not 2 greater because it would be too unbalanced - { - newRightTree.Left = rightLeft.Right; - // the height now should be defined by rr - because left now is shorter by 1 - newRightTree.Height = rightRightHeight + 1; - // the whole height consequentially can be defined by `newRightTree` (rr+1) because left is consist of short Left and -2 rl.Left - return new ImHashMap(rightLeft.Entry, - // Left should be >= rightLeft.Left because it maybe rightLeft.Right which defines rl height - new ImHashMap(Entry, Left, rightLeft.Left, height: Left.Height + 1), - newRightTree, rightRightHeight + 2); - - //return new ImHashMap(rightLeft.Entry, - // new ImHashMap(Entry, Left, rightLeft.Left), - // new ImHashMap(newRightTree.Entry, rightLeft.Right, rightRight)); - } - - // we may decide on the height because the Left smaller by 2 - newRightTree.Left = new ImHashMap(Entry, Left, rightLeft, rightLeftHeight + 1); - // if rr was > rl by 1 than new rl+1 should be equal height to rr now, if rr was == rl than new rl wins anyway - newRightTree.Height = rightLeftHeight + 2; - return newRightTree; - - //return new ImHashMap(newRightTree.Entry, new ImHashMap(Entry, Left, rightLeft), rightRight); - } - - /// Uses the user provided hash and adds and updates the tree with passed key-value and the update function for the existing value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(int hash, K key, V value, Update update) => - Height == 0 ? new ImHashMap(hash, key, value) - : hash == Hash ? UpdateValueOrAddOrUpdateConflict(hash, key, value, update) - : AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); - - private ImHashMap UpdateValueOrAddOrUpdateConflict(int hash, K key, V value, Update update) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) - ? new ImHashMap(new ImHashMapEntry(hash, key, update(key, Value, value)), Left, Right, Height) - : AddOrUpdateConflict(conflictsData, hash, key, value, update); - } - - private ImHashMap AddOrUpdateLeftOrRightWithUpdate(int hash, K key, V value, Update update) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); - - if (Left.Hash == hash) - return new ImHashMap(Entry, Left.UpdateValueOrAddOrUpdateConflict(hash, key, value, update), Right, Height); - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); - - if (Right.Hash == hash) - return new ImHashMap(Entry, Left, Right.UpdateValueOrAddOrUpdateConflict(hash, key, value, update), Height); - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); - } - - var right = Right.AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - /// Returns a new tree with added or updated key-value. Uses the provided for updating the existing value. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(K key, V value, Update update) => - AddOrUpdate(key.GetHashCode(), key, value, update); - - /// Returns a new tree with added or updated key-value. Uses the provided for updating the existing value. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(K key, V value, Update update) => - AddOrUpdate(key.GetHashCode(), key, value, update.IgnoreKey); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(int hash, K key, V value) => - Height == 0 ? new ImHashMap(hash, key, value) - : hash == Hash ? KeepValueOrAddConflict(hash, key, value) - : AddOrKeepLeftOrRight(hash, key, value); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(K key, V value) => - AddOrKeep(key.GetHashCode(), key, value); - - private ImHashMap KeepValueOrAddConflict(int hash, K key, V value) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) ? this - : AddOrUpdateConflict(conflictsData, hash, key, value, null, DoAddOrUpdateConflicts.AddOrKeep); - } - private ImHashMap AddOrKeepLeftOrRight(int hash, K key, V value) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); - - if (Left.Hash == hash) - { - var leftWithNewConflict = Left.KeepValueOrAddConflict(hash, key, value); - return ReferenceEquals(leftWithNewConflict, Left) ? this - : new ImHashMap(Entry, leftWithNewConflict, Right, Height); - } - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, - new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrKeepLeftOrRight(hash, key, value); - if (ReferenceEquals(left, Left)) - return this; - - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); - - if (Right.Hash == hash) - { - var rightWithNewConflict = Right.KeepValueOrAddConflict(hash, key, value); - return ReferenceEquals(rightWithNewConflict, Right) ? this - : new ImHashMap(Entry, Left, rightWithNewConflict, Height); - } - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); - } - - var right = Right.AddOrKeepLeftOrRight(hash, key, value); - if (ReferenceEquals(right, Right)) - return this; - - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(int hash, K key) => - Height == 0 ? new ImHashMap(hash, key) - : hash == Hash ? KeepValueOrAddConflict(hash, key) - : AddOrKeepLeftOrRight(hash, key); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(K key) => - AddOrKeep(key.GetHashCode(), key); - - private ImHashMap KeepValueOrAddConflict(int hash, K key) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) - ? this : AddOrKeepConflict(conflictsData, hash, key); - } - - private ImHashMap AddOrKeepConflict(ImHashMapConflicts conflictsData, int hash, K key) - { - if (conflictsData == null) - return new ImHashMap( - new ImHashMapConflicts(hash, Entry, new ImHashMapEntry(hash, key)), - Left, Right, Height); - - var conflicts = conflictsData.Conflicts; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) - --conflictIndex; - - if (conflictIndex != -1) - return this; - - // add the new conflicting value - var newConflicts = new ImHashMapEntry[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = new ImHashMapEntry(hash, key); - - return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); - } - - private ImHashMap AddOrKeepLeftOrRight(int hash, K key) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key), Right, 2); - - if (Left.Hash == hash) - { - var leftWithNewConflict = Left.KeepValueOrAddConflict(hash, key); - return ReferenceEquals(leftWithNewConflict, Left) ? this - : new ImHashMap(Entry, leftWithNewConflict, Right, Height); - } - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, - new ImHashMap(hash, key), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrKeepLeftOrRight(hash, key); - if (ReferenceEquals(left, Left)) - return this; - - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key), 2); - - if (Right.Hash == hash) - { - var rightWithNewConflict = Right.KeepValueOrAddConflict(hash, key); - return ReferenceEquals(rightWithNewConflict, Right) ? this - : new ImHashMap(Entry, Left, rightWithNewConflict, Height); - } - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key), 2); - } - - var right = Right.AddOrKeepLeftOrRight(hash, key); - if (ReferenceEquals(right, Right)) - return this; - - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - public ImHashMap Update(int hash, K key, V value, Update update = null) - { - if (Height == 0) - return this; - - // No need to balance cause we not adding or removing nodes - if (hash < Hash) - { - var left = Left.Update(hash, key, value, update); - return ReferenceEquals(left, Left) ? this : new ImHashMap(Entry, left, Right, Height); - } - - if (hash > Hash) - { - var right = Right.Update(hash, key, value, update); - return ReferenceEquals(right, Right) ? this : new ImHashMap(Entry, Left, right, Height); - } - - var conflictsData = Entry as ImHashMapConflicts; - if (conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key))) - return new ImHashMap( - new ImHashMapEntry(hash, key, update == null ? value : update(key, Value, value)), - Left, Right, Height); - - return AddOrUpdateConflict(conflictsData, hash, key, value, update, DoAddOrUpdateConflicts.Update); - } - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap Update(K key, V value) => - Update(key.GetHashCode(), key, value); - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap Update(K key, V value, Update update) => - Update(key.GetHashCode(), key, value, update.IgnoreKey); - - /// Updates the map with the Default (null for reference types) value if key is found, otherwise returns the same unchanged map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap UpdateToDefault(int hash, K key) - { - if (Height == 0) - return this; - - // No need to balance cause we not adding or removing nodes - if (hash < Hash) - { - var left = Left.UpdateToDefault(hash, key); - return left == Left ? this : new ImHashMap(Entry, left, Right, Height); - } - - if (hash > Hash) - { - var right = Right.UpdateToDefault(hash, key); - return right == Right ? this : new ImHashMap(Entry, Left, right, Height); - } - - var conflictsData = Entry as ImHashMapConflicts; - if (conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key))) - return new ImHashMap(new ImHashMapEntry(hash, key), Left, Right, Height); - - return UpdateConflictToDefault(conflictsData, hash, key); - } - - private ImHashMap UpdateConflictToDefault(ImHashMapConflicts conflictsData, int hash, K key) - { - if (conflictsData == null) - return this; - - var conflicts = conflictsData.Conflicts; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) - --conflictIndex; - - if (conflictIndex == -1) - return this; - - // update the existing conflicted value - var newConflicts = new ImHashMapEntry[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictIndex] = new ImHashMapEntry(hash, key); - return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// - public IEnumerable> Enumerate() - { - if (Height != 0) - { - var parents = new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parents[++parentCount] = node; - node = node.Left; - } - else - { - node = parents[parentCount--]; - if (node.Entry is ImHashMapConflicts conflictsData) - { - var conflicts = conflictsData.Conflicts; - for (var i = 0; i < conflicts.Length; i++) - yield return conflicts[i]; - } - else - { - yield return node.Entry; - } - - node = node.Right; - } - } - } - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public S Fold(S state, Func, S, S> reduce, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - return reduce(Entry, state); - - if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - state = reduce(node.Entry, state); - else - { - var conflict = conflicts.Conflicts; - for (var i = 0; i < conflict.Length; i++) - state = reduce(conflict[i], state); - } - - node = node.Right; - } - } - } - - return state; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public S Fold(S state, Func, int, S, S> reduce, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - return reduce(Entry, 0, state); - - if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var index = 0; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - state = reduce(node.Entry, index++, state); - else - { - var conflictData = conflicts.Conflicts; - for (var i = 0; i < conflictData.Length; i++) - state = reduce(conflictData[i], index++, state); - } - - node = node.Right; - } - } - } - - return state; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public S Visit(S state, Action, S> effect, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - { - effect(Entry, state); - } - else if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - effect(node.Entry, state); - else - { - var conflict = conflicts.Conflicts; - for (var i = 0; i < conflict.Length; i++) - effect(conflict[i], state); - } - - node = node.Right; - } - } - } - - return state; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public void Visit(Action> effect, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - effect(Entry); - else if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - effect(node.Entry); - else - { - var conflict = conflicts.Conflicts; - for (var i = 0; i < conflict.Length; i++) - effect(conflict[i]); - } - - node = node.Right; - } - } - } - } - - /// Finds the first entry matching the condition, returns `null` if not found - public ImHashMapEntry FindFirstOrDefault(Func, bool> condition, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - { - if (condition(Entry)) - return Entry; - } - else if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - { - if (condition(node.Entry)) - return node.Entry; - } - else - { - var conflictedEntries = conflicts.Conflicts; - for (var i = 0; i < conflictedEntries.Length; i++) - if (condition(conflictedEntries[i])) - return conflictedEntries[i]; - } - - node = node.Right; - } - } - } - - return null; - } - - /// Removes or updates value for specified key, or does nothing if the key is not found (returns the unchanged map) - /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx - public ImHashMap Remove(int hash, K key) => - RemoveImpl(hash, key); - - /// Removes or updates value for specified key, or does nothing if the key is not found (returns the unchanged map) - /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx - [MethodImpl((MethodImplOptions)256)] - public ImHashMap Remove(K key) => - RemoveImpl(key.GetHashCode(), key); - - private ImHashMap RemoveImpl(int hash, K key, bool ignoreKey = false) - { - if (Height == 0) - return this; - - ImHashMap result; - if (hash == Hash) // found node - { - if (ignoreKey || Equals(Key, key)) - { - if (Height == 1) // remove node - return Empty; - - if (Right.IsEmpty) - result = Left; - else if (Left.IsEmpty) - result = Right; - else - { - // we have two children, so remove the next highest node and replace this node with it. - var next = Right; - while (!next.Left.IsEmpty) - next = next.Left; - result = new ImHashMap(next.Entry, Left, Right.RemoveImpl(next.Hash, default, ignoreKey: true)); - } - } - else if (Entry is ImHashMapConflicts conflictsData) - return TryRemoveConflicted(conflictsData, hash, key); - else - return this; // if key is not matching and no conflicts to lookup - just return - } - else - result = hash < Hash - ? Balance(Entry, Left.RemoveImpl(hash, key, ignoreKey), Right) - : Balance(Entry, Left, Right.RemoveImpl(hash, key, ignoreKey)); - - return result; - } - - /// Searches for the key in the conflicts and returns true if found - public bool ContainsConflictedData(K key) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Key)) - return true; - } - return false; - } - - /// Searches for the key in the node conflicts - public ImHashMapEntry GetConflictedEntryOrDefault(K key) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Key)) - return conflicts[i]; - } - return null; - } - - /// Searches for the key in the node conflicts - public V GetConflictedValueOrDefault(K key, V defaultValue) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Key)) - return conflicts[i].Value; - } - return defaultValue; - } - - /// Searches for the key in the node conflicts - public bool TryFindConflictedValue(K key, out V value) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (Equals(conflicts[i].Key, key)) - { - value = conflicts[i].Value; - return true; - } - } - value = default; - return false; - } - - // todo: implement in terms of BalanceNewLeftTree | BalanceNewRightTree - private static ImHashMap Balance(ImHashMapEntry entry, ImHashMap left, ImHashMap right) - { - var delta = left.Height - right.Height; - if (delta > 1) // left is longer by 2, rotate left - { - var leftLeft = left.Left; - var leftRight = left.Right; - if (leftRight.Height > leftLeft.Height) - { - // double rotation: - // 5 => 5 => 4 - // 2 6 4 6 2 5 - // 1 4 2 3 1 3 6 - // 3 1 - return new ImHashMap(leftRight.Entry, - new ImHashMap(left.Entry, leftLeft, leftRight.Left), - new ImHashMap(entry, leftRight.Right, right)); - } - - // one rotation: - // 5 => 2 - // 2 6 1 5 - // 1 4 4 6 - return new ImHashMap(left.Entry, - leftLeft, new ImHashMap(entry, leftRight, right)); - } - - if (delta < -1) - { - var rightLeft = right.Left; - var rightRight = right.Right; - return rightLeft.Height > rightRight.Height - ? new ImHashMap(rightLeft.Entry, - new ImHashMap(entry, left, rightLeft.Left), - new ImHashMap(right.Entry, rightLeft.Right, rightRight)) - : new ImHashMap(right.Entry, new ImHashMap(entry, left, rightLeft), rightRight); - } - - return new ImHashMap(entry, left, right); - } - - private ImHashMap TryRemoveConflicted(ImHashMapConflicts conflictsData, int hash, K key) - { - var conflicts = conflictsData.Conflicts; - var index = conflicts.Length - 1; - while (index != -1 && !conflicts[index].Key.Equals(key)) --index; - if (index == -1) // key is not found in conflicts - just return - return this; - - // we removing the one from the 2 items, so we can reference the remaining item directly from the map node - if (conflicts.Length == 2) - return new ImHashMap(index == 0 ? conflicts[1] : conflicts[0], Left, Right, Height); - - // copy all except the `index`ed data into shrinked conflicts - var shrinkedConflicts = new ImHashMapEntry[conflicts.Length - 1]; - var newIndex = 0; - for (var i = 0; i < conflicts.Length; ++i) - if (i != index) - shrinkedConflicts[newIndex++] = conflicts[i]; - return new ImHashMap(new ImHashMapConflicts(hash, shrinkedConflicts), Left, Right, Height); - } - } - - /// ImHashMap methods for faster performance - public static class ImHashMap - { - internal static V IgnoreKey(this Update update, K _, V oldValue, V newValue) => update(oldValue, newValue); - - /// Looks for key in a tree and returns `true` if found. - [MethodImpl((MethodImplOptions)256)] - public static bool Contains(this ImHashMap map, int hash, K key) - { - while (map.Height != 0 && map.Hash != hash) - map = hash < map.Hash ? map.Left : map.Right; - return map.Height != 0 && (key.Equals(map.Key) || map.ContainsConflictedData(key)); - } - - /// Looks for key in a tree and returns `true` if found. - [MethodImpl((MethodImplOptions)256)] - public static bool Contains(this ImHashMap map, K key) => - map.Height != 0 && map.Contains(key.GetHashCode(), key); - - /// Looks for key in a tree and returns the Data object if found or `null` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImHashMapEntry GetEntryOrDefault(this ImHashMap map, int hash, K key) - { - while (map.Height != 0 && map.Hash != hash) - map = hash < map.Hash ? map.Left : map.Right; - - return map.Height == 0 ? null : - key.Equals(map.Key) ? map.Entry : - map.GetConflictedEntryOrDefault(key); - } - - /// Looks for key in a tree and returns the Data object if found or `null` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImHashMapEntry GetEntryOrDefault(this ImHashMap map, K key) - { - if (map.Height == 0) - return null; - - var hash = key.GetHashCode(); - - while (map.Hash != hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return null; - } - - return key.Equals(map.Key) ? map.Entry : map.GetConflictedEntryOrDefault(key); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, K key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - var hash = key.GetHashCode(); - - while (map.Hash != hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - return key.Equals(map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, int hash, K key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - while (map.Hash != hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - return key.Equals(map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, Type key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - var hash = RuntimeHelpers.GetHashCode(key); - while (hash != map.Hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - // we don't need to check `Height != 0` again cause in that case `key` will be `null` and `ReferenceEquals` will fail - return ReferenceEquals(key, map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, int hash, Type key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - while (hash != map.Hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - // we don't need to check `Height != 0` again cause in that case `key` will be `null` and `ReferenceEquals` will fail - return ReferenceEquals(key, map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, K key, out V value) - { - if (map.Height != 0) - { - var hash = key.GetHashCode(); - - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - if (key.Equals(map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, int hash, K key, out V value) - { - if (map.Height != 0) - { - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - if (key.Equals(map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Returns true if key is found and the result value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, Type key, out V value) - { - if (map.Height != 0) - { - var hash = RuntimeHelpers.GetHashCode(key); - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - // assign to `var data = ...` - if (ReferenceEquals(key, map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Returns true if hash and key are found and the result value, or the false otherwise - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, int hash, Type key, out V value) - { - if (map.Height != 0) - { - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - if (ReferenceEquals(key, map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Uses `RuntimeHelpers.GetHashCode()` - public static ImHashMap AddOrUpdate(this ImHashMap map, Type key, V value) => - map.AddOrUpdate(RuntimeHelpers.GetHashCode(key), key, value); - } - - /// The array of ImHashMap slots where the key first bits are used for FAST slot location - /// and the slot is the reference to ImHashMap that can be swapped with its updated value - public static class ImHashMapSlots - { - /// Default number of slots - public const int SLOT_COUNT_POWER_OF_TWO = 32; - - /// The default mask to partition the key to the target slot - public const int HASH_MASK_TO_FIND_SLOT = SLOT_COUNT_POWER_OF_TWO - 1; - - /// Creates the array with the empty slots - [MethodImpl((MethodImplOptions)256)] - public static ImHashMap[] CreateWithEmpty(int slotCountPowerOfTwo = SLOT_COUNT_POWER_OF_TWO) - { - var slots = new ImHashMap[slotCountPowerOfTwo]; - for (var i = 0; i < slots.Length; ++i) - slots[i] = ImHashMap.Empty; - return slots; - } - - /// Returns a new tree with added or updated value for specified key. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(hash, key, value), copy) != copy) - RefAddOrUpdateSlot(ref slot, hash, key, value); - } - - /// Returns a new tree with added or updated value for specified key. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.AddOrUpdate(key.GetHashCode(), key, value, hashMaskToFindSlot); - - /// Updates the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrUpdateSlot(ref ImHashMap slot, int hash, K key, V value) => - Ref.Swap(ref slot, hash, key, value, (x, h, k, v) => x.AddOrUpdate(h, k, v)); - - /// Updates the value with help of `updateValue` function - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, int hash, K key, V value, Update update, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(hash, key, value, update), copy) != copy) - RefAddOrUpdateSlot(ref slot, hash, key, value, update); - } - - /// Updates the value with help of `updateValue` function - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, K key, V value, Update updateValue, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.AddOrUpdate(key.GetHashCode(), key, value, updateValue, hashMaskToFindSlot); - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrUpdateSlot(ref ImHashMap slot, int hash, K key, V value, Update update) => - Ref.Swap(ref slot, hash, key, value, (x, h, k, v) => x.AddOrUpdate(h, k, v, update)); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(hash, key, value), copy) != copy) - RefAddOrKeepSlot(ref slot, hash, key, value); - } - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.AddOrKeep(key.GetHashCode(), key, value, hashMaskToFindSlot); - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrKeepSlot(ref ImHashMap slot, int hash, K key, V value) => - Ref.Swap(ref slot, hash, key, value, (s, h, k, v) => s.AddOrKeep(h, k, v)); - - /// Updates the specified slot or does not change it - [MethodImpl((MethodImplOptions)256)] - public static void Update(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.Update(hash, key, value), copy) != copy) - RefUpdateSlot(ref slot, hash, key, value); - } - - /// Updates the specified slot or does not change it - [MethodImpl((MethodImplOptions)256)] - public static void Update(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.Update(key.GetHashCode(), key, value, hashMaskToFindSlot); - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefUpdateSlot(ref ImHashMap slot, int hash, K key, V value) => - Ref.Swap(ref slot, key, value, (s, k, v) => s.Update(k, v)); - - /// Returns all map tree nodes without the order - public static S Fold(this ImHashMap[] slots, S state, Func, S, S> reduce) - { - var parentStack = ArrayTools.Empty>(); - for (var s = 0; s < slots.Length; s++) - { - var map = slots[s]; - var height = map.Height; - if (height != 0) - { - if (height > 1 && parentStack.Length < height) - parentStack = new ImHashMap[height]; - state = map.Fold(state, reduce, parentStack); - } - } - - return state; - } - } -} diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/IModule.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/IModule.cs deleted file mode 100644 index bb464a2..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/IModule.cs +++ /dev/null @@ -1,11 +0,0 @@ - -using DryIoc; - -namespace BattleFieldSimulator.Utilities -{ - public interface IModule - { - void Register(IContainer container); - void Resolve(IContainer container); - } -} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/Properties/AssemblyInfo.cs b/BattleFieldSimulator/BattleFieldSimulator.Utilities/Properties/AssemblyInfo.cs deleted file mode 100644 index edc5761..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("BattleFieldSimulator.Utilities")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("BattleFieldSimulator.Utilities")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("F65ECA33-08FF-4797-8E53-B086320214F4")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator.Utilities/packages.config b/BattleFieldSimulator/BattleFieldSimulator.Utilities/packages.config deleted file mode 100644 index 1aff88b..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator.Utilities/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattleFieldSimulator.csproj b/BattleFieldSimulator/BattleFieldSimulator/BattleFieldSimulator.csproj index bb897eb..ab19920 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/BattleFieldSimulator.csproj +++ b/BattleFieldSimulator/BattleFieldSimulator/BattleFieldSimulator.csproj @@ -40,10 +40,27 @@ + - + + + + + + + + + + + + + + + + + @@ -51,7 +68,9 @@ - + + + @@ -64,11 +83,21 @@ + + + + + PreserveNewest + + + PreserveNewest + + diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattleFieldSimulator.csproj.DotSettings b/BattleFieldSimulator/BattleFieldSimulator/BattleFieldSimulator.csproj.DotSettings new file mode 100644 index 0000000..67e7d6b --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattleFieldSimulator.csproj.DotSettings @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnviornment/Map.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnviornment/Map.cs deleted file mode 100644 index e1fc12b..0000000 --- a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnviornment/Map.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using BattleFieldSimulator.JsonSerialization; - -namespace BattleFieldSimulator.BattlefieldEnviornment -{ - public class Map - { - public List> Grid { get; } - public int X { get; } - public int Y { get; } - public Map(int x, int y, IEnumerable grid) - { - X = x; - Y = y; - Grid = ConvertGrid(grid); - } - - private List> ConvertGrid(IEnumerable grid) - { - var r_grid = new List>(); - var index = 0; - var jsonObjects = grid.ToList(); - for (var i = 0; i < X; i++) - { - for (var j = 0; j < Y; j++) - { - r_grid[i][j] = int.Parse(jsonObjects[index].ToString()); - index++; - } - } - - return r_grid; - } - } -} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Environment.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Environment.cs new file mode 100644 index 0000000..8d532f8 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Environment.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class Environment : IEnvironment + { + public IMap Map { get; } + public List Allies { get; } + public List Adversaries { get; } + public Environment(IMap map, List allies, List adversaries) + { + Map = map; + Allies = allies; + Adversaries = adversaries; + } + + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/EnvironmentFactory.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/EnvironmentFactory.cs new file mode 100644 index 0000000..bf88373 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/EnvironmentFactory.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class EnvironmentFactory : IEnvironmentFactory + { + + + public Environment CreateEnvironment(IMap map, List allies, List adversaries) + { + return new Environment(map, allies, adversaries); + } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/EnvironmentSetup.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/EnvironmentSetup.cs new file mode 100644 index 0000000..e0395a2 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/EnvironmentSetup.cs @@ -0,0 +1,25 @@ +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class EnvironmentSetup : IEnvironmentSetup + { + private readonly IMapLoader _mapLoader; + private readonly IEnvironmentFactory _environmentFactory; + private ITroopLoader _troopLoader; + + public EnvironmentSetup(IMapLoader mapLoader, IEnvironmentFactory environmentFactory, ITroopLoader troopLoader) + { + _mapLoader = mapLoader; + _environmentFactory = environmentFactory; + _troopLoader = troopLoader; + } + + public Environment Setup(string mapName, string troopFile) + { + var map = _mapLoader.LoadMap(mapName); + var allies = _troopLoader.LoadAllies(troopFile); + var adversaries = _troopLoader.LoadAdversaries(troopFile); + var env = _environmentFactory.CreateEnvironment(map, allies, adversaries); + return env; + } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironment.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironment.cs new file mode 100644 index 0000000..850f679 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironment.cs @@ -0,0 +1,12 @@ + +using System.Collections.Generic; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public interface IEnvironment + { + IMap Map { get; } + List Allies { get; } + List Adversaries { get; } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironmentFactory.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironmentFactory.cs new file mode 100644 index 0000000..c01a46c --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironmentFactory.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public interface IEnvironmentFactory + { + Environment CreateEnvironment(IMap map, List allies, List adversaries); + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironmentSetup.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironmentSetup.cs new file mode 100644 index 0000000..3254e64 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/IEnvironmentSetup.cs @@ -0,0 +1,7 @@ +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public interface IEnvironmentSetup + { + Environment Setup(string mapName, string troopFile); + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/IMap.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/IMap.cs new file mode 100644 index 0000000..0f6090c --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/IMap.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public interface IMap + { + List> Grid { get; } + int X { get; } + int Y { get; } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/IMapLoader.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/IMapLoader.cs new file mode 100644 index 0000000..a2955d9 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/IMapLoader.cs @@ -0,0 +1,7 @@ +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public interface IMapLoader + { + IMap LoadMap(string mapName); + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/Map.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/Map.cs new file mode 100644 index 0000000..6885e3a --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/Map.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using BattleFieldSimulator.Exceptions; +using BattleFieldSimulator.JsonSerialization; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class Map : IMap + { + public List> Grid { get; } + public int X { get; } + public int Y { get; } + public Map(int x, int y, List> grid) + { + X = x; + Y = y; + Grid = grid; + ValidateMap(); + } + + private void ValidateMap() + { + if(Grid.Count != X) + throw new InvalidMapException($"X Dimension of map is incorrect expecting: {X} but was: {Grid.Count}"); + if(Grid[0].Count != Y) + throw new InvalidMapException($"Y Dimension of map is incorrect expecting: {Y} but was: {Grid[0].Count}"); + foreach (var row in Grid) + { + foreach (var x in row) + { + if(x > 5 || x < -5) + throw new InvalidMapException($"Map values must be between (-5,5) but value {x} was found."); + } + } + } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/MapLoader.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/MapLoader.cs new file mode 100644 index 0000000..2e125a1 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/MapLoader.cs @@ -0,0 +1,22 @@ +using System.IO; +using BattleFieldSimulator.FileSystem; +using BattleFieldSimulator.JsonSerialization; +using static BattleFieldSimulator.FileSystem.FileSystemConstants; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class MapLoader : IMapLoader + { + private readonly IJsonSerializer _jsonSerializer; + + public MapLoader(IJsonSerializer jsonSerializer) + { + _jsonSerializer = jsonSerializer; + } + + public IMap LoadMap(string mapName) + { + return _jsonSerializer.Deserialize(Path.Combine(MapLocation, mapName)).GetMap("Map"); + } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/Point.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/Point.cs new file mode 100644 index 0000000..ea71489 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Map/Point.cs @@ -0,0 +1,17 @@ +using System.Reflection; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public struct Point + { + public int X; + public int Y; + + public Point(int x, int y) + { + X = x; + Y = y; + } + + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/EffectivenessRatios.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/EffectivenessRatios.cs new file mode 100644 index 0000000..17b475f --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/EffectivenessRatios.cs @@ -0,0 +1,13 @@ +using System; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public struct EffectivenessRatios + { + public const double Aggressiveness = .275; + public const double Size = .4; + public const double MarksmanShip = .3; + public const double Defense = .225; + public const double MovementSpeed = -.2; + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/ITroop.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/ITroop.cs new file mode 100644 index 0000000..3df8c09 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/ITroop.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class ITroop + { + int SightDistance { get;} + int EngagementDistance { get;} + int TroopCount { get; } + double MovementSpeed { get; } + double WeaponDamage { get; } + double Marksmanship { get; } + double Aggressiveness { get; } + double Defense { get; } + Queue AssistanceQueue { get; } + Point Objective { get; } + double CurrentSpeed { get; set; } + List IdentifiedEnemy { get; } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/ITroopLoader.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/ITroopLoader.cs new file mode 100644 index 0000000..3fce09e --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/ITroopLoader.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public interface ITroopLoader + { + List LoadAllies(string troopFile); + List LoadAdversaries(string troopFile); + + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/Mission.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/Mission.cs new file mode 100644 index 0000000..69b0ad2 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/Mission.cs @@ -0,0 +1,15 @@ +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum Mission + { + [EnumMember(Value = "Attack")] + Attack = 1, + [EnumMember(Value = "Defend")] + Defend = 0 + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/Troop.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/Troop.cs new file mode 100644 index 0000000..88d0144 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/Troop.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Reflection; +using BattleFieldSimulator.Exceptions; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class Troop + { + public int SightDistance { get; } + public int EngagementDistance { get; } + public int TroopCount { get; } + public Mission Mission { get; } + public Point Location { get; } + public double MovementSpeed { get; } + public double WeaponDamage { get; } + public double Marksmanship { get; } + public double Aggressiveness { get; } + public double Defense { get; } + public List AssistanceQueue { get; } + public Point Objective { get; } + public double CurrentSpeed { get; set; } + public List IdentifiedEnemy { get; } + public int Fatalities { get; set; } + + + + public Troop + ( + double movementSpeed, + int sightDistance, + int engagementDistance, + double weaponDamage, + double marksmanship, + int troopCount, + double aggressiveness, + double defense, + string mission, + Point point, + Point objective + + ) + { + SightDistance = sightDistance; + EngagementDistance = engagementDistance; + TroopCount = troopCount; + MovementSpeed = movementSpeed; + WeaponDamage = weaponDamage; + Marksmanship = marksmanship; + Aggressiveness = aggressiveness; + Defense = defense; + var missionM = mission == "Attack" ? Mission.Attack : Mission.Defend; + Mission = missionM; + Location = point; + CurrentSpeed = 0; + AssistanceQueue = new List(); + Objective = objective; + IdentifiedEnemy = new List(); + Fatalities = 0; + ValidateTroop(); + } + + private void ValidateTroop() + { + if(SightDistance > 10 || SightDistance < 0) + throw new InvalidTroopException($"SightDistance must be between (1,10) but was: {SightDistance}"); + if(EngagementDistance > 10 || EngagementDistance < 0) + throw new InvalidTroopException($"EngagementDistance must be between (1,10) but was: {EngagementDistance}"); + if(MovementSpeed > 10 || MovementSpeed < 0) + throw new InvalidTroopException($"MovementSpeed must be between (0,10) but was: {MovementSpeed}"); + if(WeaponDamage > 10 || WeaponDamage < 0) + throw new InvalidTroopException($"WeaponDamage must be between (1,10) but was: {WeaponDamage}"); + if(Marksmanship > 10 || Marksmanship < 0) + throw new InvalidTroopException($"Marksmanship must be between (1,10) but was: {Marksmanship}"); + if(Aggressiveness > 10 || Aggressiveness < 1) + throw new InvalidTroopException($"Aggressiveness must be between (1,10) but was: {Aggressiveness}"); + if(Defense > 10 || Defense < 0) + throw new InvalidTroopException($"Defense must be between (1,10) but was: {Defense}"); + if(TroopCount<=0) + throw new InvalidTroopException($"TroopCount must be greater than 0 but was: {TroopCount}"); + + } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/TroopLoader.cs b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/TroopLoader.cs new file mode 100644 index 0000000..1bfbd4f --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/BattlefieldEnvironment/Troops/TroopLoader.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.IO; +using BattleFieldSimulator.FileSystem; +using BattleFieldSimulator.JsonSerialization; +using static BattleFieldSimulator.FileSystem.FileSystemConstants; + +namespace BattleFieldSimulator.BattlefieldEnvironment +{ + public class TroopLoader : ITroopLoader + { + private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; + + public TroopLoader(IJsonSerializer jsonSerializer, IFileSystem fileSystem) + { + _jsonSerializer = jsonSerializer; + _fileSystem = fileSystem; + } + public List LoadAllies(string troopFile) + { + var troops = _jsonSerializer.Deserialize(Path.Combine(TroopFileLocation, troopFile)).GetTroop("Allies"); + return troops; + } + + public List LoadAdversaries(string troopFile) + { + var troops = _jsonSerializer.Deserialize(Path.Combine(TroopFileLocation, troopFile)).GetTroop("Adversaries"); + return troops; + } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/ConsoleClient/ConsoleClient.cs b/BattleFieldSimulator/BattleFieldSimulator/ConsoleClient/ConsoleClient.cs index 4c61d84..2b4b177 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/ConsoleClient/ConsoleClient.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/ConsoleClient/ConsoleClient.cs @@ -31,7 +31,7 @@ namespace BattleFieldSimulator.ConsoleClient PrintHelpMenu(); break; case "-r": - _simRunner.RunSimulation(); + _simRunner.RunSimulation("SimpleMap.json", "SimpleTroopFile.json"); break; default: return; diff --git a/BattleFieldSimulator/BattleFieldSimulator/CoreModule.cs b/BattleFieldSimulator/BattleFieldSimulator/CoreModule.cs index 03fd57f..1b88a54 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/CoreModule.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/CoreModule.cs @@ -1,3 +1,6 @@ +using BattleFieldSimulator.BattlefieldEnvironment; +using BattleFieldSimulator.FileSystem; +using BattleFieldSimulator.JsonSerialization; using BattleFieldSimulator.SimRunner; using DryIoc; @@ -7,7 +10,17 @@ namespace BattleFieldSimulator { public void Register(IContainer container) { - container.Register(); + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); +// container.Register<>(Reuse.Singleton); + + } public void Resolve(IContainer container) diff --git a/BattleFieldSimulator/BattleFieldSimulator/Exceptions/IBattleFieldException.cs b/BattleFieldSimulator/BattleFieldSimulator/Exceptions/BattleFieldException.cs similarity index 100% rename from BattleFieldSimulator/BattleFieldSimulator/Exceptions/IBattleFieldException.cs rename to BattleFieldSimulator/BattleFieldSimulator/Exceptions/BattleFieldException.cs diff --git a/BattleFieldSimulator/BattleFieldSimulator/Exceptions/InvalidMapException.cs b/BattleFieldSimulator/BattleFieldSimulator/Exceptions/InvalidMapException.cs new file mode 100644 index 0000000..ce95691 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/Exceptions/InvalidMapException.cs @@ -0,0 +1,11 @@ +namespace BattleFieldSimulator.Exceptions +{ + public class InvalidMapException : BattleFieldException + { + public InvalidMapException(string message) : base(message) + { + } + + public override string Explanation { get; } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/Exceptions/InvalidTroopException.cs b/BattleFieldSimulator/BattleFieldSimulator/Exceptions/InvalidTroopException.cs new file mode 100644 index 0000000..ee092cd --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/Exceptions/InvalidTroopException.cs @@ -0,0 +1,11 @@ +namespace BattleFieldSimulator.Exceptions +{ + public class InvalidTroopException : BattleFieldException + { + public InvalidTroopException(string message) : base(message) + { + } + + public override string Explanation { get; } + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/FileSystem/FileSystemConstants.cs b/BattleFieldSimulator/BattleFieldSimulator/FileSystem/FileSystemConstants.cs index 6d31813..be3ecf7 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/FileSystem/FileSystemConstants.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/FileSystem/FileSystemConstants.cs @@ -1,11 +1,20 @@ using static System.IO.Path; using static System.Environment.SpecialFolder; using static System.Environment; +using static System.Reflection.Assembly; namespace BattleFieldSimulator.FileSystem { public static class FileSystemConstants { + public static readonly string ExecutionDirectory = + GetDirectoryName(GetExecutingAssembly().Location); + public static readonly string BattleFieldSimulatorDirectory = + Combine(GetFolderPath(LocalApplicationData), "BattleFieldSimulator"); public static readonly string LogDirectory = Combine(GetFolderPath(CommonDocuments), "BattlefieldSimulator", "Logs"); + public static readonly string TroopFileLocation = + Combine(ExecutionDirectory, "troops"); + public static readonly string MapLocation = + Combine(ExecutionDirectory, "maps"); } } \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/IJsonObject.cs b/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/IJsonObject.cs index de54be9..0479714 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/IJsonObject.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/IJsonObject.cs @@ -1,7 +1,13 @@ +using System.Collections.Generic; +using BattleFieldSimulator.BattlefieldEnvironment; + namespace BattleFieldSimulator.JsonSerialization { public interface IJsonObject { - + IEnumerable GetJsonArray(string dataIdentifier); + Map GetMap(string dataIdentifier); + List GetTroop(string dataIdentifier); + } } \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/JsonObjectWrapper.cs b/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/JsonObjectWrapper.cs index 14a53cb..3023bc4 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/JsonObjectWrapper.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/JsonSerialization/JsonObjectWrapper.cs @@ -1,9 +1,9 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using BattleFieldSimulator.BattlefieldEnviornment; +using BattleFieldSimulator.BattlefieldEnvironment; using BattleFieldSimulator.Exceptions; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace BattleFieldSimulator.JsonSerialization @@ -41,10 +41,40 @@ namespace BattleFieldSimulator.JsonSerialization return (Map) null; var x = jArray["X"].Value(); var y = jArray["Y"].Value(); - var grid = GetJsonArray("map"); + var grid = JsonConvert.DeserializeObject>>(jArray["Grid"].ToString()); return new Map(x, y, grid); }); + public List GetTroop(string dataIdentifier) => GetData>(dataIdentifier, jArray => + { + var r_List = new List(); + if (!jArray.Any()) + return null; + foreach (var token in jArray.Children()) + { + var troop = token["Troop"]; + var movementSpeed = troop["MovementSpeed"].Value(); + var sightDistance = troop["SightDistance"].Value(); + var engagementDistance = troop["EngagementDistance"].Value(); + var troopCount = troop["TroopCount"].Value(); + var marksmanship = troop["Marksmanship"].Value(); + var weaponDamage = troop["WeaponDamage"].Value(); + var aggressiveness = troop["Aggressiveness"].Value(); + var defense = troop["Defense"].Value(); + var mission = troop["Mission"].Value(); + var entryPointX = troop["EntryPointX"].Value(); + var entryPointY = troop["EntryPointY"].Value(); + var point = new Point(entryPointX, entryPointY); + var objectiveX = troop["ObjectiveX"].Value(); + var objectiveY = troop["ObjectiveY"].Value(); + var objective = new Point(objectiveX, objectiveY); + var r_troop = new Troop(movementSpeed, sightDistance, engagementDistance, weaponDamage, marksmanship, + troopCount, aggressiveness, defense, mission, point, objective); + r_List.Add(r_troop); + } + return r_List; + }); + private T2 GetData(string dataIdentifier, Func convertFunc) { if (_jObject.ContainsKey(dataIdentifier)) diff --git a/BattleFieldSimulator/BattleFieldSimulator/Program.cs b/BattleFieldSimulator/BattleFieldSimulator/Program.cs index 1f92d93..f926af9 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/Program.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/Program.cs @@ -1,4 +1,5 @@ -using BattleFieldSimulator.SimRunner; +using System; +using BattleFieldSimulator.SimRunner; namespace BattleFieldSimulator { @@ -9,7 +10,14 @@ namespace BattleFieldSimulator var bootstrapper = BootStrapper.BootstrapSystem(new CoreModule()); var simRunner = bootstrapper.Resolve(); var consoleClient = new ConsoleClient.ConsoleClient(simRunner); - consoleClient.Run(args); + try + { + consoleClient.Run(args); + } + catch (Exception e) + { + Console.WriteLine(e); + } } } } \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/SimRunner/ISimRunner.cs b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/ISimRunner.cs index da6dfe5..02f1a84 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/SimRunner/ISimRunner.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/ISimRunner.cs @@ -2,6 +2,6 @@ namespace BattleFieldSimulator.SimRunner { public interface ISimRunner { - void RunSimulation(); + void RunSimulation(string mapName, string troopFile); } } \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/SimRunner/ISimulation.cs b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/ISimulation.cs new file mode 100644 index 0000000..63b9848 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/ISimulation.cs @@ -0,0 +1,9 @@ +using BattleFieldSimulator.BattlefieldEnvironment; + +namespace BattleFieldSimulator.SimRunner +{ + public interface ISimulation + { + void Run(IEnvironment environment); + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/SimRunner/SimRunner.cs b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/SimRunner.cs index 585e1a8..3120930 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/SimRunner/SimRunner.cs +++ b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/SimRunner.cs @@ -1,15 +1,30 @@ +using System; +using BattleFieldSimulator.BattlefieldEnvironment; +using DryIoc; +using Environment = BattleFieldSimulator.BattlefieldEnvironment.Environment; + namespace BattleFieldSimulator.SimRunner { public class SimRunner : ISimRunner { - public SimRunner() + private readonly IEnvironmentSetup _environmentSetup; + private readonly ISimulation _simulation; + + public SimRunner(IEnvironmentSetup environmentSetup, ISimulation simulation) { - + _environmentSetup = environmentSetup; + _simulation = simulation; } - public void RunSimulation() + public void RunSimulation(string mapName, string troopFile) { - throw new System.NotImplementedException(); + var environment = Setup(mapName, troopFile); + _simulation.Run(environment); + } + + private Environment Setup(string mapName, string troopFile) + { + return _environmentSetup.Setup(mapName, troopFile); } } } \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/SimRunner/Simulation.cs b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/Simulation.cs new file mode 100644 index 0000000..3f4db69 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/SimRunner/Simulation.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Xml.Schema; +using BattleFieldSimulator.BattlefieldEnvironment; +using BattleFieldSimulator.FileSystem; + +namespace BattleFieldSimulator.SimRunner +{ + public class Simulation : ISimulation + { + public Simulation() + { + + } + + public void Run(IEnvironment environment) + { + var finished = false; + while (!finished) + { + RunRound(environment); + finished = CheckIfFinished(environment.Allies, environment.Adversaries); + } + } + + private void RunRound(IEnvironment environment) + { + var map = environment.Map; + var allies = environment.Allies; + var adversaries = environment.Adversaries; + foreach (var troop in allies) + LookAround(troop, map, adversaries); + + foreach (var troop in adversaries) + LookAround(troop, map, allies); + CheckCallForHelp(allies, adversaries); + var nextTurn = false; + var current = 0; + while (!nextTurn) + { + if (current >= allies.Count && current >= adversaries.Count) + nextTurn = true; + if (current < allies.Count) + Attack(allies[current], allies[current].IdentifiedEnemy); + if (current < adversaries.Count) + Attack(adversaries[current], adversaries[current].IdentifiedEnemy); + if (current < allies.Count) + if (allies[current].TroopCount == allies[current].Fatalities) + allies.Remove(allies[current]); + if (current < adversaries.Count) + if (adversaries[current].TroopCount == adversaries[current].Fatalities) + adversaries.Remove(adversaries[current]); + current++; + } + + + } + + private void CheckCallForHelp(List allies, List adversaries) + { + foreach (var eTroop in from ally in allies from eTroop in ally.IdentifiedEnemy where CalculateAttackValues(eTroop, ally)>CalculateAttackValues(ally, eTroop) select eTroop) + { + CallForHelp(eTroop.Location, allies); + } + foreach (var eTroop in from adversary in adversaries from eTroop in adversary.IdentifiedEnemy where CalculateAttackValues(eTroop, adversary)>CalculateAttackValues(adversary, eTroop) select eTroop) + { + CallForHelp(eTroop.Location, adversaries); + } + } + + private void CallForHelp(Point eTroopLocation, List teams) + { + foreach (var teamMember in teams.Where(teamMember => !teamMember.AssistanceQueue.Contains(eTroopLocation))) + { + teamMember.AssistanceQueue.Add(eTroopLocation); + } + } + + private void LookAround(Troop troop, IMap map, List enemy) + { + var enterX = 0; + var enterY = 0; + var stopX = 0; + var stopY = 0; + //Calculate first point. + if (troop.Location.X - troop.SightDistance < 0) + enterX = 0; + else + enterX = troop.Location.X - troop.SightDistance; + if (troop.Location.Y - troop.SightDistance < 0) + enterY = 0; + else + enterY = troop.Location.Y - troop.SightDistance; + //Calculate last point + if (troop.Location.X + troop.SightDistance > map.X) + stopX = map.X; + else + stopX = troop.Location.X + troop.SightDistance; + if (troop.Location.Y + troop.SightDistance > map.Y) + stopY = map.Y; + else + stopY = troop.Location.Y + troop.SightDistance; + + var nearbyTroops = new List(); + var adjustedTroopSight = CalculateTrueSightDistance(troop); + for (var x = enterX; x < stopX; x++) + { + for (var y = enterY; y < stopY; y++) + { + var currentCell = new Point(x, y); + if (Distance(troop.Location, currentCell) < adjustedTroopSight) + nearbyTroops.AddRange(enemy.Where(q => + q.Location.X == currentCell.X && q.Location.Y == currentCell.Y).ToList()); + + } + } + troop.IdentifiedEnemy.AddRange(VerifyTroopSighting(nearbyTroops, troop, map)); + } + + private double CalculateTrueSightDistance(Troop troop) + { + if (troop.CurrentSpeed == 0) + return troop.SightDistance; + var sightDeprecation = troop.MovementSpeed / troop.CurrentSpeed; + return troop.SightDistance * sightDeprecation; + } + + private List VerifyTroopSighting(List enemyTroops, Troop troop, IMap map) + { + var returnList = new List(); + foreach (var enemy in enemyTroops) + { + var x = Math.Min(troop.Location.X, enemy.Location.X); + var y = Math.Min(troop.Location.Y, enemy.Location.Y); + var done = false; + var canSee = true; + while (!done) + { + var startX = x; + var startY = y; + if (map.Grid[x][y] > map.Grid[troop.Location.X][troop.Location.Y] + 2) + canSee = false; + if (x < Math.Max(troop.Location.X, enemy.Location.X)) + x++; + if (y < Math.Max(troop.Location.Y, enemy.Location.Y)) + y++; + if (startX == x && startY == y) + done = true; + } + + if (canSee) + returnList.Add(enemy); + } + + return returnList; + } + + private void Attack(Troop troop, List enemy) + { + if (troop.TroopCount == troop.Fatalities) + return; + var r = new Random(); + //Each troop can only engage one enemy + var troopsInEngagement = 0; + for(var i = 0; i < enemy.Count; i++) + { + var e = enemy[i]; + var attack = CalculateAttackValues(troop, e); + if (e.TroopCount != e.Fatalities) + { + var startTroopsInEngagement = troopsInEngagement; + var startingEnemyCount = e.TroopCount - e.Fatalities; + var nextEngage = false; + while (!nextEngage) + { + troopsInEngagement++; + if (NextBool(r, attack * 100)) + e.Fatalities++; + if (troopsInEngagement - startTroopsInEngagement == startingEnemyCount && i < enemy.Count) + nextEngage = true; + } + } + } + } + + private double CalculateAttackValues(Troop troop, Troop enemy) + { + var strengthList = new List(); + if(enemy.TroopCount-enemy.Fatalities != 0) + strengthList.Add((troop.TroopCount-troop.Fatalities) / (enemy.TroopCount-enemy.Fatalities)*EffectivenessRatios.Size); + else + strengthList.Add((troop.TroopCount - troop.Fatalities) / double.MinValue*EffectivenessRatios.Size); + strengthList.Add(troop.Aggressiveness / enemy.Aggressiveness*EffectivenessRatios.Aggressiveness); + strengthList.Add(troop.Marksmanship / enemy.Marksmanship*EffectivenessRatios.MarksmanShip); + if (enemy.CurrentSpeed != 0) + strengthList.Add(troop.CurrentSpeed / enemy.CurrentSpeed*EffectivenessRatios.MovementSpeed); + else + strengthList.Add(troop.CurrentSpeed/double.MinValue*EffectivenessRatios.MovementSpeed); + strengthList.Add(troop.Defense/enemy.Defense*EffectivenessRatios.Defense); + return Math.Abs(1-1/strengthList.Sum()); + } + private static bool NextBool(Random r, double truePercentage = 50) => + r.NextDouble() < truePercentage / 100.0; + + private double Distance(Point p1, Point p2) => + Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); + + private bool CheckIfFinished(List environmentAllies, List environmentAdversaries) => + environmentAdversaries.Count == 0 || environmentAllies.Count == 0; + + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.exe b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.exe index 6c9f14c..4d7303f 100644 Binary files a/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.exe and b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.exe differ diff --git a/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.pdb b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.pdb index 6fbb553..6282ed6 100644 Binary files a/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.pdb and b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/BattleFieldSimulator.pdb differ diff --git a/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/maps/SimpleMap.json b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/maps/SimpleMap.json new file mode 100644 index 0000000..44e7c20 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/maps/SimpleMap.json @@ -0,0 +1,18 @@ +{ + "Map": { + "X": "10", + "Y" : "10", + "Grid": [ + [0, 0, 0, 0, 0, 0, 0, 5, 5, 0], + [0, 1, 1, 1, 0, 0, 0, 5, 5, 0], + [0, 2, 2, 2, 0, 0, 0, 5, 4, 0], + [0, 2, 3, 2, 0, 0, 0, 4, 3, 0], + [0, 2, 2, 1, 0, 0, 0, 3, 2, 0], + [0, 0, 0, 0, 0, 0, 0, 2, 2, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/troops/SimpleTroopFile.json b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/troops/SimpleTroopFile.json new file mode 100644 index 0000000..f9f6936 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/troops/SimpleTroopFile.json @@ -0,0 +1,81 @@ +{ + "Allies": [ + { + "Troop": + { + "MovementSpeed": 1.5, + "SightDistance": 4, + "EngagementDistance": 3, + "WeaponDamage": 8.5, + "Marksmanship": 7, + "TroopCount": 30, + "Aggressiveness": 7, + "Defense": 6, + "Mission": "Defend", + "EntryPointX": 0, + "EntryPointY": 0, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + }, + { + "Troop": + { + "MovementSpeed": 2.5, + "SightDistance": 2, + "EngagementDistance": 1, + "WeaponDamage": 5, + "Marksmanship": 4, + "TroopCount": 10, + "Aggressiveness": 10, + "Defense": 3, + "Mission": "Defend", + "EntryPointX": 0, + "EntryPointY": 2, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + + } + ], + "Adversaries": [ + { + "Troop": + { + "MovementSpeed": 2.5, + "SightDistance": 3, + "EngagementDistance": 1, + "WeaponDamage": 7, + "Marksmanship": 4, + "TroopCount": 10, + "Aggressiveness": 10, + "Defense": 3, + "Mission": "Attack", + "EntryPointX": 1, + "EntryPointY": 1, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + + }, + { + "Troop": + { + "MovementSpeed": 2.5, + "SightDistance": 2, + "EngagementDistance": 1, + "WeaponDamage": 1, + "Marksmanship": 4, + "TroopCount": 15, + "Aggressiveness": 10, + "Defense": 3, + "Mission": "Attack", + "EntryPointX": 2, + "EntryPointY": 7, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + + } + ] +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/maps/SimpleMap.json b/BattleFieldSimulator/BattleFieldSimulator/maps/SimpleMap.json new file mode 100644 index 0000000..44e7c20 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/maps/SimpleMap.json @@ -0,0 +1,18 @@ +{ + "Map": { + "X": "10", + "Y" : "10", + "Grid": [ + [0, 0, 0, 0, 0, 0, 0, 5, 5, 0], + [0, 1, 1, 1, 0, 0, 0, 5, 5, 0], + [0, 2, 2, 2, 0, 0, 0, 5, 4, 0], + [0, 2, 3, 2, 0, 0, 0, 4, 3, 0], + [0, 2, 2, 1, 0, 0, 0, 3, 2, 0], + [0, 0, 0, 0, 0, 0, 0, 2, 2, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + } +} \ No newline at end of file diff --git a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csproj.FileListAbsolute.txt b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csproj.FileListAbsolute.txt index 72d1ea7..a9cbd39 100644 --- a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csproj.FileListAbsolute.txt +++ b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csproj.FileListAbsolute.txt @@ -6,3 +6,5 @@ /Users/bradybodily/Repositories/CS5110_Multi_Agent/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/Newtonsoft.Json.dll /Users/bradybodily/Repositories/CS5110_Multi_Agent/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/Newtonsoft.Json.xml /Users/bradybodily/Repositories/CS5110_Multi_Agent/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csproj.CopyComplete +/Users/bradybodily/Repositories/CS5110_Multi_Agent/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/maps/SimpleMap.json +/Users/bradybodily/Repositories/CS5110_Multi_Agent/BattleFieldSimulator/BattleFieldSimulator/bin/Debug/troops/SimpleTroopFile.json diff --git a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csprojAssemblyReference.cache b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csprojAssemblyReference.cache index 138ee45..37f6772 100644 Binary files a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csprojAssemblyReference.cache and b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.csprojAssemblyReference.cache differ diff --git a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.exe b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.exe index 6c9f14c..4d7303f 100644 Binary files a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.exe and b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.exe differ diff --git a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.pdb b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.pdb index 6fbb553..6282ed6 100644 Binary files a/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.pdb and b/BattleFieldSimulator/BattleFieldSimulator/obj/Debug/BattleFieldSimulator.pdb differ diff --git a/BattleFieldSimulator/BattleFieldSimulator/troops/SimpleTroopFile.json b/BattleFieldSimulator/BattleFieldSimulator/troops/SimpleTroopFile.json new file mode 100644 index 0000000..f9f6936 --- /dev/null +++ b/BattleFieldSimulator/BattleFieldSimulator/troops/SimpleTroopFile.json @@ -0,0 +1,81 @@ +{ + "Allies": [ + { + "Troop": + { + "MovementSpeed": 1.5, + "SightDistance": 4, + "EngagementDistance": 3, + "WeaponDamage": 8.5, + "Marksmanship": 7, + "TroopCount": 30, + "Aggressiveness": 7, + "Defense": 6, + "Mission": "Defend", + "EntryPointX": 0, + "EntryPointY": 0, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + }, + { + "Troop": + { + "MovementSpeed": 2.5, + "SightDistance": 2, + "EngagementDistance": 1, + "WeaponDamage": 5, + "Marksmanship": 4, + "TroopCount": 10, + "Aggressiveness": 10, + "Defense": 3, + "Mission": "Defend", + "EntryPointX": 0, + "EntryPointY": 2, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + + } + ], + "Adversaries": [ + { + "Troop": + { + "MovementSpeed": 2.5, + "SightDistance": 3, + "EngagementDistance": 1, + "WeaponDamage": 7, + "Marksmanship": 4, + "TroopCount": 10, + "Aggressiveness": 10, + "Defense": 3, + "Mission": "Attack", + "EntryPointX": 1, + "EntryPointY": 1, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + + }, + { + "Troop": + { + "MovementSpeed": 2.5, + "SightDistance": 2, + "EngagementDistance": 1, + "WeaponDamage": 1, + "Marksmanship": 4, + "TroopCount": 15, + "Aggressiveness": 10, + "Defense": 3, + "Mission": "Attack", + "EntryPointX": 2, + "EntryPointY": 7, + "ObjectiveX": 0, + "ObjectiveY": 0 + } + + } + ] +} \ No newline at end of file diff --git a/BattleFieldSimulator/ConsoleClient/ConsoleClient.csproj b/BattleFieldSimulator/ConsoleClient/ConsoleClient.csproj deleted file mode 100644 index fd365a0..0000000 --- a/BattleFieldSimulator/ConsoleClient/ConsoleClient.csproj +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - Debug - AnyCPU - {35646D97-C2E5-4869-8995-2CB075E0631F} - Exe - Properties - ConsoleClient - ConsoleClient - v4.7.2 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - - - - - \ No newline at end of file diff --git a/BattleFieldSimulator/ConsoleClient/ConsoleClientModule.cs b/BattleFieldSimulator/ConsoleClient/ConsoleClientModule.cs deleted file mode 100644 index 00d161a..0000000 --- a/BattleFieldSimulator/ConsoleClient/ConsoleClientModule.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.ComponentModel; -using DryIoc; - -namespace ConsoleClient -{ - public class ConsoleClientModule - { - public class CoreModule : IModule - { - public void Register(IContainer container) - { - container.Register(); - } - - public void Resolve(IContainer container) - { - throw new System.NotImplementedException(); - } - } - } -} \ No newline at end of file diff --git a/BattleFieldSimulator/ConsoleClient/DryIoc/Container.cs b/BattleFieldSimulator/ConsoleClient/DryIoc/Container.cs deleted file mode 100644 index 6f30561..0000000 --- a/BattleFieldSimulator/ConsoleClient/DryIoc/Container.cs +++ /dev/null @@ -1,14042 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2013-2020 Maksim Volkau - -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 !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_FAST_EXPRESSION_COMPILER -#endif -#if !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_ISERVICE_PROVIDER -#endif -#if !PCL && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 -#define SUPPORTS_SERIALIZABLE -#define SUPPORTS_STACK_TRACE -#define SUPPORTS_MANAGED_THREAD_ID -#endif -#if !PCL && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_5 && !NET35 && !NET40 && !NET403 && !NET45 && !NET451 && !NET452 -#define SUPPORTS_ASYNC_LOCAL -#endif -#if !PCL && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NET35 && !NET40 && !NET403 -#define SUPPORTS_VARIANCE -#endif -#if !PCL && !NET35 && !NET40 && !NET403 && !NET45 && !NET451 && !NET452 && !NET46 && !NET461 && !NET462 && !NET47 && !NET471 && !NET472 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 -#define SUPPORTS_EXPRESSION_COMPILE_WITH_PREFER_INTERPRETATION_PARAM -#endif -#if !PCL && !NET35 && !NET40 && !NET403 -#define SUPPORTS_DELEGATE_METHOD -#endif - -namespace DryIoc -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Text; - using System.Threading; - using System.Diagnostics.CodeAnalysis; // for SuppressMessage - using System.Diagnostics; // for StackTrace - using System.Runtime.CompilerServices; // for MethodImplAttribute - - using ImTools; - using static ImTools.ArrayTools; - using static System.Environment; - - using ExprType = System.Linq.Expressions.ExpressionType; - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - using FastExpressionCompiler.LightExpression; - using static FastExpressionCompiler.LightExpression.Expression; -#else - using System.Linq.Expressions; - using static System.Linq.Expressions.Expression; -#endif - - /// Inversion of control container - public sealed partial class Container : IContainer - { - /// Creates new container with default rules . - public Container() : this(Rules.Default, Ref.Of(Registry.Default), NewSingletonScope()) - { - SetInitialFactoryID(); - } - - /// Creates new container, optionally providing to modify default container behavior. - /// (optional) Rules to modify container default resolution behavior. - /// If not specified, then will be used. - /// (optional) Scope context to use for scoped reuse. - public Container(Rules rules = null, IScopeContext scopeContext = null) - : this(rules ?? Rules.Default, Ref.Of(Registry.Default), NewSingletonScope(), scopeContext) - { - SetInitialFactoryID(); - } - - /// Creates new container with configured rules. - /// Allows to modify rules. - /// (optional) Scope context to use for . - public Container(Func configure, IScopeContext scopeContext = null) - : this(configure.ThrowIfNull()(Rules.Default) ?? Rules.Default, scopeContext) - { } - - private sealed class SinlgetonScopeName - { - public static readonly SinlgetonScopeName Name = new SinlgetonScopeName(); - private SinlgetonScopeName() { } - public override string ToString() => nameof(SinlgetonScopeName); - } - - /// Helper to create singleton scope - public static IScope NewSingletonScope() => new Scope(name: SinlgetonScopeName.Name); - - /// Pretty prints the container info including the open scope details if any. - public override string ToString() - { - var s = _scopeContext == null ? "Container" : "Container with ambient ScopeContext " + _scopeContext; - - var scope = CurrentScope; - s += scope == null ? " without Scope" : " with Scope " + scope; - - if (Rules != Rules.Default) - s += NewLine + " with " + Rules; - - if (IsDisposed) - { - s += " has been DISPOSED!" + NewLine; - if (_disposeStackTrace != null) - s += " Dispose stack-trace " + _disposeStackTrace; - else - s += " You may include Dispose stack-trace into the message via:" + NewLine + - "container.With(rules => rules.WithCaptureContainerDisposeStackTrace())"; - } - - return s; - } - - /// Dispose either open scope, or container with singletons, if no scope opened. - public void Dispose() - { - // if already disposed - just leave - if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1) - return; - - // Nice to have disposal stack-trace, but we can live without it if something goes wrong - if (Rules.CaptureContainerDisposeStackTrace) - try { _disposeStackTrace = new StackTrace(); } - catch { } - - if (_parent != null) - { - if (_ownCurrentScope != null) - { - _ownCurrentScope.Dispose(); - } - else if (_scopeContext != null) - { - IScope currentScope = null; - _scopeContext.SetCurrent(s => - { - // save the current scope for the later, - // do dispose it AFTER its parent is actually set to be a new ambient current scope. - currentScope = s; - return s?.Parent; - }); - currentScope?.Dispose(); - } - } - else - { - _registry.Swap(Registry.Empty); - Rules = Rules.Default; - _singletonScope.Dispose(); // will also dispose any tracked scopes - _scopeContext?.Dispose(); - } - } - - #region Compile-time generated parts - former DryIocZero - - partial void GetLastGeneratedFactoryID(ref int lastFactoryID); - - partial void ResolveGenerated(ref object service, Type serviceType); - - partial void ResolveGenerated(ref object service, - Type serviceType, object serviceKey, Type requiredServiceType, Request preRequestParent, object[] args); - - partial void ResolveManyGenerated(ref IEnumerable services, Type serviceType); - - /// Identifies the service when resolving collection - public struct ResolveManyResult - { - /// Factory, the required part - public FactoryDelegate FactoryDelegate; - - /// Optional key - public object ServiceKey; - - /// Optional required service type, can be an open-generic type. - public Type RequiredServiceType; - - /// Constructs the struct. - public static ResolveManyResult Of(FactoryDelegate factoryDelegate, - object serviceKey = null, Type requiredServiceType = null) => - new ResolveManyResult - { - FactoryDelegate = factoryDelegate, - ServiceKey = serviceKey, - RequiredServiceType = requiredServiceType - }; - } - - /// Directly uses generated factories to resolve service. Or returns the default if service does not have generated factory. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ExpressionIsAlwaysNull", Justification = "Per design")] - public object ResolveCompileTimeGeneratedOrDefault(Type serviceType) - { - object service = null; - ResolveGenerated(ref service, serviceType); - return service; - } - - /// Directly uses generated factories to resolve service. Or returns the default if service does not have generated factory. - [SuppressMessage("ReSharper", "InvocationIsSkipped", Justification = "Per design")] - [SuppressMessage("ReSharper", "ExpressionIsAlwaysNull", Justification = "Per design")] - public object ResolveCompileTimeGeneratedOrDefault(Type serviceType, object serviceKey) - { - object service = null; - ResolveGenerated(ref service, serviceType, serviceKey, - requiredServiceType: null, preRequestParent: null, args: null); - return service; - } - - /// Resolves many generated only services. Ignores runtime registrations. - public IEnumerable ResolveManyCompileTimeGeneratedOrEmpty(Type serviceType) - { - IEnumerable manyGenerated = ArrayTools.Empty(); - ResolveManyGenerated(ref manyGenerated, serviceType); - return manyGenerated; - } - - #endregion - - #region IRegistrator - - /// Returns all registered service factories with their Type and optional Key. - /// Decorator and Wrapper types are not included. - public IEnumerable GetServiceRegistrations() => - _registry.Value.GetServiceRegistrations(); - - // todo: Make `serviceKey` and `factoryType` optional - /// Searches for registered factories by type, and key (if specified), - /// and by factory type (by default uses ). - /// May return empty, 1 or multiple factories. - public Factory[] GetRegisteredFactories(Type serviceType, object serviceKey, FactoryType factoryType) => - _registry.Value.GetRegisteredFactories(serviceType.ThrowIfNull(), serviceKey, factoryType); - - /// Stores factory into container using and as key - /// for later lookup. - /// Any subtypes of . - /// Type of service to resolve later. - /// (optional) Service key of any type with and - /// implemented. - /// (optional) Says how to handle existing registration with the same - /// and . - /// Confirms that service and implementation types are statically checked by compiler. - /// True if factory was added to registry, false otherwise. - /// False may be in case of setting and already existing factory. - public void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked) - { - ThrowIfContainerDisposed(); - - if (serviceKey == null) - serviceKey = Rules.DefaultRegistrationServiceKey; - - factory.ThrowIfNull().ValidateAndNormalizeRegistration(serviceType, serviceKey, isStaticallyChecked, Rules); - - if (!ifAlreadyRegistered.HasValue) - ifAlreadyRegistered = Rules.DefaultIfAlreadyRegistered; - - // Improves performance a bit by first attempting to swap the registry while it is still unchanged. - var r = _registry.Value; - if (!_registry.TrySwapIfStillCurrent(r, r.Register(factory, serviceType, ifAlreadyRegistered.Value, serviceKey))) - RegistrySwap(factory, serviceType, serviceKey, ifAlreadyRegistered); - } - - // hiding nested lambda in method to reduce allocations - private Registry RegistrySwap(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered) => - _registry.Swap(r => r.Register(factory, serviceType, ifAlreadyRegistered.Value, serviceKey)); - - /// - public bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, Func condition) - { - ThrowIfContainerDisposed(); - return _registry.Value.IsRegistered(serviceType, serviceKey, factoryType, condition); - } - - /// - public void Unregister(Type serviceType, object serviceKey, FactoryType factoryType, Func condition) - { - ThrowIfContainerDisposed(); - _registry.Swap(r => r.Unregister(factoryType, serviceType, serviceKey, condition)); - } - -#endregion - -#region IResolver - -#if SUPPORTS_ISERVICE_PROVIDER - /// Resolves service with policy, - /// enabling the fallback resolution for not registered services (default MS convention) - object IServiceProvider.GetService(Type serviceType) => - ((IResolver)this).Resolve(serviceType, IfUnresolved.ReturnDefaultIfNotRegistered); -#endif - - object IResolver.Resolve(Type serviceType, IfUnresolved ifUnresolved) - { - object service = null; - ResolveGenerated(ref service, serviceType); - if (service != null) - return service; - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - var cacheEntry = _registry.Value.GetCachedDefaultFactoryOrDefault(serviceTypeHash, serviceType); - if (cacheEntry != null) - { - ref var entry = ref cacheEntry.Value; - if (entry.Value is FactoryDelegate cachedDelegate) - return cachedDelegate(this); - - if (ResolverContext.TryGetUsedInstance(this, serviceType, out var usedInstance)) - { - entry.Value = null; // reset the cache - return usedInstance; - } - - var rules = Rules; - while (entry.Value is Expression expr) - { - if (rules.UseInterpretation && - Interpreter.TryInterpretAndUnwrapContainerException(this, expr, false, out var result)) - return result; - - // set to Compiling to notify other threads to use the interpretation until the service is compiled - if (Interlocked.CompareExchange(ref entry.Value, new Registry.Compiling(expr), expr) == expr) - { - var compiledFactory = expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation); - // todo: should we instead cache only after invoking the factory delegate - entry.Value = compiledFactory; - return compiledFactory(this); - } - } - - if (entry.Value is Registry.Compiling compiling) - { - if (Interpreter.TryInterpretAndUnwrapContainerException(this, compiling.Expression, false, out var result)) - return result; - return compiling.Expression.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation)(this); - } - } - - return ResolveAndCache(serviceTypeHash, serviceType, ifUnresolved); - } - - private object ResolveAndCache(int serviceTypeHash, Type serviceType, IfUnresolved ifUnresolved) - { - ThrowIfContainerDisposed(); - - if (ResolverContext.TryGetUsedInstance(this, serviceType, out var usedInstance)) - return usedInstance; - - var request = Request.Create(this, serviceType, ifUnresolved: ifUnresolved); - var factory = ((IContainer)this).ResolveFactory(request); // HACK: may mutate request, but it should be safe - - // Delegate to full blown Resolve aware of service key, open scope, etc. - var serviceKey = request.ServiceKey; - var scopeName = CurrentScope?.Name; - if (serviceKey != null || scopeName != null) - return ResolveAndCacheKeyed(serviceTypeHash, serviceType, serviceKey, ifUnresolved, scopeName, null, Request.Empty, null); - - if (factory == null) - return null; - - var rules = Rules; - FactoryDelegate factoryDelegate; - - // todo: [Obsolete] - in v5.0 there should be no check nor the InstanceFactory - if (factory is InstanceFactory || - !rules.UseInterpretationForTheFirstResolution) - { - factoryDelegate = factory.GetDelegateOrDefault(request); - if (factoryDelegate == null) - return null; - } - else - { - var expr = factory.GetExpressionOrDefault(request); - if (expr == null) - return null; - - if (expr is ConstantExpression constExpr) - { - var value = constExpr.Value; - if (factory.Caching != FactoryCaching.DoNotCache) - _registry.Value.TryCacheDefaultFactory(serviceTypeHash, serviceType, value.ToFactoryDelegate); - return value; - } - - // Important to cache expression first before tying to interpret, - // so that parallel resolutions may already use it and UseInstance may correctly evict the cache if needed. - if (factory.Caching != FactoryCaching.DoNotCache) - _registry.Value.TryCacheDefaultFactory(serviceTypeHash, serviceType, expr); - - // 1) First try to interpret - if (Interpreter.TryInterpretAndUnwrapContainerException(this, expr, rules.UseFastExpressionCompiler, out var instance)) - return instance; - // 2) Fallback to expression compilation - factoryDelegate = expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation); - } - - if (factory.Caching != FactoryCaching.DoNotCache) - _registry.Value.TryCacheDefaultFactory(serviceTypeHash, serviceType, factoryDelegate); - - return factoryDelegate(this); - } - - object IResolver.Resolve(Type serviceType, object serviceKey, - IfUnresolved ifUnresolved, Type requiredServiceType, Request preResolveParent, object[] args) - { - // fallback to simple Resolve and its default cache if no keys are passed - var scopeName = CurrentScope?.Name; - if (serviceKey == null && requiredServiceType == null && scopeName == null && - (preResolveParent == null || preResolveParent.IsEmpty) && args.IsNullOrEmpty()) - return ((IResolver)this).Resolve(serviceType, ifUnresolved); - - return ResolveAndCacheKeyed(RuntimeHelpers.GetHashCode(serviceType), serviceType, - serviceKey, ifUnresolved, scopeName, requiredServiceType, preResolveParent ?? Request.Empty, args); - } - - private object ResolveAndCacheKeyed(int serviceTypeHash, Type serviceType, - object serviceKey, IfUnresolved ifUnresolved, object scopeName, Type requiredServiceType, Request preResolveParent, - object[] args) - { - object service = null; - ResolveGenerated(ref service, serviceType, serviceKey, requiredServiceType, preResolveParent, args); - if (service != null) - return service; - - object cacheKey = null; - if (requiredServiceType == null && preResolveParent.IsEmpty && args.IsNullOrEmpty()) - { - cacheKey = scopeName == null ? serviceKey - : serviceKey == null ? scopeName - : KV.Of(scopeName, serviceKey); - - if (_registry.Value.GetCachedKeyedFactoryOrDefault(serviceTypeHash, serviceType, cacheKey, out var cacheEntry)) - { - if (cacheEntry.Factory is FactoryDelegate cachedDelegate) - return cachedDelegate(this); - if (TryInterpretOrCompileCachedExpression(this, cacheEntry, Rules, out var result)) - return result; - } - } - - // Cache is missed, so get the factory and put it into cache: - ThrowIfContainerDisposed(); - - var request = Request.Create(this, serviceType, serviceKey, ifUnresolved, requiredServiceType, preResolveParent, default, args); - var factory = ((IContainer)this).ResolveFactory(request); - if (factory == null) - return null; - - // Prevents caching if factory says Don't - if (factory.Caching == FactoryCaching.DoNotCache) - cacheKey = null; - - // Request service key may be changed when resolving the factory, - // so we need to look into Default cache again for the new key - if (cacheKey != null && serviceKey == null && request.ServiceKey != null) - { - cacheKey = scopeName == null ? request.ServiceKey : KV.Of(scopeName, request.ServiceKey); - if (_registry.Value.GetCachedKeyedFactoryOrDefault(serviceTypeHash, serviceType, cacheKey, out var cacheEntry)) - { - if (cacheEntry.Factory is FactoryDelegate cachedDelegate) - return cachedDelegate(this); - if (TryInterpretOrCompileCachedExpression(this, cacheEntry, Rules, out var result)) - return result; - } - } - - FactoryDelegate factoryDelegate; - if (factory is InstanceFactory || !Rules.UseInterpretationForTheFirstResolution) - { - factoryDelegate = factory.GetDelegateOrDefault(request); - if (factoryDelegate == null) - return null; - } - else - { - var expr = factory.GetExpressionOrDefault(request); - if (expr == null) - return null; - - if (expr is ConstantExpression constExpr) - { - var value = constExpr.Value; - if (cacheKey != null) - _registry.Value.TryCacheKeyedFactory(serviceTypeHash, serviceType, cacheKey, (FactoryDelegate)value.ToFactoryDelegate); - return value; - } - - // Important to cache expression first before tying to interpret, - // so that parallel resolutions may already use it and UseInstance may correctly evict the cache if needed - if (cacheKey != null) - _registry.Value.TryCacheKeyedFactory(serviceTypeHash, serviceType, cacheKey, expr); - - // 1) First try to interpret - var useFec = Rules.UseFastExpressionCompiler; - if (Interpreter.TryInterpretAndUnwrapContainerException(this, expr, useFec, out var instance)) - return instance; - - // 2) Fallback to expression compilation - factoryDelegate = expr.CompileToFactoryDelegate(useFec, Rules.UseInterpretation); - } - - // Cache factory only when we successfully called the factory delegate, to prevent failing delegates to be cached. - // Additionally disable caching when no services registered, not to cache an empty collection wrapper or alike. - if (cacheKey != null) - _registry.Value.TryCacheKeyedFactory(serviceTypeHash, serviceType, cacheKey, factoryDelegate); - - return factoryDelegate(this); - } - - private static bool TryInterpretOrCompileCachedExpression(IResolverContext r, - Registry.KeyedFactoryCacheEntry cacheEntry, Rules rules, out object result) - { - while (cacheEntry.Factory is Expression expr) - { - if (rules.UseInterpretation && - Interpreter.TryInterpretAndUnwrapContainerException(r, expr, false, out result)) - return true; - - // set to Compiling to notify other threads to use the interpretation until the service is compiled - if (Interlocked.CompareExchange(ref cacheEntry.Factory, new Registry.Compiling(expr), expr) == expr) - { - var factoryDelegate = expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation); - // todo: should we instead cache only after invoking the factory delegate - cacheEntry.Factory = factoryDelegate; - result = factoryDelegate(r); - return true; - } - } - - if (cacheEntry.Factory is Registry.Compiling compiling) - { - if (!Interpreter.TryInterpretAndUnwrapContainerException(r, compiling.Expression, false, out result)) - result = compiling.Expression.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation)(r); - return true; - } - - result = null; - return false; - } - - IEnumerable IResolver.ResolveMany(Type serviceType, object serviceKey, - Type requiredServiceType, Request preResolveParent, object[] args) - { - var requiredItemType = requiredServiceType ?? serviceType; - - // first return compile-time generated factories if any - var generatedFactories = Enumerable.Empty(); - ResolveManyGenerated(ref generatedFactories, serviceType); - if (serviceKey != null) - generatedFactories = generatedFactories.Where(x => serviceKey.Equals(x.ServiceKey)); - if (requiredServiceType != null) - generatedFactories = generatedFactories.Where(x => requiredServiceType == x.RequiredServiceType); - - foreach (var generated in generatedFactories) - yield return generated.FactoryDelegate(this); - - // Emulating the collection parent so that collection related rules and conditions were applied - // the same way as if resolving IEnumerable - if (preResolveParent == null || preResolveParent.IsEmpty) - preResolveParent = Request.Empty.Push( - typeof(IEnumerable), requiredItemType, serviceKey, IfUnresolved.Throw, - WrappersSupport.CollectionWrapperID, FactoryType.Wrapper, null, null, 0, 0); - - var container = (IContainer)this; - - var unwrappedType = container.GetWrappedType(requiredItemType, null); - if (unwrappedType != null && unwrappedType != typeof(void)) // accounting for the resolved action GH#114 - requiredItemType = unwrappedType; - - var items = container.GetAllServiceFactories(requiredItemType).ToArrayOrSelf() - .Where(x => x.Value != null) - .Select(f => new ServiceRegistrationInfo(f.Value, requiredServiceType, f.Key)); - - IEnumerable openGenericItems = null; - if (requiredItemType.IsClosedGeneric()) - { - var requiredItemOpenGenericType = requiredItemType.GetGenericDefinitionOrNull(); - openGenericItems = container.GetAllServiceFactories(requiredItemOpenGenericType) - .Where(x => x.Value != null) - .Select(x => new ServiceRegistrationInfo(x.Value, requiredServiceType, - new OpenGenericTypeKey(requiredItemOpenGenericType, x.Key))); - } - - // Append registered generic types with compatible variance, - // e.g. for IHandler - IHandler is compatible with IHandler if B : A. - IEnumerable variantGenericItems = null; - if (requiredItemType.IsGeneric() && container.Rules.VariantGenericTypesInResolvedCollection) - { - variantGenericItems = container.GetServiceRegistrations() - .Where(x => x.ServiceType.IsGeneric() - && x.ServiceType.GetGenericTypeDefinition() == requiredItemType.GetGenericTypeDefinition() - && x.ServiceType != requiredItemType - && x.ServiceType.IsAssignableTo(requiredItemType)); - } - - if (serviceKey != null) // include only single item matching key. - { - items = items.Where(it => serviceKey.Equals(it.OptionalServiceKey)); - if (openGenericItems != null) - openGenericItems = openGenericItems // extract the actual key from combined type and key - .Where(x => serviceKey.Equals(((OpenGenericTypeKey)x.OptionalServiceKey).ServiceKey)); - if (variantGenericItems != null) - variantGenericItems = variantGenericItems - .Where(it => serviceKey.Equals(it.OptionalServiceKey)); - } - - var metadataKey = preResolveParent.MetadataKey; - var metadata = preResolveParent.Metadata; - if (metadataKey != null || metadata != null) - { - items = items.Where(x => x.Factory.Setup.MatchesMetadata(metadataKey, metadata)); - if (openGenericItems != null) - openGenericItems = openGenericItems - .Where(x => x.Factory.Setup.MatchesMetadata(metadataKey, metadata)); - if (variantGenericItems != null) - variantGenericItems = variantGenericItems - .Where(x => x.Factory.Setup.MatchesMetadata(metadataKey, metadata)); - } - - // Exclude composite parent service from items, skip decorators - var parent = preResolveParent; - if (parent.FactoryType != FactoryType.Service) - parent = parent.FirstOrDefault(p => p.FactoryType == FactoryType.Service) ?? Request.Empty; - - if (!parent.IsEmpty && parent.GetActualServiceType() == requiredItemType) - { - items = items.Where(x => x.Factory.FactoryID != parent.FactoryID); - - if (openGenericItems != null) - openGenericItems = openGenericItems.Where(x => x - .Factory.FactoryGenerator?.GeneratedFactories.Enumerate() - .FindFirst(f => f.Value.FactoryID == parent.FactoryID) == null); - - if (variantGenericItems != null) - variantGenericItems = variantGenericItems - .Where(x => x.Factory.FactoryID != parent.FactoryID); - } - - var allItems = openGenericItems == null && variantGenericItems == null ? items - : variantGenericItems == null ? items.Concat(openGenericItems) - : openGenericItems == null ? items.Concat(variantGenericItems) - : items.Concat(openGenericItems).Concat(variantGenericItems); - - // Resolve in registration order - foreach (var item in allItems.OrderBy(x => x.FactoryRegistrationOrder)) - { - var service = container.Resolve(serviceType, item.OptionalServiceKey, - IfUnresolved.ReturnDefaultIfNotRegistered, item.ServiceType, preResolveParent, args); - if (service != null) // skip unresolved items - yield return service; - } - } - - private void ThrowIfContainerDisposed() - { - if (IsDisposed) - Throw.It(Error.ContainerIsDisposed, ToString()); - } - -#endregion - -#region IResolverContext - - /// - public IResolverContext Parent => _parent; - - /// - public IResolverContext Root - { - get - { - if (_parent == null) - return null; - var p = _parent; - while (p.Parent != null) - p = p.Parent; - return p; - } - } - - /// - public IScope SingletonScope => _singletonScope; - - /// - public IScopeContext ScopeContext => _scopeContext; - - /// - public IScope CurrentScope => - _scopeContext == null ? _ownCurrentScope : _scopeContext.GetCurrentOrDefault(); - - /// - [MethodImpl((MethodImplOptions)256)] - public IResolverContext WithCurrentScope(IScope scope) - { - ThrowIfContainerDisposed(); - return new Container(Rules, _registry, _singletonScope, _scopeContext, - scope, _disposed, _disposeStackTrace, parent: this); - } - - /// [Obsolete("Please use `RegisterInstance` or `Use` method instead")] - public void UseInstance(Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey) - { - ThrowIfContainerDisposed(); - - if (instance != null) - instance.ThrowIfNotInstanceOf(serviceType, Error.RegisteringInstanceNotAssignableToServiceType); - - if (weaklyReferenced) - instance = new WeakReference(instance); - else if (preventDisposal) - instance = new HiddenDisposable(instance); - - var scope = _ownCurrentScope ?? _singletonScope; - var reuse = scope == _singletonScope ? Reuse.Singleton : Reuse.Scoped; - var instanceType = instance?.GetType() ?? typeof(object); - - _registry.Swap(r => - { - var entry = r.Services.GetValueOrDefault(serviceType); - var oldEntry = entry; - - // no entries, first registration, usual/hot path - if (entry == null) - { - // add new entry with instance factory - var instanceFactory = new InstanceFactory(instance, instanceType, reuse, scope); - entry = serviceKey == null - ? (object)instanceFactory - : FactoriesEntry.Empty.With(instanceFactory, serviceKey); - } - else - { - // have some registrations of instance, find if we should replace, add, or throw - var singleDefaultFactory = entry as Factory; - if (singleDefaultFactory != null) - { - if (serviceKey != null) - { - // @ifAlreadyRegistered does not make sense for keyed, because there are no other keyed - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope), serviceKey); - } - else // for default instance - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.AppendNotKeyed: - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - case IfAlreadyRegistered.Throw: - Throw.It(Error.UnableToRegisterDuplicateDefault, serviceType, singleDefaultFactory); - break; - case IfAlreadyRegistered.Keep: - break; - case IfAlreadyRegistered.Replace: - var reusedFactory = singleDefaultFactory as InstanceFactory; - if (reusedFactory != null) - scope.SetOrAdd(reusedFactory.FactoryID, instance); - else if (reuse != Reuse.Scoped) // for non-instance single registration, just replace with non-scoped instance only - entry = new InstanceFactory(instance, instanceType, reuse, scope); - else - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - case IfAlreadyRegistered.AppendNewImplementation: // otherwise Keep the old one - if (singleDefaultFactory.CanAccessImplementationType && - singleDefaultFactory.ImplementationType != instanceType) - entry = FactoriesEntry.Empty.With(singleDefaultFactory) - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - } - } - } - else // for multiple existing or single keyed factory - { - var singleKeyedOrManyDefaultFactories = (FactoriesEntry)entry; - if (serviceKey != null) - { - var singleKeyedFactory = singleKeyedOrManyDefaultFactories.Factories.GetValueOrDefault(serviceKey); - if (singleKeyedFactory == null) - { - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope), serviceKey); - } - else // when keyed instance is found - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.Replace: - var reusedFactory = singleKeyedFactory as InstanceFactory; - if (reusedFactory != null) - scope.SetOrAdd(reusedFactory.FactoryID, instance); - else - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope), serviceKey); - break; - case IfAlreadyRegistered.Keep: - break; - default: - Throw.It(Error.UnableToRegisterDuplicateKey, serviceType, serviceKey, singleKeyedFactory); - break; - } - } - } - else // for default instance - { - var defaultFactories = singleKeyedOrManyDefaultFactories.LastDefaultKey == null - ? Empty() - : singleKeyedOrManyDefaultFactories.Factories.Enumerate() - .Match(it => it.Key is DefaultKey, it => it.Value) - .ToArrayOrSelf(); - - if (defaultFactories.Length == 0) // no default factories among the multiple existing keyed factories - { - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - } - else // there are existing default factories - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.AppendNotKeyed: - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - break; - case IfAlreadyRegistered.Throw: - Throw.It(Error.UnableToRegisterDuplicateDefault, serviceType, defaultFactories); - break; - case IfAlreadyRegistered.Keep: - break; // entry does not change - case IfAlreadyRegistered.Replace: - var instanceFactories = defaultFactories.Match(f => f is InstanceFactory); - if (instanceFactories.Length == 1) - { - scope.SetOrAdd(instanceFactories[0].FactoryID, instance); - } - else // multiple default or a keyed factory - { - // scoped instance may be appended only, and not replacing anything - if (reuse == Reuse.Scoped) - { - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - } - else // here is the replacement goes on - { - var keyedFactories = singleKeyedOrManyDefaultFactories.Factories.Enumerate() - .Match(it => !(it.Key is DefaultKey)).ToArrayOrSelf(); - - if (keyedFactories.Length == 0) // replaces all default factories? - entry = new InstanceFactory(instance, instanceType, reuse, scope); - else - { - var factoriesEntry = FactoriesEntry.Empty; - for (var i = 0; i < keyedFactories.Length; i++) - factoriesEntry = factoriesEntry - .With(keyedFactories[i].Value, keyedFactories[i].Key); - entry = factoriesEntry - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - } - } - } - break; - case IfAlreadyRegistered.AppendNewImplementation: // otherwise Keep the old one - var duplicateImplIndex = defaultFactories.IndexOf( - x => x.CanAccessImplementationType && x.ImplementationType == instanceType); - if (duplicateImplIndex == -1) // add new implementation - entry = singleKeyedOrManyDefaultFactories - .With(new InstanceFactory(instance, instanceType, reuse, scope)); - // otherwise do nothing - keep the old entry - break; - } - } - } - } - } - - var hash = RuntimeHelpers.GetHashCode(serviceType); - var registry = r.WithServices(r.Services.AddOrUpdate(hash, serviceType, entry)); - - // clearing the resolution cache for the updated factory if any - if (oldEntry != null && oldEntry != entry) - { - var oldFactory = oldEntry as Factory; - if (oldFactory != null) - registry.DropFactoryCache(oldFactory, hash, serviceType); - else - ((FactoriesEntry)oldEntry).Factories.Enumerate().ToArray() - .ForEach(x => registry.DropFactoryCache(x.Value, hash, serviceType, serviceKey)); - } - - return registry; - }); - } - - void IResolverContext.UseInstance(Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey) => - UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - void IRegistrator.UseInstance(Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey) => - UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - void IResolverContext.InjectPropertiesAndFields(object instance, string[] propertyAndFieldNames) - { - var instanceType = instance.ThrowIfNull().GetType(); - - PropertiesAndFieldsSelector propertiesAndFields = null; - if (!propertyAndFieldNames.IsNullOrEmpty()) - { - var matchedMembers = instanceType.GetTypeInfo().DeclaredMembers.Match( - m => (m is PropertyInfo || m is FieldInfo) && propertyAndFieldNames.IndexOf(m.Name) != -1, - PropertyOrFieldServiceInfo.Of); - // todo: Should we throw when no props are found? - propertiesAndFields = matchedMembers.ToFunc>; - } - - propertiesAndFields = propertiesAndFields ?? Rules.PropertiesAndFields ?? PropertiesAndFields.Auto; - - var request = Request.Create(this, instanceType) - .WithResolvedFactory(new RegisteredInstanceFactory(instance, Reuse.Transient), - skipRecursiveDependencyCheck: true, skipCaptiveDependencyCheck: true); - - foreach (var serviceInfo in propertiesAndFields(request)) - if (serviceInfo != null) - { - var details = serviceInfo.Details; - var value = ((IResolver)this).Resolve(serviceInfo.ServiceType, details.ServiceKey, - details.IfUnresolved, details.RequiredServiceType, request, args: null); - if (value != null) - serviceInfo.SetValue(instance, value); - } - } - - /// Adding the factory directly to scope for resolution - public void Use(Type serviceType, FactoryDelegate factory) - { - (CurrentScope ?? SingletonScope).SetUsedInstance(serviceType, factory); - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - var cacheEntry = _registry.Value.GetCachedDefaultFactoryOrDefault(serviceTypeHash, serviceType); - if (cacheEntry != null) - { - ref var entry = ref cacheEntry.Value; - entry.Value = null; // reset the cache if any - } - } - -#endregion - -#region IContainer - - /// The rules object defines policies per container for registration and resolution. - public Rules Rules { get; private set; } - - /// Represents scope bound to container itself, and not the ambient (context) thing. - public IScope OwnCurrentScope => _ownCurrentScope; - - /// Indicates that container is disposed. - public bool IsDisposed => _disposed == 1 || _singletonScope.IsDisposed; - - /// - public IContainer With(Rules rules, IScopeContext scopeContext, RegistrySharing registrySharing, IScope singletonScope) => - With(_parent, rules, scopeContext, registrySharing, singletonScope, _ownCurrentScope); - - /// - public IContainer With(IResolverContext parent, Rules rules, IScopeContext scopeContext, - RegistrySharing registrySharing, IScope singletonScope, IScope curentScope) - { - ThrowIfContainerDisposed(); - - var registry = - registrySharing == RegistrySharing.Share ? _registry : - registrySharing == RegistrySharing.CloneButKeepCache ? Ref.Of(_registry.Value) - : Ref.Of(_registry.Value.WithoutCache()); - - return new Container(rules ?? Rules, registry, singletonScope ?? NewSingletonScope(), scopeContext, - curentScope ?? _ownCurrentScope, _disposed, _disposeStackTrace, parent ?? _parent); - } - - /// Produces new container which prevents any further registrations. - /// (optional) Controls what to do with the next registration: ignore or throw exception. - /// Throws exception by default. - public IContainer WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow = false) => - new Container(Rules, - Ref.Of(_registry.Value.WithNoMoreRegistrationAllowed(ignoreInsteadOfThrow)), - _singletonScope, _scopeContext, _ownCurrentScope, - _disposed, _disposeStackTrace, _parent); - - /// - public bool ClearCache(Type serviceType, FactoryType? factoryType, object serviceKey) - { - var hash = RuntimeHelpers.GetHashCode(serviceType); - - if (factoryType != null) - return _registry.Value.ClearCache(hash, serviceType, serviceKey, factoryType.Value); - - var registry = _registry.Value; - - var clearedServices = registry.ClearCache(hash, serviceType, serviceKey, FactoryType.Service); - var clearedWrapper = registry.ClearCache(hash, serviceType, serviceKey, FactoryType.Wrapper); - var clearedDecorator = registry.ClearCache(hash, serviceType, serviceKey, FactoryType.Decorator); - - return clearedServices || clearedWrapper || clearedDecorator; - } - - [MethodImpl((MethodImplOptions)256)] - internal Expression GetCachedFactoryExpression(int factoryId, Request request, out ImMapEntry slot) => - _registry.Value.GetCachedFactoryExpression(factoryId, request, out slot); - - [MethodImpl((MethodImplOptions) 256)] - internal void CacheFactoryExpression(int factoryId, Request request, Expression expr, ImMapEntry slot) => - _registry.Value.CacheFactoryExpression(factoryId, request, expr, slot); - - Factory IContainer.ResolveFactory(Request request) - { - var factory = ((IContainer)this).GetServiceFactoryOrDefault(request); - if (factory == null) - { - factory = GetWrapperFactoryOrDefault(request); - if (factory != null) - return factory; - - var unknownServiceResolvers = Rules.UnknownServiceResolvers; - if (!unknownServiceResolvers.IsNullOrEmpty()) - for (var i = 0; factory == null && i < unknownServiceResolvers.Length; i++) - factory = unknownServiceResolvers[i](request)?.DoNotCache(); - } - - if (factory?.FactoryGenerator != null) - factory = factory.FactoryGenerator.GetGeneratedFactory(request); - - if (factory == null) - TryThrowUnableToResolve(request); - - return factory; - } - - internal static void TryThrowUnableToResolve(Request request) - { - if (request.IfUnresolved != IfUnresolved.Throw) - return; - - var str = new StringBuilder(); - str = request.Container - .GetAllServiceFactories(request.ServiceType, bothClosedAndOpenGenerics: true) - .Aggregate(str, (s, x) => s - .Append(x.Value.Reuse?.CanApply(request) ?? true ? " " : " without matching scope ") - .Print(x)); - - if (str.Length != 0) - Throw.It(Error.UnableToResolveFromRegisteredServices, request, str); - else - Throw.It(Error.UnableToResolveUnknownService, request, - request.Rules.DynamicRegistrationProviders.EmptyIfNull().Length, - request.Rules.UnknownServiceResolvers.EmptyIfNull().Length); - } - - private Factory GetServiceFactoryOrDefaultForNullServiceKey(Request request, ServiceDetails details) - { - var requiredServiceType = details.RequiredServiceType; - var serviceType = requiredServiceType != null && requiredServiceType.IsOpenGeneric() - ? requiredServiceType - : request.GetActualServiceType(); - - if (Rules.FactorySelector != null) - return GetRuleSelectedServiceFactoryOrDefault(request, serviceType); - - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - if (entry == null && serviceType.GetTypeInfo().IsGenericType) - entry = serviceFactories.GetValueOrDefault(serviceType.GetGenericTypeDefinition()); - - // Most common case when we have a single default factory and no dynamic rules in addition - if (entry is Factory singleDefaultFactory && - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - { - if (singleDefaultFactory.Setup.Condition?.Invoke(request) == false || - (details.MetadataKey != null || details.Metadata != null) && - !singleDefaultFactory.Setup.MatchesMetadata(details.MetadataKey, details.Metadata)) - return null; - - return singleDefaultFactory; - } - - var factories = entry == null ? null - : entry is Factory factory ? new KV(DefaultKey.Value, factory).One() - : entry.To().Factories - .Visit(new List>(2), (x, list) => list.Add(KV.Of(x.Key, x.Value))) - .ToArray(); // todo: optimize - we may not need ToArray here - - if (!Rules.DynamicRegistrationProviders.IsNullOrEmpty() && - (factories.IsNullOrEmpty() || !Rules.UseDynamicRegistrationsAsFallbackOnly)) - factories = CombineRegisteredWithDynamicFactories(factories, true, FactoryType.Service, serviceType, null); - - if (factories.IsNullOrEmpty()) - return null; - - // First, filter out non default normal and dynamic factories - var defaultFactories = factories.Match(f => f.Key is DefaultKey || f.Key is DefaultDynamicKey); - if (defaultFactories.Length == 0) - return null; - - // For multiple matched factories, if the single one has a condition, then use it - var matchedFactories = defaultFactories.Match(x => request.MatchFactoryConditionAndMetadata(x)); - - // Check the for matching scopes. Only for more than 1 factory, - // for the single factory the check will be down the road - // BitBucket issue: #175 - if (matchedFactories.Length > 1 && request.Rules.ImplicitCheckForReuseMatchingScope) - { - var reuseMatchedFactories = matchedFactories.Match(x => request.MatchFactoryReuse(x)); - if (reuseMatchedFactories.Length == 1) - matchedFactories = reuseMatchedFactories; - else if (reuseMatchedFactories.Length > 1) - matchedFactories = FindFactoryWithTheMinReuseLifespan(matchedFactories)?.One() ?? matchedFactories; - - if (matchedFactories.Length == 1) - { - // Issue: #382 - // Add asResolutionCall for the factory to prevent caching of in-lined expression in context with not matching condition - if (request.IsResolutionCall) - request.ChangeServiceKey(matchedFactories[0].Key); - else // for injected dependency - matchedFactories[0].Value.Setup = matchedFactories[0].Value.Setup.WithAsResolutionCall(); - } - } - - // Match open-generic implementation with closed service type. Performance is OK because the generated factories are cached - - // so there should not be repeating of the check, and not match of Performance decrease. - if (matchedFactories.Length > 1) - matchedFactories = matchedFactories.Match(x => request.MatchGeneratedFactory(x)); - - if (matchedFactories.Length > 1) - { - var conditionedFactories = matchedFactories.Match(f => f.Value.Setup.Condition != null); - if (conditionedFactories.Length == 1) - matchedFactories = conditionedFactories; - } - - if (matchedFactories.Length > 1) - { - var preferedFactories = matchedFactories.Match(f => f.Value.Setup.PreferInSingleServiceResolve); - if (preferedFactories.Length == 1) - matchedFactories = preferedFactories; - } - - // The result is a single matched factory - if (matchedFactories.Length == 1) - { - // Changes service key for resolution call to identify single factory in cache and prevent wrong hit - if (defaultFactories.Length > 1 && request.IsResolutionCall) - request.ChangeServiceKey(matchedFactories[0].Key); - return matchedFactories[0].Value; - } - - if (matchedFactories.Length > 1 && request.IfUnresolved == IfUnresolved.Throw) - Throw.It(Error.ExpectedSingleDefaultFactory, matchedFactories, request); - - return null; - } - - private Factory GetServiceFactoryOrDefaultForServiceKey(Request request, ServiceDetails details, object serviceKey) - { - Type serviceType; - var requiredServiceType = details.RequiredServiceType; - if (requiredServiceType != null && requiredServiceType.IsOpenGeneric()) - serviceType = requiredServiceType; - else - { - serviceType = request.GetActualServiceType(); - - // Special case when open-generic required service type is encoded in ServiceKey as array of { ReqOpenGenServiceType, ServiceKey } - // presumes that required service type is closed generic - if (serviceKey is OpenGenericTypeKey openGenericTypeKey && - serviceType.GetTypeInfo().IsGenericType && serviceType.GetGenericTypeDefinition() == openGenericTypeKey.RequiredServiceType) - { - serviceType = openGenericTypeKey.RequiredServiceType; - serviceKey = openGenericTypeKey.ServiceKey; - } - } - - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - - // If the entry is not found or the key in entry is not found go for the open-generic services - if ((entry == null || - entry is Factory && !serviceKey.Equals(DefaultKey.Value) || - entry is FactoriesEntry facEntry && facEntry.Factories.GetValueOrDefault(serviceKey) == null) && - serviceType.IsClosedGeneric()) - entry = serviceFactories.GetValueOrDefault(serviceType.GetGenericTypeDefinition()) ?? entry; - - // Most common case when we have a single default factory and no dynamic rules in addition - if (entry is Factory singleDefaultFactory && - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - { - if (serviceKey != DefaultKey.Value || - singleDefaultFactory.Setup.Condition?.Invoke(request) == false || - (details.MetadataKey != null || details.Metadata != null) && - !singleDefaultFactory.Setup.MatchesMetadata(details.MetadataKey, details.Metadata)) - return null; - - return singleDefaultFactory; - } - - var factories = entry == null ? null - : entry is Factory factory ? new KV(DefaultKey.Value, factory).One() - : entry.To().Factories - .Visit(new List>(2), (x, list) => list.Add(KV.Of(x.Key, x.Value))) - .ToArray(); // todo: optimize - we may not need ToArray here - - if (!Rules.DynamicRegistrationProviders.IsNullOrEmpty() && - (factories.IsNullOrEmpty() || !Rules.UseDynamicRegistrationsAsFallbackOnly)) - factories = CombineRegisteredWithDynamicFactories(factories, true, FactoryType.Service, serviceType, serviceKey); - - if (factories.IsNullOrEmpty()) - return null; - - // For requested keyed service (which may be a `DefaultKey` or `DefaultDynamicKey`) - // just lookup for the key and return whatever the result - foreach (var f in factories) - if (serviceKey.Equals(f.Key) && f.Value.CheckCondition(request)) - return f.Value; - return null; - } - - Factory IContainer.GetServiceFactoryOrDefault(Request request) - { - var details = request.ServiceDetails; - if (details.ServiceKey == null) - return GetServiceFactoryOrDefaultForNullServiceKey(request, details); - return GetServiceFactoryOrDefaultForServiceKey(request, details, details.ServiceKey); - } - - private Factory GetRuleSelectedServiceFactoryOrDefault(Request request, Type serviceType) - { - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - - KV[] factories; - if (entry is Factory singleDefaultFactory) - { - // optimize for the most usual case - a single factory and no dynamic rules - if (Rules.UseDynamicRegistrationsAsFallbackOnly || - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return request.MatchFactoryConditionAndMetadata(singleDefaultFactory) - ? Rules.FactorySelector(request, DefaultKey.Value.Pair(singleDefaultFactory).One()) - : null; - - factories = new[] {new KV(DefaultKey.Value, singleDefaultFactory)}; - } - else if (entry is FactoriesEntry e) - { - factories = e.Factories.Visit(new List>(), (x, l) => l.Add(KV.Of(x.Key, x.Value))).ToArray(); - } - else - { - object openGenericEntry; - factories = Empty>(); - if (serviceType.IsClosedGeneric()) - { - var openGenericServiceType = serviceType.GetGenericTypeDefinition(); - openGenericEntry = serviceFactories.GetValueOrDefault(openGenericServiceType); - if (openGenericEntry != null) - factories = GetRegistryEntryKeyFactoryPairs(openGenericEntry).ToArrayOrSelf(); - } - } - - if ((factories.Length == 0 || !Rules.UseDynamicRegistrationsAsFallbackOnly) && - !Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - factories = CombineRegisteredWithDynamicFactories(factories, true, FactoryType.Service, serviceType); - - if (factories.Length == 0) - return null; - - // optimize for the case with the single factory - if (factories.Length == 1) - return request.MatchFactoryConditionAndMetadata(factories[0]) - ? Rules.FactorySelector(request, factories[0].Key.Pair(factories[0].Value).One()) - : null; - - // Sort in registration order - if (factories.Length > 1) - Array.Sort(factories, _lastFactoryIDWinsComparer); - - var matchedFactories = factories.Match(request.MatchFactoryConditionAndMetadata); - if (matchedFactories.Length > 1 && request.Rules.ImplicitCheckForReuseMatchingScope) - { - // Check the for matching scopes. Only for more than 1 factory, - // for the single factory the check will be down the road - // BitBucket issue: #175 - matchedFactories = matchedFactories.Match(request.MatchFactoryReuse); - if (matchedFactories.Length == 1) - { - // Issue: #382 - // Add asResolutionCall for the factory to prevent caching of in-lined expression in context with not matching condition - if (request.IsResolutionCall) - request.ChangeServiceKey(matchedFactories[0].Key); - else // for injected dependency - matchedFactories[0].Value.Setup = matchedFactories[0].Value.Setup.WithAsResolutionCall(); - } - } - - // Match open-generic implementation with closed service type. Performance is OK because the generated factories are cached - - // so there should not be repeating of the check, and not match of Performance decrease. - if (matchedFactories.Length > 1) - matchedFactories = matchedFactories.Match(request.MatchGeneratedFactory); - - if (matchedFactories.Length == 0) - return null; - - var selectedFactory = Rules.FactorySelector(request, matchedFactories.Map(x => x.Key.Pair(x.Value))); - if (selectedFactory == null) - return null; - - // Issue: #508 - if (factories.Length > 1) - { - var i = 0; - while (i < matchedFactories.Length && matchedFactories[i].Value.FactoryID != selectedFactory.FactoryID) - ++i; - if (i < matchedFactories.Length) - request.ChangeServiceKey(matchedFactories[i].Key); - } - - return selectedFactory; - } - - private static KV FindFactoryWithTheMinReuseLifespan(KV[] factories) - { - var minLifespan = int.MaxValue; - var multipleFactories = false; - KV minLifespanFactory = null; - for (var i = 0; i < factories.Length; i++) - { - var factory = factories[i]; - var reuse = factory.Value.Reuse; - var lifespan = reuse == null || reuse == Reuse.Transient ? int.MaxValue : reuse.Lifespan; - if (lifespan < minLifespan) - { - minLifespan = lifespan; - minLifespanFactory = factory; - multipleFactories = false; - } - else if (lifespan == minLifespan) - { - multipleFactories = true; - } - } - - return !multipleFactories && minLifespanFactory != null ? minLifespanFactory : null; - } - - IEnumerable> IContainer.GetAllServiceFactories(Type serviceType, bool bothClosedAndOpenGenerics) - { - var serviceFactories = _registry.Value.Services; - var entry = serviceFactories.GetValueOrDefault(serviceType); - - var factories = GetRegistryEntryKeyFactoryPairs(entry).ToArrayOrSelf(); - - if (bothClosedAndOpenGenerics && serviceType.IsClosedGeneric()) - { - var openGenericServiceType = serviceType.GetGenericTypeDefinition(); - var openGenericEntry = serviceFactories.GetValueOrDefault(openGenericServiceType); - if (openGenericEntry != null) - factories = factories.Append(GetRegistryEntryKeyFactoryPairs(openGenericEntry).ToArrayOrSelf()); - } - - if (!factories.IsNullOrEmpty() && Rules.UseDynamicRegistrationsAsFallbackOnly || - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return factories; - - return CombineRegisteredWithDynamicFactories(factories, bothClosedAndOpenGenerics, FactoryType.Service, serviceType); - } - - private static IEnumerable> GetRegistryEntryKeyFactoryPairs(object entry) => - entry == null - ? Empty>() - : entry is Factory ? new[] { new KV(DefaultKey.Value, (Factory)entry) } - // todo: optimize - : entry.To().Factories.Visit(new List>(), (x, l) => l.Add(KV.Of(x.Key, x.Value))).ToArray(); - - Expression IContainer.GetDecoratorExpressionOrDefault(Request request) - { - // return early if no decorators registered - if (_registry.Value.Decorators.IsEmpty && request.Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return null; - - var serviceType = request.ServiceType; - var arrayElementType = serviceType.GetArrayElementTypeOrNull(); - if (arrayElementType != null) - { - request = request.WithChangedServiceInfo(x => - x.With(typeof(IEnumerable<>).MakeGenericType(arrayElementType))); - serviceType = request.ServiceType; - } - - var container = request.Container; - var decorators = container.GetDecoratorFactoriesOrDefault(serviceType); - - // Combine with required service type if different from service type - var requiredServiceType = request.GetActualServiceType(); - if (requiredServiceType != serviceType) - decorators = decorators.Append(container.GetDecoratorFactoriesOrDefault(requiredServiceType)); - - // Define the list of ids for the already applied decorators - int[] appliedDecoratorIDs = null; - if (!decorators.IsNullOrEmpty()) - { - appliedDecoratorIDs = GetAppliedDecoratorIDs(request); - if (!appliedDecoratorIDs.IsNullOrEmpty()) - decorators = decorators.Match(appliedDecoratorIDs, (ids, d) => ids.IndexOf(d.FactoryID) == -1); - } - - // Append open-generic decorators - var genericDecorators = Empty(); - Type openGenericServiceType = null; - if (serviceType.GetTypeInfo().IsGenericType) - genericDecorators = container.GetDecoratorFactoriesOrDefault(openGenericServiceType = serviceType.GetGenericTypeDefinition()); - - // Combine with open-generic required type if they are different from service type - if (requiredServiceType != serviceType) - { - var openGenericRequiredType = requiredServiceType.GetTypeInfo().IsGenericType ? requiredServiceType.GetGenericTypeDefinition() : null; - if (openGenericRequiredType != null && openGenericRequiredType != openGenericServiceType) - genericDecorators = genericDecorators.Append( - container.GetDecoratorFactoriesOrDefault(openGenericRequiredType)); - } - - // Append generic type argument decorators, registered as Object - // Note: the condition for type arguments should be checked before generating the closed generic version - var typeArgDecorators = container.GetDecoratorFactoriesOrDefault(typeof(object)); - if (!typeArgDecorators.IsNullOrEmpty()) - genericDecorators = genericDecorators.Append( - typeArgDecorators.Match(request, (r, d) => d.CheckCondition(r))); - - // Filter out already applied generic decorators - // And combine with rest of decorators - if (!genericDecorators.IsNullOrEmpty()) - { - appliedDecoratorIDs = appliedDecoratorIDs ?? GetAppliedDecoratorIDs(request); - if (!appliedDecoratorIDs.IsNullOrEmpty()) - { - genericDecorators = genericDecorators - .Match(appliedDecoratorIDs, - (appliedDecIds, d) => - { - var factoryGenerator = d.FactoryGenerator; - if (factoryGenerator == null) - return appliedDecIds.IndexOf(d.FactoryID) == -1; - - foreach (var entry in factoryGenerator.GeneratedFactories.Enumerate()) - if (appliedDecIds.IndexOf(entry.Value.FactoryID) != -1) - return false; - - return true; - }); - } - - // Generate closed-generic versions - if (!genericDecorators.IsNullOrEmpty()) - { - genericDecorators = genericDecorators - .Map(request, (r, d) => d.FactoryGenerator == null ? d : d.FactoryGenerator.GetGeneratedFactory(r, ifErrorReturnDefault: true)) - .Match(d => d != null); - decorators = decorators.Append(genericDecorators); - } - } - - // Filter out the recursive decorators by doing the same recursive check - // that Request.WithResolvedFactory does. Fixes: #267 - if (!decorators.IsNullOrEmpty()) - decorators = decorators.Match(request, (r, d) => !r.HasRecursiveParent(d.FactoryID)); - - // Return earlier if no decorators found, or we have filtered out everything - if (decorators.IsNullOrEmpty()) - return null; - - Factory decorator; - if (decorators.Length == 1) - { - decorator = decorators[0]; - if (!decorator.CheckCondition(request)) - return null; - } - else - { - // Within the remaining decorators find one with the maximum Order - // or if no Order for all decorators, then find the last registered - with the biggest FactoryID - decorator = decorators - .OrderByDescending(d => ((Setup.DecoratorSetup)d.Setup).Order) - .ThenByDescending(d => d.RegistrationOrder) - .FirstOrDefault(d => d.CheckCondition(request)); - } - - var decoratorExpr = decorator?.GetExpressionOrDefault(request); - if (decoratorExpr == null) - return null; - - // decorator of arrays should be converted back from IEnumerable to array. - if (arrayElementType != null) - decoratorExpr = Call(WrappersSupport.ToArrayMethod.MakeGenericMethod(arrayElementType), decoratorExpr); - - return decoratorExpr; - } - - private static int[] GetAppliedDecoratorIDs(Request request) - { - var appliedIDs = Empty(); - for (var p = request.DirectParent; !p.IsEmpty && p.FactoryType != FactoryType.Service; p = p.DirectParent) - { - if (p.FactoryType == FactoryType.Decorator && - p.DecoratedFactoryID == request.FactoryID) - appliedIDs = appliedIDs.AppendOrUpdate(p.FactoryID); - } - return appliedIDs; - } - - Factory IContainer.GetWrapperFactoryOrDefault(Type serviceType) - { - var wrappers = _registry.Value.Wrappers; - var wrapper = wrappers.GetValueOrDefault(serviceType); - if (wrapper == null && serviceType.GetTypeInfo().IsGenericType) - wrapper = wrappers.GetValueOrDefault(serviceType.GetGenericTypeDefinition()); - return wrapper as Factory; - } - - Factory[] IContainer.GetDecoratorFactoriesOrDefault(Type serviceType) - { - var allDecorators = _registry.Value.Decorators; - var decorators = allDecorators.IsEmpty ? null : (Factory[])allDecorators.GetValueOrDefault(serviceType); - if (decorators == null) - { - if (Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return Empty(); - return CombineRegisteredWithDynamicFactories(null, true, FactoryType.Decorator, serviceType, null).Map(x => x.Value); - } - - if (Rules.UseDynamicRegistrationsAsFallbackOnly || - Rules.DynamicRegistrationProviders.IsNullOrEmpty()) - return decorators; - - var decoratorsWithDefaultKey = decorators.Map(d => new KV(DefaultKey.Value, d)); - return CombineRegisteredWithDynamicFactories( - decoratorsWithDefaultKey, true, FactoryType.Decorator, serviceType, null).Map(x => x.Value); - } - - Type IContainer.GetWrappedType(Type serviceType, Type requiredServiceType) - { - if (requiredServiceType != null && requiredServiceType.IsOpenGeneric()) - return ((IContainer)this).GetWrappedType(serviceType, null); - - serviceType = requiredServiceType ?? serviceType; - - var wrappedType = serviceType.GetArrayElementTypeOrNull(); - if (wrappedType == null) - { - var factory = ((IContainer)this).GetWrapperFactoryOrDefault(serviceType); - if (factory != null) - { - wrappedType = ((Setup.WrapperSetup)factory.Setup).GetWrappedTypeOrNullIfWrapsRequired(serviceType); - if (wrappedType == null) - return null; - } - } - - return wrappedType == null ? serviceType - : ((IContainer)this).GetWrappedType(wrappedType, null); - } - - /// Converts known item into literal expression or wraps it in a constant expression. - public Expression GetConstantExpression(object item, Type itemType = null, bool throwIfStateRequired = false) - { - // Check for UsedForExpressionGeneration, and if not set just short-circuit to Expression.Constant - if (!throwIfStateRequired && !Rules.ThrowIfRuntimeStateRequired && !Rules.UsedForExpressionGeneration) - return itemType == null ? Constant(item) : Constant(item, itemType); - - if (item == null) - return itemType == null || itemType == typeof(object) ? Constant(null) : Constant(null, itemType); - - var convertible = item as IConvertibleToExpression; - if (convertible != null) - return convertible.ToExpression(it => GetConstantExpression(it, null, throwIfStateRequired)); - - var actualItemType = item.GetType(); - if (actualItemType.GetGenericDefinitionOrNull() == typeof(KV<,>)) - { - var kvArgTypes = actualItemType.GetGenericParamsAndArgs(); - return Call(_kvOfMethod.MakeGenericMethod(kvArgTypes), - GetConstantExpression(actualItemType.GetTypeInfo().GetDeclaredField("Key").GetValue(item), kvArgTypes[0], throwIfStateRequired), - GetConstantExpression(actualItemType.GetTypeInfo().GetDeclaredField("Value").GetValue(item), kvArgTypes[1], throwIfStateRequired)); - } - - if (actualItemType.IsPrimitive() || - actualItemType.IsAssignableTo()) - return itemType == null ? Constant(item) : Constant(item, itemType); - - // don't try to recover the non primitive type of element, - // cause it is a too much work to find the base common element type in array - var arrayElemType = actualItemType.GetArrayElementTypeOrNull(); - if (arrayElemType != null && arrayElemType != typeof(object) && - (arrayElemType.IsPrimitive() || actualItemType.IsAssignableTo())) - return NewArrayInit(arrayElemType, - ((object[])item).Map(x => GetConstantExpression(x, arrayElemType, throwIfStateRequired))); - - var itemExpr = Rules.ItemToExpressionConverter?.Invoke(item, itemType); - if (itemExpr != null) - return itemExpr; - - Throw.If(throwIfStateRequired || Rules.ThrowIfRuntimeStateRequired, - Error.StateIsRequiredToUseItem, item); - - return itemType == null ? Constant(item) : Constant(item, itemType); - } - - private static readonly MethodInfo _kvOfMethod = - typeof(KV).GetTypeInfo().GetDeclaredMethod(nameof(KV.Of)); - -#endregion - -#region Factories Add/Get - - internal sealed class FactoriesEntry - { - public readonly DefaultKey LastDefaultKey; - public readonly ImHashMap Factories; - - // lastDefaultKey may be null - public FactoriesEntry(DefaultKey lastDefaultKey, ImHashMap factories) - { - LastDefaultKey = lastDefaultKey; - Factories = factories; - } - - public static readonly FactoriesEntry Empty = - new FactoriesEntry(null, ImHashMap.Empty); - - public FactoriesEntry With(Factory factory, object serviceKey = null) - { - var lastDefaultKey = serviceKey == null - ? LastDefaultKey == null ? DefaultKey.Value : LastDefaultKey.Next() - : LastDefaultKey; - - return new FactoriesEntry(lastDefaultKey, Factories.AddOrUpdate(serviceKey ?? lastDefaultKey, factory)); - } - } - - private KV[] CombineRegisteredWithDynamicFactories( - KV[] registeredFactories, bool bothClosedAndOpenGenerics, - FactoryType factoryType, Type serviceType, object serviceKey = null) - { - Rules.DynamicRegistrationProvider[] dynamicRegistrationProviders = Rules.DynamicRegistrationProviders; - var serviceGenericTypeDefinition = bothClosedAndOpenGenerics && serviceType.IsClosedGeneric() - ? serviceType.GetGenericTypeDefinition() - : null; - - // Assign unique continious keys across all of dynamic providers, - // to prevent duplicate keys and peeking the wrong factory by collection wrappers - // NOTE: Given that dynamic registration always return the same implementation types in the same order - // then the dynamic key will be assigned deterministically, so that even if `CombineRegisteredWithDynamicFactories` - // is called multiple times during the resolution (like for `ResolveMany`) it is possible to match the required factory by its order. - var dynamicKey = DefaultDynamicKey.Value; - - var resultFactories = registeredFactories; - for (var i = 0; i < dynamicRegistrationProviders.Length; i++) - { - var dynamicRegistrationProvider = dynamicRegistrationProviders[i]; - - var dynamicRegistrations = dynamicRegistrationProvider(serviceType, serviceKey).ToArrayOrSelf(); - if (serviceGenericTypeDefinition != null) - dynamicRegistrations = dynamicRegistrations.Append(dynamicRegistrationProvider(serviceGenericTypeDefinition, serviceKey).ToArrayOrSelf()); - if (dynamicRegistrations.IsNullOrEmpty()) - continue; - - if (resultFactories == null) - { - resultFactories = dynamicRegistrations.Match(x => - x.Factory.FactoryType == factoryType && - x.Factory.ValidateAndNormalizeRegistration(serviceType, serviceKey, false, Rules), - x => KV.Of(x.ServiceKey ?? (dynamicKey = dynamicKey.Next()), x.Factory)); - continue; - } - - var remainingDynamicFactories = dynamicRegistrations - .Match(x => - { - if (x.Factory.FactoryType != factoryType || - !x.Factory.ValidateAndNormalizeRegistration(serviceType, serviceKey, false, Rules)) - return false; - - if (x.ServiceKey == null) // for the default dynamic factory - { - switch (x.IfAlreadyRegistered) - { - // accept the default if result factories don't contain it already - case IfAlreadyRegistered.Keep: - case IfAlreadyRegistered.Throw: - return resultFactories.IndexOf(f => f.Key is DefaultKey || f.Key is DefaultDynamicKey) == -1; - - // remove the default from the result factories - case IfAlreadyRegistered.Replace: - resultFactories = resultFactories.Match(f => !(f.Key is DefaultKey || f.Key is DefaultDynamicKey)); - return true; - - case IfAlreadyRegistered.AppendNotKeyed: - return true; - - case IfAlreadyRegistered.AppendNewImplementation: - // if we cannot access to dynamic implementation type, assume that the type is new implementation - if (!x.Factory.CanAccessImplementationType) - return true; - - // keep dynamic factory if there is no result factory with the same implementation type - return resultFactories.IndexOf(x, (s, f) => - f.Value.CanAccessImplementationType && f.Value.ImplementationType == s.Factory.ImplementationType) == -1; - } - } - else // for the keyed dynamic factory - { - switch (x.IfAlreadyRegistered) - { - // remove the result factory with the same key - case IfAlreadyRegistered.Replace: - resultFactories = resultFactories.Match(x.ServiceKey, (key, f) => !f.Key.Equals(key)); - return true; - - // keep the dynamic factory with the new service key, otherwise skip it - default: - return resultFactories.IndexOf(x.ServiceKey, (key, f) => f.Key.Equals(key)) == -1; - } - } - - return true; - }, - x => KV.Of(x.ServiceKey ?? (dynamicKey = dynamicKey.Next()), x.Factory)); - - resultFactories = resultFactories.Append(remainingDynamicFactories); - } - - return resultFactories; - } - - private static readonly LastFactoryIDWinsComparer _lastFactoryIDWinsComparer = new LastFactoryIDWinsComparer(); - private struct LastFactoryIDWinsComparer : IComparer> - { - public int Compare(KV first, KV next) => - (first?.Value.FactoryID ?? 0) - (next?.Value.FactoryID ?? 0); - } - - private Factory GetWrapperFactoryOrDefault(Request request) - { - // wrapper ignores the service key and propagates the service key to wrapped service - var serviceType = request.GetActualServiceType(); - - var serviceTypeInfo = serviceType.GetTypeInfo(); - if (serviceTypeInfo.IsArray) - serviceType = typeof(IEnumerable<>).MakeGenericType(serviceTypeInfo.GetElementType()); - - var factory = ((IContainer)this).GetWrapperFactoryOrDefault(serviceType); - if (factory?.FactoryGenerator != null) - factory = factory.FactoryGenerator.GetGeneratedFactory(request); - - if (factory == null) - return null; - - var condition = factory.Setup.Condition; - if (condition != null && !condition(request)) - return null; - - return factory; - } - -#endregion - -#region Implementation - - private int _disposed; - private StackTrace _disposeStackTrace; - - internal readonly Ref _registry; - - private readonly IScope _singletonScope; - private readonly IScope _ownCurrentScope; - private readonly IScopeContext _scopeContext; - private readonly IResolverContext _parent; - - internal sealed class InstanceFactory : Factory - { - public override Type ImplementationType { get; } - public override bool HasRuntimeState => true; - - public InstanceFactory(object instance, Type instanceType, IReuse reuse, IScope scopeToAdd = null) : base(reuse) - { - ImplementationType = instanceType; - scopeToAdd?.SetOrAdd(FactoryID, instance); - } - - /// Switched off until I (or someone) will figure it out. - public override bool UseInterpretation(Request request) => false; - - /// Tries to return instance directly from scope or sigleton, and fallbacks to expression for decorator. - public override FactoryDelegate GetDelegateOrDefault(Request request) - { - if (request.IsResolutionRoot) - { - var decoratedExpr = request.Container.GetDecoratorExpressionOrDefault(request.WithResolvedFactory(this)); - if (decoratedExpr != null) - return decoratedExpr.CompileToFactoryDelegate(request.Rules.UseFastExpressionCompiler, request.Rules.UseInterpretation); - } - - return GetInstanceFromScopeChainOrSingletons; - } - - /// Called for Injection as dependency. - public override Expression GetExpressionOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - return request.Container.GetDecoratorExpressionOrDefault(request) - ?? CreateExpressionOrDefault(request); - } - - public override Expression CreateExpressionOrDefault(Request request) => - Resolver.CreateResolutionExpression(request); - -#region Implementation - - private object GetInstanceFromScopeChainOrSingletons(IResolverContext r) - { - for (var scope = r.CurrentScope; scope != null; scope = scope.Parent) - { - var result = GetAndUnwrapOrDefault(scope, FactoryID); - if (result != null) - return result; - } - - var instance = GetAndUnwrapOrDefault(r.SingletonScope, FactoryID); - return instance.ThrowIfNull(Error.UnableToFindSingletonInstance); - } - - private static object GetAndUnwrapOrDefault(IScope scope, int factoryId) - { - object value; - if (!scope.TryGet(out value, factoryId)) - return null; - return (value as WeakReference)?.Target.ThrowIfNull(Error.WeakRefReuseWrapperGCed) - ?? (value as HiddenDisposable)?.Value - ?? value; - } - -#endregion - } - - internal sealed class Registry - { - public static readonly Registry Empty = new Registry(); - public static readonly Registry Default = new Registry(WrappersSupport.Wrappers); - - // Factories: - public readonly ImMap> Services; - // todo: we may use Factory or Factory[] as a value for decorators - public readonly ImMap> Decorators; // value is Factory[] - public readonly ImMap> Wrappers; // value is Factory - - internal const int CACHE_SLOT_COUNT = 16; - internal const int CACHE_SLOT_COUNT_MASK = CACHE_SLOT_COUNT - 1; - - public sealed class Compiling - { - public readonly Expression Expression; - public Compiling(Expression expression) => Expression = expression; - } - - public ImMap>[] DefaultFactoryCache; - - [MethodImpl((MethodImplOptions)256)] - public ImMapEntry> GetCachedDefaultFactoryOrDefault(int serviceTypeHash, Type serviceType) - { - // copy to local `cache` will prevent NRE if cache is set to null from outside - var cache = DefaultFactoryCache; - return cache == null ? null : cache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]?.GetEntryOrDefault(serviceTypeHash, serviceType); - } - - public void TryCacheDefaultFactory(int serviceTypeHash, Type serviceType, T factory) - { - // Disable caching when no services registered, not to cache an empty collection wrapper or alike. - if (Services.IsEmpty) - return; - - if (DefaultFactoryCache == null) - Interlocked.CompareExchange(ref DefaultFactoryCache, new ImMap>[CACHE_SLOT_COUNT], null); - - ref var map = ref DefaultFactoryCache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]; - if (map == null) - Interlocked.CompareExchange(ref map, ImMap>.Empty, null); - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrUpdate(serviceTypeHash, serviceType, factory), m) != m) - Ref.Swap(ref map, serviceTypeHash, serviceType, factory, (x, h, t, f) => x.AddOrUpdate(h, t, f)); - } - - internal sealed class KeyedFactoryCacheEntry - { - public readonly KeyedFactoryCacheEntry Rest; - public readonly object Key; - public object Factory; - public KeyedFactoryCacheEntry(KeyedFactoryCacheEntry rest, object key, object factory) - { - Rest = rest; - Key = key; - Factory = factory; - } - } - - // Where key is `KV.Of(ServiceKey | ScopeName | RequiredServiceType | KV.Of(ServiceKey, ScopeName | RequiredServiceType) | ...)` - // and value is `KeyedFactoryCacheEntries` - public ImMap>[] KeyedFactoryCache; - - [MethodImpl((MethodImplOptions)256)] - public bool GetCachedKeyedFactoryOrDefault(int serviceTypeHash, Type serviceType, object key, out KeyedFactoryCacheEntry result) - { - result = null; - var cache = KeyedFactoryCache; - if (cache != null) - { - var entry = cache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]?.GetEntryOrDefault(serviceTypeHash, serviceType); - if (entry != null) - for (var x = (KeyedFactoryCacheEntry)entry.Value.Value; x != null && result == null; x = x.Rest) - if (x.Key.Equals(key)) - result = x; - } - - return result != null; - } - - public void TryCacheKeyedFactory(int serviceTypeHash, Type serviceType, object key, object factory) - { - // Disable caching when no services registered, not to cache an empty collection wrapper or alike. - if (Services.IsEmpty) - return; - - if (KeyedFactoryCache == null) - Interlocked.CompareExchange(ref KeyedFactoryCache, new ImMap>[CACHE_SLOT_COUNT], null); - - ref var map = ref KeyedFactoryCache[serviceTypeHash & CACHE_SLOT_COUNT_MASK]; - if (map == null) - Interlocked.CompareExchange(ref map, ImMap>.Empty, null); - - var entry = map.GetEntryOrDefault(serviceTypeHash, serviceType); - if (entry == null) - { - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(serviceTypeHash, serviceType), m) != m) - Ref.Swap(ref map, serviceTypeHash, serviceType, (x, h, t) => x.AddOrKeep(h, t)); - entry = map.GetEntryOrDefault(serviceTypeHash, serviceType); - } - - var e = entry.Value.Value; - if (Interlocked.CompareExchange(ref entry.Value.Value, SetOrAddKeyedCacheFactory(e, key, factory), e) != e) - Ref.Swap(ref entry.Value.Value, key, factory, SetOrAddKeyedCacheFactory); - } - - private object SetOrAddKeyedCacheFactory(object x, object k, object f) - { - for (var entry = (KeyedFactoryCacheEntry)x; entry != null; entry = entry.Rest) - { - if (entry.Key.Equals(k)) - { - entry.Factory = f; - return x; - } - } - - return new KeyedFactoryCacheEntry((KeyedFactoryCacheEntry)x, k, f); - } - - internal struct ExpressionCacheSlot - { - public Expression Transient; - public Expression Scoped; - public KeyValuePair[] ScopedToName; - } - - /// The int key is the `FactoryID` - public ImMap[] FactoryExpressionCache; - - public Expression GetCachedFactoryExpression( - int factoryId, Request request, out ImMapEntry entry) - { - entry = null; - var cache = FactoryExpressionCache; - if (cache != null) - { - var map = cache[factoryId & CACHE_SLOT_COUNT_MASK]; - if (map != null) - { - entry = map.GetEntryOrDefault(factoryId); - if (entry != null) - { - var reuse = request.Reuse; - if (reuse == Reuse.Transient) - return entry.Value.Transient; - - if (reuse is CurrentScopeReuse scoped) - { - if (scoped.Name == null) - return entry.Value.Scoped; - - var named = entry.Value.ScopedToName; - if (named != null) - for (var i = 0; i < named.Length; i++) - if (Equals(named[i].Key, scoped.Name)) - return named[i].Value; - } - } - } - } - - return null; - } - - internal void CacheFactoryExpression(int factoryId, Request request, Expression expr, ImMapEntry entry = null) - { - if (entry == null) - { - if (FactoryExpressionCache == null) - Interlocked.CompareExchange(ref FactoryExpressionCache, - new ImMap[CACHE_SLOT_COUNT], null); - - ref var map = ref FactoryExpressionCache[factoryId & CACHE_SLOT_COUNT_MASK]; - if (map == null) - Interlocked.CompareExchange(ref map, ImMap.Empty, null); - - entry = map.GetEntryOrDefault(factoryId); - if (entry == null) - { - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(factoryId), m) != m) - Ref.Swap(ref map, factoryId, (x, id) => x.AddOrKeep(id)); - entry = map.GetEntryOrDefault(factoryId); - } - } - - var reuse = request.Reuse; - if (reuse == Reuse.Transient) - { - entry.Value.Transient = expr; - } - else if (reuse is CurrentScopeReuse scoped) - { - if (scoped.Name == null) - entry.Value.Scoped = expr; - else - { - var named = entry.Value.ScopedToName; - if (named == null) - { - entry.Value.ScopedToName = scoped.Name.Pair(expr).One(); - } - else - { - var i = named.Length - 1; - for (; i >= 0; i--) - if (Equals(named[i].Key, scoped.Name)) - break; - if (i == -1) - { - var newNamed = new KeyValuePair[named.Length + 1]; - Array.Copy(named, 0, newNamed, 0, named.Length); - newNamed[named.Length] = scoped.Name.Pair(expr); - entry.Value.ScopedToName = newNamed; - } - } - } - } - } - - private enum IsChangePermitted { Permitted, Error, Ignored } - private readonly IsChangePermitted _isChangePermitted; - - private Registry(ImMap> wrapperFactories = null) - : this(ImMap>.Empty, ImMap>.Empty, wrapperFactories ?? ImMap>.Empty, - null, null, null, // caches are initialized to `null` to quickly check that they - IsChangePermitted.Permitted) - { } - - private Registry( - ImMap> services, - ImMap> decorators, - ImMap> wrappers, - ImMap>[] defaultFactoryCache, - ImMap>[] keyedFactoryCache, - ImMap[] factoryExpressionCache, - IsChangePermitted isChangePermitted) - { - Services = services; - Decorators = decorators; - Wrappers = wrappers; - DefaultFactoryCache = defaultFactoryCache; - KeyedFactoryCache = keyedFactoryCache; - FactoryExpressionCache = factoryExpressionCache; - _isChangePermitted = isChangePermitted; - } - - public Registry WithoutCache() => - new Registry(Services, Decorators, Wrappers, null, null, null, _isChangePermitted); - - internal Registry WithServices(ImMap> services) => - services == Services ? this : - new Registry(services, Decorators, Wrappers, - // Using Copy is fine when you have only the registrations because the caches will be null and no actual copy will be done. - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), - _isChangePermitted); - - private Registry WithDecorators(ImMap> decorators) => - decorators == Decorators ? this : - new Registry(Services, decorators, Wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), _isChangePermitted); - - private Registry WithWrappers(ImMap> wrappers) => - wrappers == Wrappers ? this : - new Registry(Services, Decorators, wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), _isChangePermitted); - - public IEnumerable GetServiceRegistrations() - { - foreach (var entry in Services.Enumerate()) - { - if (entry.Value.Value is Factory factory) - yield return new ServiceRegistrationInfo(factory, entry.Value.Key, null); - else - { - var factories = ((FactoriesEntry)entry.Value.Value).Factories; - foreach (var f in factories.Enumerate()) - yield return new ServiceRegistrationInfo(f.Value, entry.Value.Key, f.Key); - } - } - } - - public Registry Register(Factory factory, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered, object serviceKey) - { - if (_isChangePermitted != IsChangePermitted.Permitted) - return _isChangePermitted == IsChangePermitted.Ignored ? this - : Throw.For(Error.NoMoreRegistrationsAllowed, - serviceType, serviceKey != null ? "with key " + serviceKey : string.Empty, factory); - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - return factory.FactoryType == FactoryType.Service - ? serviceKey == null - ? WithDefaultService(factory, serviceTypeHash, serviceType, ifAlreadyRegistered) - : WithKeyedService(factory, serviceTypeHash, serviceType, ifAlreadyRegistered, serviceKey) - : factory.FactoryType == FactoryType.Decorator - ? WithDecorators(Decorators.AddOrUpdate(serviceTypeHash, serviceType, factory.One(), - (_, oldf, newf) => oldf.To().Append((Factory[])newf))) - : WithWrappers(Wrappers.AddOrUpdate(serviceTypeHash, serviceType, factory)); - } - - public Factory[] GetRegisteredFactories(Type serviceType, object serviceKey, FactoryType factoryType) - { - serviceType = serviceType.ThrowIfNull(); - switch (factoryType) - { - case FactoryType.Wrapper: - { - // first checking for the explicitly provided say `MyWrapper` - if (Wrappers.GetValueOrDefault(serviceType) is Factory wrapper) - return wrapper.One(); - - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null && - Wrappers.GetValueOrDefault(openGenServiceType) is Factory openGenWrapper) - return openGenWrapper.One(); - - if (serviceType.GetArrayElementTypeOrNull() != null && - Wrappers.GetValueOrDefault(typeof(IEnumerable<>)) is Factory collectionWrapper) - return collectionWrapper.One(); - - return null; - } - case FactoryType.Decorator: - { - var decorators = Decorators.GetValueOrDefault(serviceType) as Factory[]; - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null) - decorators = decorators.Append(Decorators.GetValueOrDefault(openGenServiceType) as Factory[]); - return decorators; - } - default: - { - var entry = Services.GetValueOrDefault(serviceType); - if (entry == null) - return null; - - if (entry is Factory factory) - return serviceKey == null || DefaultKey.Value.Equals(serviceKey) ? factory.One() : null; - - var factories = ((FactoriesEntry)entry).Factories; - if (serviceKey == null) // get all the factories - return factories.Visit(new List(), (x, l) => l.Add(x.Value)).ToArray(); - - return factories.GetValueOrDefault(serviceKey)?.One(); - } - } - } - - public bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, - Func condition) - { - serviceType = serviceType.ThrowIfNull(); - switch (factoryType) - { - case FactoryType.Wrapper: - { - // first checking for the explicitly provided say `MyWrapper` - if (Wrappers.GetValueOrDefault(serviceType) is Factory wrapper && - (condition == null || condition(wrapper))) - return true; - - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null && - Wrappers.GetValueOrDefault(openGenServiceType) is Factory openGenWrapper && - (condition == null || condition(openGenWrapper))) - return true; - - if (serviceType.GetArrayElementTypeOrNull() != null && - Wrappers.GetValueOrDefault(typeof(IEnumerable<>)) is Factory collectionWrapper && - (condition == null || condition(collectionWrapper))) - return true; - - return false; - } - case FactoryType.Decorator: - { - if (Decorators.GetValueOrDefault(serviceType) is Factory[] decorators && decorators.Length != 0 && - (condition == null || decorators.FindFirst(condition) != null)) - return true; - - var openGenServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenServiceType != null && - Decorators.GetValueOrDefault(openGenServiceType) is Factory[] openGenDecorators && openGenDecorators.Length != 0 && - (condition == null || openGenDecorators.FindFirst(condition) != null)) - return true; - - return false; - } - default: // services - { - // note: We are not checking the open-generic for the closed-generic service type - // to be able to explicitly understand what registration is available - open or the closed-generic - var entry = Services.GetValueOrDefault(serviceType); - if (entry == null) - return false; - - if (entry is Factory factory) - return serviceKey == null || DefaultKey.Value.Equals(serviceKey) - ? condition == null || condition(factory) - : false; - - var factories = ((FactoriesEntry)entry).Factories; - if (serviceKey == null) - return condition == null || factories.FindFirstOrDefault(f => condition(f.Value)) != null; - - factory = factories.GetValueOrDefault(serviceKey); - return factory != null && (condition == null || condition(factory)); - } - } - } - - public bool ClearCache(int hash, Type serviceType, object serviceKey, FactoryType factoryType) - { - var factories = GetRegisteredFactories(serviceType, serviceKey, factoryType); - if (factories.IsNullOrEmpty()) - return false; - - for (var i = 0; i < factories.Length; i++) - DropFactoryCache(factories[i], hash, serviceType, serviceKey); - - return true; - } - private Registry WithDefaultService(Factory factory, int serviceTypeHash, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered) - { - var services = Services; - object newEntry = factory; - var oldEntry = services.GetValueOrDefault(serviceTypeHash, serviceType); - if (oldEntry != null) - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.AppendNotKeyed: - newEntry = (oldEntry as FactoriesEntry ?? FactoriesEntry.Empty.With((Factory)oldEntry)).With(factory); - break; - case IfAlreadyRegistered.Throw: - newEntry = oldEntry is FactoriesEntry oldFactoriesEntry && oldFactoriesEntry.LastDefaultKey == null - ? oldFactoriesEntry.With(factory) - : Throw.For(Error.UnableToRegisterDuplicateDefault, serviceType, factory, oldEntry); - break; - case IfAlreadyRegistered.Replace: - if (oldEntry is FactoriesEntry facEntryToReplace) - { - if (facEntryToReplace.LastDefaultKey == null) - newEntry = facEntryToReplace.With(factory); - else - { - // remove defaults but keep keyed (issue #569) by collecting the only keyed factories - // and using them in a new factory entry - var keyedFactories = facEntryToReplace.Factories.Fold( - ImHashMap.Empty, - (x, map) => x.Key is DefaultKey == false ? map.AddOrUpdate(x.Key, x.Value) : map); - if (!keyedFactories.IsEmpty) - newEntry = new FactoriesEntry(DefaultKey.Value, - keyedFactories.AddOrUpdate(DefaultKey.Value, factory)); - } - } - break; - case IfAlreadyRegistered.AppendNewImplementation: - var oldImplFacsEntry = oldEntry as FactoriesEntry; - if (oldImplFacsEntry != null && oldImplFacsEntry.LastDefaultKey == null) - newEntry = oldImplFacsEntry.With(factory); - else - { - var oldFactory = oldEntry as Factory; - var implementationType = factory.ImplementationType; - if (implementationType == null || - oldFactory != null && oldFactory.ImplementationType != implementationType) - newEntry = (oldImplFacsEntry ?? FactoriesEntry.Empty.With(oldFactory)).With(factory); - else if (oldImplFacsEntry != null) - { - var isNewImplType = true; - foreach (var f in oldImplFacsEntry.Factories.Enumerate()) - if (f.Value.ImplementationType == implementationType) - { - isNewImplType = false; - break; - } - - newEntry = isNewImplType - ? (oldImplFacsEntry ?? FactoriesEntry.Empty.With(oldFactory)).With(factory) - : oldEntry; - } - } - break; - default: // IfAlreadyRegisteredKeepDefaultService - newEntry = oldEntry is FactoriesEntry oldFacsEntry && oldFacsEntry.LastDefaultKey == null - ? oldFacsEntry.With(factory) - : oldEntry; - break; - } - } - - // services did not change - if (newEntry == oldEntry) - return this; - - var newServices = services.AddOrUpdate(serviceTypeHash, serviceType, newEntry); - var newRegistry = new Registry(newServices, Decorators, Wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), _isChangePermitted); - - if (oldEntry != null) - { - if (oldEntry is Factory oldFactory) - newRegistry.DropFactoryCache(oldFactory, serviceTypeHash, serviceType); - else if (oldEntry is FactoriesEntry oldFactoriesEntry && oldFactoriesEntry?.LastDefaultKey != null) - oldFactoriesEntry.Factories.Visit(new{ newRegistry, serviceTypeHash, serviceType }, (x, s) => - { - if (x.Key is DefaultKey) - s.newRegistry.DropFactoryCache(x.Value, s.serviceTypeHash, s.serviceType); - }); - } - - return newRegistry; - } - - private Registry WithKeyedService(Factory factory, int serviceTypeHash, Type serviceType, IfAlreadyRegistered ifAlreadyRegistered, object serviceKey) - { - object newEntry = null; - var services = Services; - var oldEntry = services.GetValueOrDefault(serviceTypeHash, serviceType); - if (oldEntry != null) - { - switch (ifAlreadyRegistered) - { - case IfAlreadyRegistered.Keep: - if (oldEntry is Factory factoryToKeep) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey).With(factoryToKeep); - else - { - var oldFacs = (FactoriesEntry)oldEntry; - if (oldFacs.Factories.Contains(serviceKey)) - return this; // keep the old registry - newEntry = new FactoriesEntry(oldFacs.LastDefaultKey, - oldFacs.Factories.AddOrUpdate(serviceKey, factory)); - } - break; - case IfAlreadyRegistered.Replace: - if (oldEntry is Factory factoryToReplace) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey).With(factoryToReplace); - else - newEntry = new FactoriesEntry(((FactoriesEntry)oldEntry).LastDefaultKey, - ((FactoriesEntry)oldEntry).Factories.AddOrUpdate(serviceKey, factory)); - break; - default: - if (oldEntry is Factory defaultFactory) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey).With(defaultFactory); - else - { - var oldFacs = (FactoriesEntry)oldEntry; - var oldFac = oldFacs.Factories.GetValueOrDefault(serviceKey); - if (oldFac != null) - Throw.It(Error.UnableToRegisterDuplicateKey, serviceKey, serviceKey, oldFac); - newEntry = new FactoriesEntry(oldFacs.LastDefaultKey, - ((FactoriesEntry)oldEntry).Factories.AddOrUpdate(serviceKey, factory)); - } - break; - } - } - - if (newEntry == null) - newEntry = FactoriesEntry.Empty.With(factory, serviceKey); - - var newServices = services.AddOrUpdate(serviceTypeHash, serviceType, newEntry); - var newRegistry = new Registry(newServices, Decorators, Wrappers, - DefaultFactoryCache.Copy(), KeyedFactoryCache.Copy(), FactoryExpressionCache.Copy(), - _isChangePermitted); - - if (oldEntry != null && ifAlreadyRegistered == IfAlreadyRegistered.Replace && - oldEntry is FactoriesEntry updatedOldFactories && - updatedOldFactories.Factories.TryFind(serviceKey, out var droppedFactory)) - newRegistry.DropFactoryCache(droppedFactory, serviceTypeHash, serviceType, serviceKey); - - return newRegistry; - } - - // todo: optimize allocations away - public Registry Unregister(FactoryType factoryType, Type serviceType, object serviceKey, Func condition) - { - if (_isChangePermitted != IsChangePermitted.Permitted) - return _isChangePermitted == IsChangePermitted.Ignored ? this - : Throw.For(Error.NoMoreUnregistrationsAllowed, - serviceType, serviceKey != null ? "with key " + serviceKey : string.Empty, factoryType); - - var serviceTypeHash = RuntimeHelpers.GetHashCode(serviceType); - switch (factoryType) - { - case FactoryType.Wrapper: - object removedWrapper = null; - var registry = WithWrappers(Wrappers.Update(serviceTypeHash, serviceType, null, (_, factory, _null) => - { - if (factory != null && condition != null && !condition((Factory)factory)) - return factory; - removedWrapper = factory; - return null; - })); - - if (removedWrapper == null) - return this; - registry.DropFactoryCache((Factory)removedWrapper, serviceTypeHash, serviceType); - return registry; - - case FactoryType.Decorator: - Factory[] removedDecorators = null; - // todo: minimize allocations in the lambdas below - if (condition == null) - registry = WithDecorators(Decorators.Update(serviceTypeHash, serviceType, null, (_, factories, _null) => - { - removedDecorators = (Factory[])factories; - return null; - })); - else - registry = WithDecorators(Decorators.Update(serviceTypeHash, serviceType, null, (_, factories, _null) => - { - removedDecorators = ((Factory[])factories).Match(condition); - return removedDecorators == factories ? null : factories.To().Except(removedDecorators).ToArray(); - })); - - if (removedDecorators.IsNullOrEmpty()) - return this; - - for (var i = 0; i < removedDecorators.Length; i++) - registry.DropFactoryCache(removedDecorators[i], serviceTypeHash, serviceType); - - return registry; - - default: - return UnregisterServiceFactory(serviceType, serviceKey, condition); - } - } - - // todo: optimize allocations away - private Registry UnregisterServiceFactory(Type serviceType, object serviceKey = null, Func condition = null) - { - object removed = null; // Factory or FactoriesEntry or Factory[] - ImMap> services; - var hash = RuntimeHelpers.GetHashCode(serviceType); - if (serviceKey == null && condition == null) // simplest case with simplest handling - services = Services.Update(hash, serviceType, null, (_, entry, _null) => - { - removed = entry; - return null; - }); - else - services = Services.Update(hash, serviceType, null, (_, entry, _null) => - { - if (entry == null) - return null; - - if (entry is Factory) - { - if ((serviceKey != null && !DefaultKey.Value.Equals(serviceKey)) || - (condition != null && !condition((Factory)entry))) - return entry; // keep entry - removed = entry; // otherwise remove it (the only case if serviceKey == DefaultKey.Value) - return null; - } - - var factoriesEntry = (FactoriesEntry)entry; - var oldFactories = factoriesEntry.Factories; - var remainingFactories = ImHashMap.Empty; - if (serviceKey == null) // automatically means condition != null - { - // keep factories for which condition is true - remainingFactories = oldFactories.Fold(remainingFactories, - (oldFac, remainingFacs) => condition != null && !condition(oldFac.Value) - ? remainingFacs.AddOrUpdate(oldFac.Key, oldFac.Value) - : remainingFacs); - } - else // serviceKey is not default, which automatically means condition == null - { - // set to null factory with specified key if its found - remainingFactories = oldFactories; - var factory = oldFactories.GetValueOrDefault(serviceKey); - if (factory != null) - remainingFactories = oldFactories.Height > 1 - ? oldFactories.UpdateToDefault(serviceKey.GetHashCode(), serviceKey) - : ImHashMap.Empty; - } - - if (remainingFactories.IsEmpty) - { - // if no more remaining factories, then delete the whole entry - removed = entry; - return null; - } - - // todo: huh - no perf here? - removed = oldFactories.Enumerate().Except(remainingFactories.Enumerate()).Select(f => f.Value).ToArray(); - - if (remainingFactories.Height == 1 && DefaultKey.Value.Equals(remainingFactories.Key)) - return remainingFactories.Value; // replace entry with single remaining default factory - - // update last default key if current default key was removed - var newDefaultKey = factoriesEntry.LastDefaultKey; - if (newDefaultKey != null && remainingFactories.GetValueOrDefault(newDefaultKey) == null) - newDefaultKey = remainingFactories.Enumerate().Select(x => x.Key) - .OfType().OrderByDescending(key => key.RegistrationOrder).FirstOrDefault(); - return new FactoriesEntry(newDefaultKey, remainingFactories); - }); - - if (removed == null) - return this; - - var registry = WithServices(services); - - var removedFactory = removed as Factory; - if (removedFactory != null) - registry.DropFactoryCache(removedFactory, hash, serviceType, serviceKey); - else - (removed as Factory[] ?? - ((FactoriesEntry)removed).Factories.Enumerate().Select(f => f.Value).ToArray()) - .ForEach(x => registry.DropFactoryCache(x, hash, serviceType, serviceKey)); - - return registry; - } - - internal void DropFactoryCache(Factory factory, int hash, Type serviceType, object serviceKey = null) - { - if (DefaultFactoryCache != null || KeyedFactoryCache != null) - { - if (factory.FactoryGenerator == null) - { - var d = DefaultFactoryCache; - if (d != null) - Ref.Swap(ref d[hash & CACHE_SLOT_COUNT_MASK], hash, serviceType, - (x, h, t) => (x ?? ImMap>.Empty).UpdateToDefault(h, t)); - - var k = KeyedFactoryCache; - if (k != null) - Ref.Swap(ref k[hash & CACHE_SLOT_COUNT_MASK], hash, serviceType, - (x, h, t) => (x ?? ImMap>.Empty).UpdateToDefault(h, t)); - } - else - { - // We cannot remove generated factories, because they are keyed by implementation type and we may remove wrong factory - // a safe alternative is dropping the whole cache - DefaultFactoryCache = null; - KeyedFactoryCache = null; - } - } - - if (FactoryExpressionCache != null) - { - var e = FactoryExpressionCache; - if (e != null) - Ref.Swap(ref e[factory.FactoryID & CACHE_SLOT_COUNT_MASK], - factory.FactoryID, (x, i) => (x ?? ImMap.Empty).UpdateToDefault(i)); - } - } - - public Registry WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow) => - new Registry(Services, Decorators, Wrappers, - DefaultFactoryCache, KeyedFactoryCache, FactoryExpressionCache, - ignoreInsteadOfThrow ? IsChangePermitted.Ignored : IsChangePermitted.Error); - } - - private Container(Rules rules, Ref registry, IScope singletonScope, - IScopeContext scopeContext = null, IScope ownCurrentScope = null, - int disposed = 0, StackTrace disposeStackTrace = null, - IResolverContext parent = null) - { - Rules = rules; - - _registry = registry; - - _singletonScope = singletonScope; - _scopeContext = scopeContext; - _ownCurrentScope = ownCurrentScope; - - _disposed = disposed; - _disposeStackTrace = disposeStackTrace; - - _parent = parent; - } - - private void SetInitialFactoryID() - { - var lastGeneratedId = 0; - GetLastGeneratedFactoryID(ref lastGeneratedId); - if (lastGeneratedId > Factory._lastFactoryID) - Factory._lastFactoryID = lastGeneratedId + 1; - } - - #endregion - } - - /// Special service key with info about open-generic service type - public sealed class OpenGenericTypeKey : IConvertibleToExpression - { - /// Open-generic required service-type - public readonly Type RequiredServiceType; - - /// Optional key - public readonly object ServiceKey; - - /// Constructs the thing - public OpenGenericTypeKey(Type requiredServiceType, object serviceKey) - { - RequiredServiceType = requiredServiceType.ThrowIfNull(); - ServiceKey = serviceKey; - } - - /// - public override string ToString() => - new StringBuilder(nameof(OpenGenericTypeKey)).Append('(') - .Print(RequiredServiceType).Append(", ").Print(ServiceKey) - .Append(')').ToString(); - - /// - public override bool Equals(object obj) - { - var other = obj as OpenGenericTypeKey; - return other != null && - other.RequiredServiceType == RequiredServiceType && - Equals(other.ServiceKey, ServiceKey); - } - - /// - public override int GetHashCode() => Hasher.Combine(RequiredServiceType, ServiceKey); - - /// - public Expression ToExpression(Func fallbackConverter) => - New(_ctor, Constant(RequiredServiceType, typeof(Type)), fallbackConverter(ServiceKey)); - - private static readonly ConstructorInfo _ctor = typeof(OpenGenericTypeKey) - .GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 2); - } - - ///Hides/wraps object with disposable interface. - public sealed class HiddenDisposable - { - internal static ConstructorInfo Ctor = typeof(HiddenDisposable).GetTypeInfo().DeclaredConstructors.First(); - internal static FieldInfo ValueField = typeof(HiddenDisposable).GetTypeInfo().GetDeclaredField(nameof(Value)); - - /// Wrapped value - public readonly object Value; - - /// Wraps the value - public HiddenDisposable(object value) { Value = value; } - } - - /// Interpreter of expression - where possible uses knowledge of DryIoc internals to avoid reflection - public static class Interpreter - { - /// Calls `TryInterpret` inside try-catch and unwraps/re-throws `ContainerException` from the reflection `TargetInvocationException` - public static bool TryInterpretAndUnwrapContainerException( - IResolverContext r, Expression expr, bool useFec, out object result) - { - try - { - return Interpreter.TryInterpret(r, expr, FactoryDelegateCompiler.ResolverContextParamExpr, r, null, useFec, out result); - } - catch (TargetInvocationException tex) when (tex.InnerException != null) - { - // restore the original exception which is expected by the consumer code - tex.InnerException.TryRethrowWithPreservedStackTrace(); - result = null; - return false; - } - } - - /// Stores parent lambda params and args - public sealed class ParentLambdaArgs - { - /// Parent or the `null` for the root - public readonly ParentLambdaArgs ParentWithArgs; - - /// Params - public readonly object ParamExprs; - - /// Args - public readonly object ParamValues; - - /// Constructs with parent parent or `null` for the root - public ParentLambdaArgs(ParentLambdaArgs parentWithArgs, object paramExprs, object paramValues) - { - ParentWithArgs = parentWithArgs; - ParamExprs = paramExprs; - ParamValues = paramValues; - } - } - - //private static object GetPoolOrNewObjects(object[][] objsPool, int count) => - // count < 8 ? objsPool[count - 1] ?? new object[count] : new object[count]; - - //private static void ReturnObjecstsToPool(object[][] objsPool, object[] objs) - //{ - // var length = objs.Length; - // if (length < 8) - // objsPool[length - 1] = objs; - //} - - /// Interprets passed expression - public static bool TryInterpret(IResolverContext r, Expression expr, - object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec, out object result - //, object[][] objsPools = null - ) - { - result = null; - switch (expr.NodeType) - { - case ExprType.Constant: - { - result = ((ConstantExpression)expr).Value; - return true; - } - case ExprType.New: - { - var newExpr = (NewExpression)expr; -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgCount = newExpr.FewArgumentCount; - if (fewArgCount >= 0) - { - if (fewArgCount == 0) - { - result = newExpr.Constructor.Invoke(ArrayTools.Empty()); - return true; - } - - if (fewArgCount == 1) - { - var fewArgs = new object[1]; - var fewArgsExpr = ((OneArgumentNewExpression)newExpr).Argument; - if (!TryInterpret(r, fewArgsExpr, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - - if (fewArgCount == 2) - { - var fewArgs = new object[2]; - var fewArgsExpr = ((TwoArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - - if (fewArgCount == 3) - { - var fewArgs = new object[3]; - var fewArgsExpr = ((ThreeArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - - if (fewArgCount == 4) - { - var fewArgs = new object[4]; - var fewArgsExpr = ((FourArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - if (fewArgCount == 5) - { - var fewArgs = new object[5]; - var fewArgsExpr = ((FiveArgumentsNewExpression)newExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3]) || - !TryInterpret(r, fewArgsExpr.Argument4, paramExprs, paramValues, parentArgs, useFec, out fewArgs[4])) - return false; - result = newExpr.Constructor.Invoke(fewArgs); - return true; - } - } -#endif - var newArgs = newExpr.Arguments.ToListOrSelf(); - var newArgCount = newArgs.Count; - if (newArgCount == 0) - result = newExpr.Constructor.Invoke(ArrayTools.Empty()); - else - { - var args = new object[newArgCount]; - for (var i = 0; i < args.Length; i++) - if (!TryInterpret(r, newArgs[i], paramExprs, paramValues, parentArgs, useFec, out args[i])) - return false; - result = newExpr.Constructor.Invoke(args); - } - return true; - } - case ExprType.Call: - { - return TryInterpretMethodCall(r, expr, paramExprs, paramValues, parentArgs, useFec, ref result); - } - case ExprType.Convert: - { - var convertExpr = (UnaryExpression)expr; - if (!TryInterpret(r, convertExpr.Operand, paramExprs, paramValues, parentArgs, useFec, out var instance)) - return false; - // skip conversion for null and for directly assignable type - if (instance == null || instance.GetType().IsAssignableTo(convertExpr.Type)) - result = instance; - else - result = Converter.ConvertWithOperator(instance, convertExpr.Type, expr); - return true; - } - case ExprType.MemberAccess: - { - var memberExpr = (MemberExpression)expr; - var instanceExpr = memberExpr.Expression; - object instance = null; - if (instanceExpr != null && !TryInterpret(r, instanceExpr, paramExprs, paramValues, parentArgs, useFec, out instance)) - return false; - - if (memberExpr.Member is FieldInfo field) - { - result = field.GetValue(instance); - return true; - } - - if (memberExpr.Member is PropertyInfo prop) - { - result = prop.GetValue(instance, null); - return true; - } - - return false; - } - case ExprType.MemberInit: - { - var memberInit = (MemberInitExpression)expr; - if (!TryInterpret(r, memberInit.NewExpression, paramExprs, paramValues, parentArgs, useFec, out var instance)) - return false; - - var bindings = memberInit.Bindings; - for (var i = 0; i < bindings.Count; i++) - { - var binding = (MemberAssignment)bindings[i]; - if (!TryInterpret(r, binding.Expression, paramExprs, paramValues, parentArgs, useFec, out var memberValue)) - return false; - - var field = binding.Member as FieldInfo; - if (field != null) - field.SetValue(instance, memberValue); - else - ((PropertyInfo)binding.Member).SetValue(instance, memberValue, null); - } - - result = instance; - return true; - } - case ExprType.NewArrayInit: - { - var newArray = (NewArrayExpression)expr; - var itemExprs = newArray.Expressions.ToListOrSelf(); - var items = new object[itemExprs.Count]; - - for (var i = 0; i < items.Length; i++) - if (!TryInterpret(r, itemExprs[i], paramExprs, paramValues, parentArgs, useFec, out items[i])) - return false; - - result = Converter.ConvertMany(items, newArray.Type.GetElementType()); - return true; - } - case ExprType.Invoke: - { - var invokeExpr = (InvocationExpression)expr; - var delegateExpr = invokeExpr.Expression; - - // The majority of cases the delegate will be a well known `FactoryDelegate` - so calling it directly - if (delegateExpr.Type == typeof(FactoryDelegate) && - delegateExpr is ConstantExpression delegateConstExpr) - { - if (!TryInterpret(r, invokeExpr.Arguments[0], paramExprs, paramValues, parentArgs, useFec, out var resolver)) - return false; - result = ((FactoryDelegate)delegateConstExpr.Value)((IResolverContext)resolver); - return true; - } - -#if !SUPPORTS_DELEGATE_METHOD - return false; -#else - if (!TryInterpret(r, delegateExpr, paramExprs, paramValues, parentArgs, useFec, out var delegateObj)) - return false; - var lambda = (Delegate)delegateObj; - - var argExprs = invokeExpr.Arguments.ToListOrSelf(); - if (argExprs.Count == 0) - result = lambda.GetMethodInfo().Invoke(lambda.Target, ArrayTools.Empty()); - else - { - var args = new object[argExprs.Count]; - for (var i = 0; i < args.Length; i++) - if (!TryInterpret(r, argExprs[i], paramExprs, paramValues, parentArgs, useFec, out args[i])) - return false; - result = lambda.GetMethodInfo().Invoke(lambda.Target, args); - } - return true; -#endif - } - case ExprType.Parameter: - { - if (expr == paramExprs) - { - result = paramValues; - return true; - } - - if (paramExprs is IList multipleParams) - for (var i = 0; i < multipleParams.Count; i++) - if (expr == multipleParams[i]) - { - result = ((object[])paramValues)[i]; - return true; - } - - if (parentArgs != null) - { - for (var p = parentArgs; p != null; p = p.ParentWithArgs) - { - if (expr == p.ParamExprs) - { - result = p.ParamValues; - return true; - } - - multipleParams = p.ParamExprs as IList; - if (multipleParams != null) - { - for (var i = 0; i < multipleParams.Count; i++) - if (expr == multipleParams[i]) - { - result = ((object[])paramValues)[i]; - return true; - } - } - } - } - return false; - } - case ExprType.Lambda: - { - return TryInterpretNestedLambda(r, (LambdaExpression)expr, paramExprs, paramValues, parentArgs, useFec, ref result); - } - default: - break; - } - - return false; - } - - private static bool TryInterpretNestedLambda(IResolverContext r, LambdaExpression lambdaExpr, - object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec, ref object result) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var returnType = lambdaExpr.ReturnType; -#else - var returnType = lambdaExpr.Type.GetTypeInfo().GetDeclaredMethod("Invoke").ReturnType; -#endif - if (paramExprs != null) - parentArgs = new ParentLambdaArgs(parentArgs, paramExprs, paramValues); - - var bodyExpr = lambdaExpr.Body; - var lambdaParams = lambdaExpr.Parameters; - var paramCount = lambdaParams.Count; - if (paramCount == 0) - { - if (returnType != typeof(void)) - { - result = new Func(() => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, null, null, parentArgs, useFec)); - if (returnType != typeof(object)) - result = _convertFuncMethod.MakeGenericMethod(returnType).Invoke(null, new[] { result }); - } - else - { - result = new Action(() => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, null, null, parentArgs, useFec)); - } - } - else if (paramCount == 1) - { - var paramExpr = lambdaParams[0]; - if (returnType != typeof(void)) - { - result = new Func(arg => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, paramExpr, arg, parentArgs, useFec)); - if (paramExpr.Type != typeof(object) || returnType != typeof(object)) - result = _convertOneArgFuncMethod.MakeGenericMethod(paramExpr.Type, returnType).Invoke(null, new[] { result }); - } - else - { - result = new Action(arg => TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, paramExpr, arg, parentArgs, useFec)); - if (paramExpr.Type != typeof(object)) - result = _convertOneArgActionMethod.MakeGenericMethod(paramExpr.Type).Invoke(null, new[] { result }); - } - } - else if (paramCount == 2) - { - var paramExpr0 = lambdaParams[0]; - var paramExpr1 = lambdaParams[1]; - if (returnType != typeof(void)) - { - result = new Func((arg0, arg1) => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, new[] { arg0, arg1 }, parentArgs, useFec)); - - if (paramExpr0.Type != typeof(object) || paramExpr1.Type != typeof(object) || returnType != typeof(object)) - result = _convertTwoArgFuncMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, returnType).Invoke(null, new[] { result }); - } - else - { - result = new Action((arg0, arg1) => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, new[] { arg0, arg1 }, parentArgs, useFec)); - - if (paramExpr0.Type != typeof(object) || paramExpr1.Type != typeof(object)) - result = _convertTwoArgActionMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type).Invoke(null, new[] { result }); - } - } - else if (paramCount == 3) - { - var paramExpr0 = lambdaParams[0]; - var paramExpr1 = lambdaParams[1]; - var paramExpr2 = lambdaParams[2]; - if (returnType != typeof(void)) - { - result = new Func(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertThreeArgFuncMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type, returnType) - .Invoke(null, new[] { result }); - } - else - { - result = new Action(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertThreeArgActionMethod.MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type) - .Invoke(null, new[] { result }); - } - } - else if (paramCount == 4) - { - var paramExpr0 = lambdaParams[0]; - var paramExpr1 = lambdaParams[1]; - var paramExpr2 = lambdaParams[2]; - var paramExpr3 = lambdaParams[3]; - if (returnType != typeof(void)) - { - result = new Func(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertFourArgFuncMethod - .MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type, paramExpr3.Type, returnType) - .Invoke(null, new[] {result}); - } - else - { - result = new Action(args => - TryInterpretNestedLambdaBodyAndUnwrapException(r, bodyExpr, lambdaParams, args, parentArgs, useFec)); - result = _convertFourArgActionMethod - .MakeGenericMethod(paramExpr0.Type, paramExpr1.Type, paramExpr2.Type, paramExpr3.Type) - .Invoke(null, new[] {result}); - } - } - else - return false; - - var resultType = result.GetType(); - var lambdaType = lambdaExpr.Type; - if ((resultType.GetGenericDefinitionOrNull() ?? resultType) != (lambdaType.GetGenericDefinitionOrNull() ?? lambdaType)) - { -#if SUPPORTS_DELEGATE_METHOD - result = ((Delegate)result).GetMethodInfo().CreateDelegate(lambdaType, ((Delegate)result).Target); -#else - return false; -#endif - } - - return true; - } - - private static object TryInterpretNestedLambdaBodyAndUnwrapException(IResolverContext r, - Expression bodyExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { - try - { - if (!TryInterpret(r, bodyExpr, paramExprs, paramValues, parentArgs, useFec, out var lambdaResult)) - Throw.It(Error.UnableToInterpretTheNestedLambda, bodyExpr); - return lambdaResult; - } - catch (TargetInvocationException tex) when (tex.InnerException != null) - { - // restore the original excpetion which is expected by the consumer code - throw tex.InnerException; - } - } - - internal static Func ConvertFunc(Func f) => () => (R)f(); - private static readonly MethodInfo _convertFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertFunc)); - - internal static Func ConvertOneArgFunc(Func f) => a => (R)f(a); - private static readonly MethodInfo _convertOneArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertOneArgFunc)); - - internal static Action ConvertOneArgAction(Action f) => a => f(a); - private static readonly MethodInfo _convertOneArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertOneArgAction)); - - internal static Func ConvertTwoArgFunc(Func f) => (a0, a1) => (R)f(a0, a1); - private static readonly MethodInfo _convertTwoArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertTwoArgFunc)); - - internal static Action ConvertTwoArgAction(Action f) => (a0, a1) => f(a0, a1); - private static readonly MethodInfo _convertTwoArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertTwoArgAction)); - - internal static Func ConvertThreeArgFunc(Func f) => (a0, a1, a2) => (R)f(new object[] {a0, a1, a2}); - private static readonly MethodInfo _convertThreeArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertThreeArgFunc)); - - internal static Action ConvertThreeArgAction(Action f) => (a0, a1, a2) => f(new object[] {a0, a1, a2}); - private static readonly MethodInfo _convertThreeArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertThreeArgAction)); - - internal static Func ConvertFourArgFunc(Func f) => (a0, a1, a2, a3) => (R)f(new object[] { a0, a1, a2, a3 }); - private static readonly MethodInfo _convertFourArgFuncMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertFourArgFunc)); - - internal static Action ConvertFourArgAction(Action f) => (a0, a1, a2, a3) => f(new object[] { a0, a1, a2, a3 }); - private static readonly MethodInfo _convertFourArgActionMethod = typeof(Interpreter).GetTypeInfo().GetDeclaredMethod(nameof(ConvertFourArgAction)); - - private static bool TryInterpretMethodCall(IResolverContext r, Expression expr, - object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec, ref object result) - { - if (ReferenceEquals(expr, ResolverContext.RootOrSelfExpr)) - { - result = r.Root ?? r; - return true; - } - - var callExpr = (MethodCallExpression)expr; - var method = callExpr.Method; - var methodDeclaringType = method.DeclaringType; - - if (methodDeclaringType == typeof(CurrentScopeReuse)) - { - if (method == CurrentScopeReuse.GetScopedViaFactoryDelegateNoDisposalIndexMethod) - { - result = InterpretGetScopedViaFactoryDelegateNoDisposalIndex(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - if (method == CurrentScopeReuse.GetScopedViaFactoryDelegateMethod) - { - result = InterpretGetScopedViaFactoryDelegate(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - if (method == CurrentScopeReuse.GetNameScopedViaFactoryDelegateMethod) - { - result = InterpretGetNameScopedViaFactoryDelegate(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - if (method == CurrentScopeReuse.GetScopedOrSingletonViaFactoryDelegateMethod) - { - result = InterpretGetScopedOrSingletonViaFactoryDelegate(r, callExpr, paramExprs, paramValues, parentArgs, useFec); - return true; - } - - var callArgs = callExpr.Arguments.ToListOrSelf(); - var resolver = r; - if (!ReferenceEquals(callArgs[0], FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, callArgs[0], paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - if (method == CurrentScopeReuse.TrackScopedOrSingletonMethod) - { - if (!TryInterpret(resolver, callArgs[1], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = CurrentScopeReuse.TrackScopedOrSingleton(resolver, service); - return true; - } - - if (method == CurrentScopeReuse.TrackScopedMethod) - { - var scope = resolver.GetCurrentScope((bool)((ConstantExpression)callArgs[1]).Value); - if (scope == null) - result = null; // result is null in this case - else - { - if (!TryInterpret(resolver, callArgs[2], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = scope.TrackDisposable(service /* todo: what is with `disposalOrder`*/); - } - - return true; - } - - if (method == CurrentScopeReuse.TrackNameScopedMethod) - { - var scope = resolver.GetNamedScope(ConstValue(callArgs[1]), (bool)ConstValue(callArgs[2])); - if (scope == null) - result = null; // result is null in this case - else - { - if (!TryInterpret(resolver, callArgs[3], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = scope.TrackDisposable(service); - } - - return true; - } - } - else if (methodDeclaringType == typeof(IScope)) - { - var callArgs = callExpr.Arguments.ToListOrSelf(); - if (method == Scope.GetOrAddViaFactoryDelegateMethod) - { - r = r.Root ?? r; - - // check if scoped dependency is already in scope, then just return it - var factoryId = (int) ConstValue(callArgs[0]); - if (!r.SingletonScope.TryGet(out result, factoryId)) - { - result = r.SingletonScope.TryGetOrAddWithoutClosure(factoryId, r, - ((LambdaExpression) callArgs[1]).Body, useFec, - (rc, e, uf) => - { - if (TryInterpret(rc, e, paramExprs, paramValues, parentArgs, uf, out var value)) - return value; - return e.CompileToFactoryDelegate(uf, ((IContainer) rc).Rules.UseInterpretation)(rc); - }, - (int) ConstValue(callArgs[3])); - } - - return true; - } - - if (method == Scope.TrackDisposableMethod) - { - r = r.Root ?? r; - if (!TryInterpret(r, callArgs[0], paramExprs, paramValues, parentArgs, useFec, out var service)) - return false; - result = r.SingletonScope.TrackDisposable(service, (int) ConstValue(callArgs[1])); - return true; - } - } - else if (methodDeclaringType == typeof(IResolver)) - { - var resolver = r; - if (!ReferenceEquals(callExpr.Object, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, callExpr.Object, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - var callArgs = callExpr.Arguments.ToListOrSelf(); - if (method == Resolver.ResolveFastMethod) - { - result = resolver.Resolve((Type) ConstValue(callArgs[0]), (IfUnresolved) ConstValue(callArgs[1])); - return true; - } - - if (method == Resolver.ResolveMethod) - { - object serviceKey = null, preResolveParent = null, resolveArgs = null; - if (!TryInterpret(resolver, callArgs[1], paramExprs, paramValues, parentArgs, useFec, out serviceKey) || - !TryInterpret(resolver, callArgs[4], paramExprs, paramValues, parentArgs, useFec, out preResolveParent) || - !TryInterpret(resolver, callArgs[5], paramExprs, paramValues, parentArgs, useFec, out resolveArgs)) - return false; - - result = resolver.Resolve((Type) ConstValue(callArgs[0]), serviceKey, - (IfUnresolved) ConstValue(callArgs[2]), - (Type) ConstValue(callArgs[3]), (Request) preResolveParent, (object[]) resolveArgs); - return true; - } - - if (method == Resolver.ResolveManyMethod) - { - object serviceKey = null, preResolveParent = null, resolveArgs = null; - if (!TryInterpret(resolver, callArgs[1], paramExprs, paramValues, parentArgs, useFec, out serviceKey) || - !TryInterpret(resolver, callArgs[3], paramExprs, paramValues, parentArgs, useFec, out preResolveParent) || - !TryInterpret(resolver, callArgs[4], paramExprs, paramValues, parentArgs, useFec, out resolveArgs)) - return false; - - result = resolver.ResolveMany((Type) ConstValue(callArgs[0]), serviceKey, (Type) ConstValue(callArgs[2]), - (Request) preResolveParent, (object[]) resolveArgs); - return true; - } - } - - // fallback to reflection invocation - object instance = null; - var callObjectExpr = callExpr.Object; - if (callObjectExpr != null && !TryInterpret(r, callObjectExpr, paramExprs, paramValues, parentArgs, useFec, out instance)) - return false; - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgCount = callExpr.FewArgumentCount; - if (fewArgCount >= 0) - { - if (fewArgCount == 0) - { - result = callExpr.Method.Invoke(instance, ArrayTools.Empty()); - return true; - } - - if (fewArgCount == 1) - { - var fewArgs = new object[1]; - var fewArgsExpr = ((OneArgumentMethodCallExpression)callExpr).Argument; - if (!TryInterpret(r, fewArgsExpr, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - - if (fewArgCount == 2) - { - var fewArgs = new object[2]; - var fewArgsExpr = ((TwoArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - - if (fewArgCount == 3) - { - var fewArgs = new object[3]; - var fewArgsExpr = ((ThreeArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - - if (fewArgCount == 4) - { - var fewArgs = new object[4]; - var fewArgsExpr = ((FourArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - if (fewArgCount == 5) - { - var fewArgs = new object[5]; - var fewArgsExpr = ((FiveArgumentsMethodCallExpression)callExpr); - if (!TryInterpret(r, fewArgsExpr.Argument0, paramExprs, paramValues, parentArgs, useFec, out fewArgs[0]) || - !TryInterpret(r, fewArgsExpr.Argument1, paramExprs, paramValues, parentArgs, useFec, out fewArgs[1]) || - !TryInterpret(r, fewArgsExpr.Argument2, paramExprs, paramValues, parentArgs, useFec, out fewArgs[2]) || - !TryInterpret(r, fewArgsExpr.Argument3, paramExprs, paramValues, parentArgs, useFec, out fewArgs[3]) || - !TryInterpret(r, fewArgsExpr.Argument4, paramExprs, paramValues, parentArgs, useFec, out fewArgs[4])) - return false; - result = callExpr.Method.Invoke(instance, fewArgs); - return true; - } - } -#endif - var args = callExpr.Arguments.ToListOrSelf(); - var callArgCount = args.Count; - if (callArgCount == 0) - result = method.Invoke(instance, ArrayTools.Empty()); - else - { - var argObjects = new object[callArgCount]; - for (var i = 0; i < argObjects.Length; i++) - if (!TryInterpret(r, args[i], paramExprs, paramValues, parentArgs, useFec, out argObjects[i])) - return false; - result = method.Invoke(instance, argObjects); - } - - return true; - } - - private static object InterpretGetScopedViaFactoryDelegateNoDisposalIndex(IResolverContext resolver, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgExpr = (FourArgumentsMethodCallExpression)callExpr; - var resolverArg = fewArgExpr.Argument0; -#else - var args = callExpr.Arguments.ToListOrSelf(); - var resolverArg = args[0]; -#endif - if (!ReferenceEquals(resolverArg, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, resolverArg, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - var scope = (Scope)resolver.CurrentScope; - if (scope == null) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var throwIfNoScopeArg = fewArgExpr.Argument1; -#else - var throwIfNoScopeArg = args[1]; -#endif - return (bool)((ConstantExpression)throwIfNoScopeArg).Value ? Throw.For(Error.NoCurrentScope, resolver) : null; - } - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var factoryIdArg = fewArgExpr.Argument2; -#else - var factoryIdArg = args[2]; -#endif - var id = (int)((ConstantExpression)factoryIdArg).Value; - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var lambdaArg = fewArgExpr.Argument3; -#else - var lambdaArg = args[3]; -#endif - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambdaArg is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(resolver); - else - { - var body = ((LambdaExpression)lambdaArg).Body; - if (!TryInterpret(resolver, body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = body.CompileToFactoryDelegate(useFec, ((IContainer)resolver).Rules.UseInterpretation)(resolver); - } - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - scope.AddUnorderedDisposable(disp); - } - - return itemRef.Value; - } - - private static object InterpretGetScopedViaFactoryDelegate(IResolverContext resolver, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgExpr = (FiveArgumentsMethodCallExpression)callExpr; - var resolverArg = fewArgExpr.Argument0; -#else - var args = callExpr.Arguments.ToListOrSelf(); - var resolverArg = args[0]; -#endif - if (!ReferenceEquals(resolverArg, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(resolver, resolverArg, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - resolver = (IResolverContext)resolverObj; - } - - var scope = (Scope)resolver.CurrentScope; - if (scope == null) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var throwIfNoScopeArg = fewArgExpr.Argument1; -#else - var throwIfNoScopeArg = args[1]; -#endif - return (bool)((ConstantExpression)throwIfNoScopeArg).Value ? Throw.For(Error.NoCurrentScope, resolver) : null; - } - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var factoryIdArg = fewArgExpr.Argument2; -#else - var factoryIdArg = args[2]; -#endif - var id = (int)((ConstantExpression)factoryIdArg).Value; - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var lambdaArg = fewArgExpr.Argument3; -#else - var lambdaArg = args[3]; -#endif - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambdaArg is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(resolver); - else if (!TryInterpret(resolver, ((LambdaExpression)lambdaArg).Body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = ((LambdaExpression)lambdaArg).Body.CompileToFactoryDelegate(useFec, - ((IContainer)resolver).Rules.UseInterpretation)(resolver); - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var disposalOrderArg = fewArgExpr.Argument4; -#else - var disposalOrderArg = args[4]; -#endif - var disposalOrder = (int)((ConstantExpression)disposalOrderArg).Value; - if (disposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, disposalOrder); - } - } - - return itemRef.Value; - } - - private static object InterpretGetNameScopedViaFactoryDelegate(IResolverContext r, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { - var args = callExpr.Arguments.ToListOrSelf(); - - if (!ReferenceEquals(args[0], FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(r, args[0], paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - r = (IResolverContext)resolverObj; - } - - var scope = (Scope)r.GetNamedScope(((ConstantExpression)args[1]).Value, (bool)((ConstantExpression)args[2]).Value); - if (scope == null) - return null; // result is null in this case - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // check if scoped dependency is already in scope, then just return it - var id = (int)((ConstantExpression)args[3]).Value; - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { - var lambda = args[4]; - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambda is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(r); - else if (!TryInterpret(r, ((LambdaExpression)lambda).Body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = ((LambdaExpression)lambda).Body.CompileToFactoryDelegate(useFec, - ((IContainer)r).Rules.UseInterpretation)(r); - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - { - var disposalOrder = (int)((ConstantExpression)args[5]).Value; - if (disposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, disposalOrder); - } - } - - return itemRef.Value; - } - - private static object InterpretGetScopedOrSingletonViaFactoryDelegate(IResolverContext r, - MethodCallExpression callExpr, object paramExprs, object paramValues, ParentLambdaArgs parentArgs, bool useFec) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var fewArgExpr = (FourArgumentsMethodCallExpression)callExpr; - var resolverArg = fewArgExpr.Argument0; -#else - var args = callExpr.Arguments.ToListOrSelf(); - var resolverArg = args[0]; -#endif - if (!ReferenceEquals(resolverArg, FactoryDelegateCompiler.ResolverContextParamExpr)) - { - if (!TryInterpret(r, resolverArg, paramExprs, paramValues, parentArgs, useFec, out var resolverObj)) - return false; - r = (IResolverContext)resolverObj; - } - - var scope = (Scope)(r.CurrentScope ?? r.SingletonScope); - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var factoryIdArg = fewArgExpr.Argument1; -#else - var factoryIdArg = args[1]; -#endif - var id = (int)((ConstantExpression)factoryIdArg).Value; - - ref var map = ref scope._maps[id & Scope.MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - // add only, keep old item if it already exists - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, Scope.NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value == Scope.NoItem) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var lambda = fewArgExpr.Argument2; -#else - var lambda = args[2]; -#endif - object result = null; - lock (itemRef) - { - if (itemRef.Value != Scope.NoItem) - return itemRef.Value; - - if (lambda is ConstantExpression lambdaConstExpr) - result = ((FactoryDelegate)lambdaConstExpr.Value)(r); - else if (!TryInterpret(r, ((LambdaExpression)lambda).Body, paramExprs, paramValues, parentArgs, useFec, out result)) - result = ((LambdaExpression)lambda).Body.CompileToFactoryDelegate(useFec, - ((IContainer)r).Rules.UseInterpretation)(r); - - itemRef.Value = result; - } - - if (result is IDisposable disp && disp != scope) - { -#if SUPPORTS_FAST_EXPRESSION_COMPILER - var disposalOrderArg = fewArgExpr.Argument3; -#else - var disposalOrderArg = args[3]; -#endif - var disposalOrder = (int)((ConstantExpression)disposalOrderArg).Value; - if (disposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, disposalOrder); - } - } - - return itemRef.Value; - } - - [MethodImpl((MethodImplOptions)256)] - private static object ConstValue(Expression expr) => ((ConstantExpression)expr).Value; - } - - internal static class Converter - { - public static object ConvertWithOperator(object source, Type targetType, Expression expr) - { - var sourceType = source.GetType(); - var sourceConvertOp = sourceType.FindConvertOperator(sourceType, targetType); - if (sourceConvertOp != null) - return sourceConvertOp.Invoke(null, new[] { source }); - - var targetConvertOp = targetType.FindConvertOperator(sourceType, targetType); - if (targetConvertOp == null) - Throw.It(Error.NoConversionOperatorFoundWhenInterpretingTheConvertExpression, source, targetType, expr); - return targetConvertOp.Invoke(null, new[] { source }); - } - - public static object ConvertMany(object[] source, Type targetType) => - _convertManyMethod.MakeGenericMethod(targetType).Invoke(null, source.One()); - - public static R[] DoConvertMany(object[] items) - { - if (items == null && items.Length == 0) - return ArrayTools.Empty(); - - var results = new R[items.Length]; - for (var i = 0; i < items.Length; i++) - results[i] = (R)items[i]; - return results; - } - - private static readonly MethodInfo _convertManyMethod = - typeof(Converter).GetTypeInfo().GetDeclaredMethod(nameof(DoConvertMany)); - } - - /// Compiles expression to factory delegate. - public static class FactoryDelegateCompiler - { - /// Resolver context parameter expression in FactoryDelegate. - public static readonly ParameterExpression ResolverContextParamExpr = Parameter(typeof(IResolverContext), "r"); - - /// [Obsolete("Not used anymore")] - public static readonly Type[] FactoryDelegateParamTypes = { typeof(IResolverContext) }; - - /// Optimization: singleton array with the parameter expression of IResolverContext - public static readonly ParameterExpression[] FactoryDelegateParamExprs = { ResolverContextParamExpr }; - - /// Strips the unnecessary or adds the necessary cast to expression return result - public static Expression NormalizeExpression(this Expression expr) - { - if (expr.NodeType == System.Linq.Expressions.ExpressionType.Convert) - { - var operandExpr = ((UnaryExpression)expr).Operand; - if (operandExpr.Type == typeof(object)) - return operandExpr; - } - - if (expr.Type != typeof(void) && expr.Type.IsValueType()) - return Convert(expr, typeof(object)); - - return expr; - } - - /// Wraps service creation expression (body) into and returns result lambda expression. - public static Expression WrapInFactoryExpression(this Expression expression) => - Lambda(expression.NormalizeExpression(), FactoryDelegateParamExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , typeof(object) -#endif - ); - - /// First wraps the input service expression into lambda expression and - /// then compiles lambda expression to actual used for service resolution. - public static FactoryDelegate CompileToFactoryDelegate( - this Expression expression, bool useFastExpressionCompiler, bool preferInterpretation) - { - expression = expression.NormalizeExpression(); - if (expression is ConstantExpression constExpr) - return constExpr.Value.ToFactoryDelegate; - - if (!preferInterpretation && useFastExpressionCompiler) - { - var factoryDelegate = (FactoryDelegate)(FastExpressionCompiler.LightExpression.ExpressionCompiler.TryCompileBoundToFirstClosureParam( - typeof(FactoryDelegate), expression, FactoryDelegateParamExprs, - new[] { typeof(FastExpressionCompiler.LightExpression.ExpressionCompiler.ArrayClosure), typeof(IResolverContext) }, typeof(object))); - if (factoryDelegate != null) - return factoryDelegate; - } - - // fallback for platforms when FastExpressionCompiler is not supported, - // or just in case when some expression is not supported (did not found one yet) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - return Lambda(expression, FactoryDelegateParamExprs, typeof(object)).ToLambdaExpression() -#else - return Lambda(expression, FactoryDelegateParamExprs) -#endif - .Compile( -#if SUPPORTS_EXPRESSION_COMPILE_WITH_PREFER_INTERPRETATION_PARAM - preferInterpretation -#endif - ); - } - - /// Compiles lambda expression to actual `FactoryDelegate` wrapper. - public static object CompileToFactoryDelegate(this Expression expression, - Type factoryDelegateType, Type resultType, bool useFastExpressionCompiler, bool preferInterpretation) - { - if (!preferInterpretation && useFastExpressionCompiler) - { - var factoryDelegate = (FastExpressionCompiler.LightExpression.ExpressionCompiler.TryCompileBoundToFirstClosureParam( - factoryDelegateType, expression, FactoryDelegateParamExprs, - new[] { typeof(FastExpressionCompiler.LightExpression.ExpressionCompiler.ArrayClosure), typeof(IResolverContext) }, resultType)); - if (factoryDelegate != null) - return factoryDelegate; - } - - // fallback for platforms when FastExpressionCompiler is not supported, - // or just in case when some expression is not supported (did not found one yet) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - return Lambda(factoryDelegateType, expression, FactoryDelegateParamExprs, resultType).ToLambdaExpression() -#else - return Lambda(factoryDelegateType, expression, FactoryDelegateParamExprs) -#endif - .Compile( -#if SUPPORTS_EXPRESSION_COMPILE_WITH_PREFER_INTERPRETATION_PARAM - preferInterpretation -#endif - ); - } - - /// [Obsolete("Use the version with `preferInterpretation` parameter instead")] - public static FactoryDelegate CompileToFactoryDelegate(this Expression expression, - bool useFastExpressionCompiler = false) - { - expression = expression.NormalizeExpression(); - - // Optimization for constants - if (expression is ConstantExpression ce) - return ce.Value.ToFactoryDelegate; - - if (useFastExpressionCompiler) - { - var factoryDelegate = (FactoryDelegate)(FastExpressionCompiler.LightExpression.ExpressionCompiler.TryCompileBoundToFirstClosureParam( - typeof(FactoryDelegate), expression, FactoryDelegateParamExprs, - new[] { typeof(FastExpressionCompiler.LightExpression.ExpressionCompiler.ArrayClosure), typeof(IResolverContext) }, typeof(object))); - - if (factoryDelegate != null) - return factoryDelegate; - } - - // fallback for platforms when FastExpressionCompiler is not supported, - // or just in case when some expression is not supported (did not found one yet) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - return Lambda(expression, FactoryDelegateParamExprs, typeof(object)).ToLambdaExpression().Compile(); -#else - return Lambda(expression, FactoryDelegateParamExprs).Compile(); -#endif - } - - // todo: remove unused - /// Restores the expression from LightExpression, or returns itself if already an Expression. - public static System.Linq.Expressions.Expression ToExpression(this Expression expr) => -#if SUPPORTS_FAST_EXPRESSION_COMPILER - expr.ToExpression(); -#else - expr; -#endif - } - - /// Container extended features. - public static class ContainerTools - { - /// The default key for services registered into container created by - public const string FacadeKey = "@facade"; // todo: use invisible keys #555 - - /// Allows to register new specially keyed services which will facade the same default service, - /// registered earlier. May be used to "override" registrations when testing the container - public static IContainer CreateFacade(this IContainer container, string facadeKey = FacadeKey) => - container.With(rules => rules - .WithDefaultRegistrationServiceKey(facadeKey) - .WithFactorySelector(Rules.SelectKeyedOverDefaultFactory(facadeKey))); - - /// Shares all of container state except the cache and the new rules. - public static IContainer With(this IContainer container, - Func configure = null, IScopeContext scopeContext = null) => - container.With(configure?.Invoke(container.Rules) ?? container.Rules, scopeContext ?? container.ScopeContext, - RegistrySharing.CloneAndDropCache, container.SingletonScope); - - /// Prepares container for expression generation. - public static IContainer WithExpressionGeneration(this IContainer container, bool allowRuntimeState = false) => - container.With(rules => rules.WithExpressionGeneration(allowRuntimeState)); - - /// Returns new container with all expression, delegate, items cache removed/reset. - /// But it will preserve resolved services in Singleton/Current scope. - public static IContainer WithoutCache(this IContainer container) => - container.With(container.Rules, container.ScopeContext, - RegistrySharing.CloneAndDropCache, container.SingletonScope); - - /// Creates new container with state shared with original, except for the singletons and cache. - public static IContainer WithoutSingletonsAndCache(this IContainer container) => - container.With(container.Rules, container.ScopeContext, - RegistrySharing.CloneAndDropCache, singletonScope: null); - - /// Shares the setup with original container but copies the registrations, so the new registrations - /// won't be visible in original. Registrations include decorators and wrappers as well. - public static IContainer WithRegistrationsCopy(this IContainer container, bool preserveCache = false) => - container.With(container.Rules, container.ScopeContext, - preserveCache ? RegistrySharing.CloneButKeepCache : RegistrySharing.CloneAndDropCache, - container.SingletonScope); - - /// For given instance resolves and sets properties and fields. - /// It respects rules set per container, - /// or if rules are not set it uses . - public static TService InjectPropertiesAndFields(this IResolverContext r, TService instance) => - r.InjectPropertiesAndFields(instance, null); - - /// For given instance resolves and sets properties and fields. You may specify what - /// properties and fields. - public static TService InjectPropertiesAndFields(this IResolverContext r, TService instance, - params string[] propertyAndFieldNames) - { - r.InjectPropertiesAndFields(instance, propertyAndFieldNames); - return instance; - } - - /// Creates service using container for injecting parameters without registering anything in - /// if the TYPE is not registered yet. - /// Container to use for type creation and injecting its dependencies. - /// Type to instantiate. Wrappers (Func, Lazy, etc.) is also supported. - /// Setup for the concrete type, e.g. `TrackDisposableTransient` - /// (optional) Injection rules to select constructor/factory method, inject parameters, - /// properties and fields. - /// The default is - /// Object instantiated by constructor or object returned by factory method. - public static object New(this IContainer container, Type concreteType, Setup setup, Made made = null, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) - { - var containerClone = container.With(container.Rules, container.ScopeContext, - registrySharing, container.SingletonScope); - - var implType = containerClone.GetWrappedType(concreteType, null); - - var condition = setup == null && made == null ? null - : made == null ? (Func)(f => f.Setup == setup) - : setup == null ? (Func)(f => f.Made == made) - : (f => f.Made == made && f.Setup == setup); - - if (!containerClone.IsRegistered(implType, condition: condition)) - containerClone.Register(implType, made: made, setup: setup); - - // No need to Dispose facade because it shares singleton/open scopes with source container, and disposing source container does the job. - return containerClone.Resolve(concreteType, IfUnresolved.Throw); - } - - /// Creates service using container for injecting parameters without registering anything in . - /// Container to use for type creation and injecting its dependencies. - /// Type to instantiate. Wrappers (Func, Lazy, etc.) is also supported. - /// (optional) Injection rules to select constructor/factory method, inject parameters, - /// properties and fields. - /// The default is - /// Object instantiated by constructor or object returned by factory method. - public static object New(this IContainer container, Type concreteType, Made made = null, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) => - container.New(concreteType, setup: null, made, registrySharing); - - /// Creates service using container for injecting parameters without registering anything in . - /// Type to instantiate. - /// Container to use for type creation and injecting its dependencies. - /// (optional) Injection rules to select constructor/factory method, inject parameters, properties and fields. - /// The default is - /// Object instantiated by constructor or object returned by factory method. - public static T New(this IContainer container, Made made = null, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) => - (T)container.New(typeof(T), made, registrySharing); - - /// Creates service given strongly-typed creation expression. - /// Can be used to invoke arbitrary method returning some value with injecting its parameters from container. - /// Method or constructor result type. - /// Container to use for injecting dependencies. - /// Creation expression. - /// The default is - /// Created result. - public static T New(this IContainer container, Made.TypedMade made, - RegistrySharing registrySharing = RegistrySharing.CloneButKeepCache) => - (T)container.New(typeof(T), made, registrySharing); - - // todo: vNext: remove, replaced by Registrator.RegisterMapping - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Container New service type. - /// Existing registered service type. - /// (optional) (optional) - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IContainer container, Type serviceType, Type registeredServiceType, - object serviceKey = null, object registeredServiceKey = null) => - Registrator.RegisterMapping(container, - serviceType, registeredServiceType, serviceKey, registeredServiceKey); - - // todo: vNext: remove, replaced by Registrator.RegisterMapping - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Container - /// New service type. - /// Existing registered service type. - /// (optional) (optional) - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IContainer container, - object serviceKey = null, object registeredServiceKey = null) => - Registrator.RegisterMapping(container, - typeof(TService), typeof(TRegisteredService), serviceKey, registeredServiceKey); - - // todo: Remove in VNext? - /// Forwards to . - public static void RegisterPlaceholder(this IContainer container, Type serviceType, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - Registrator.RegisterPlaceholder(container, serviceType, ifAlreadyRegistered, serviceKey); - - // todo: vNext: Remove, replaced by Registrator.RegisterPlaceholder - /// Register a service without implementation which can be provided later in terms - /// of normal registration with IfAlreadyRegistered.Replace parameter. - /// When the implementation is still not provided when the placeholder service is accessed, - /// then the exception will be thrown. - /// This feature allows you to postpone decision on implementation until it is later known. - /// Internally the empty factory is registered with the setup asResolutionCall set to true. - /// That means, instead of placing service instance into graph expression we put here redirecting call to - /// container Resolve. - public static void RegisterPlaceholder(this IContainer container, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - container.RegisterPlaceholder(typeof(TService), ifAlreadyRegistered, serviceKey); - - /// Obsolete: please use WithAutoFallbackDynamicRegistration - [Obsolete("Please use WithAutoFallbackDynamicRegistration instead")] - public static IContainer WithAutoFallbackResolution(this IContainer container, - IEnumerable implTypes, - Func changeDefaultReuse = null, - Func condition = null) => - container.ThrowIfNull().With(rules => - rules.WithUnknownServiceResolvers( - Rules.AutoRegisterUnknownServiceRule(implTypes, changeDefaultReuse, condition))); - - /// Obsolete: please use WithAutoFallbackDynamicRegistration - [Obsolete("Please use WithAutoFallbackDynamicRegistration instead")] - public static IContainer WithAutoFallbackResolution(this IContainer container, - IEnumerable implTypeAssemblies, - Func changeDefaultReuse = null, - Func condition = null) => - container.WithAutoFallbackResolution(implTypeAssemblies.ThrowIfNull() - .SelectMany(assembly => assembly.GetLoadedTypes()) - .Where(Registrator.IsImplementationType).ToArray(), - changeDefaultReuse, condition); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - Func> getImplTypes, Func factory = null) => - container.ThrowIfNull() - .With(rules => rules.WithDynamicRegistrationsAsFallback( - Rules.AutoFallbackDynamicRegistrations(getImplTypes, factory))); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, params Type[] implTypes) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypes); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - IReuse reuse, params Type[] implTypes) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypes, implType => new ReflectionFactory(implType, reuse)); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - IReuse reuse, Setup setup, params Type[] implTypes) => - container.WithAutoFallbackDynamicRegistrations( - (ignoredServiceType, ignoredServiceKey) => implTypes, - implType => new ReflectionFactory(implType, reuse, setup: setup)); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - Func> getImplTypeAssemblies, - Func factory = null) => - container.ThrowIfNull().With(rules => rules.WithDynamicRegistrations( - Rules.AutoFallbackDynamicRegistrations( - (serviceType, serviceKey) => - { - var assemblies = getImplTypeAssemblies(serviceType, serviceKey); - if (assemblies == null) - return Empty(); - return assemblies - .SelectMany(ReflectionTools.GetLoadedTypes) - .Where(Registrator.IsImplementationType) - .ToArray(); - }, - factory))); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - params Assembly[] implTypeAssemblies) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypeAssemblies); - - /// Provides automatic fallback resolution mechanism for not normally registered - /// services. Underneath uses . - public static IContainer WithAutoFallbackDynamicRegistrations(this IContainer container, - IEnumerable implTypeAssemblies) => - container.WithAutoFallbackDynamicRegistrations((_, __) => implTypeAssemblies); - - /// Creates new container with provided parameters and properties - /// to pass the custom dependency values for injection. The old parameters and properties are overridden, - /// but not replaced. - /// Container to work with. - /// (optional) Parameters specification, can be used to proved custom values. - /// (optional) Properties and fields specification, can be used to proved custom values. - /// New container with adjusted rules. - /// (_ => "Nya!")); - /// var a = c.Resolve(); // where A accepts string parameter in constructor - /// Assert.AreEqual("Nya!", a.Message) - /// ]]> - public static IContainer WithDependencies(this IContainer container, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - container.With(rules => rules.With(Made.Of( - parameters: rules.Parameters.OverrideWith(parameters), - propertiesAndFields: rules.PropertiesAndFields.OverrideWith(propertiesAndFields)), - overrideRegistrationMade: true)); - - /// Result of GenerateResolutionExpressions methods - public class GeneratedExpressions - { - /// Resolutions roots - public readonly List>> - Roots = new List>>(); - - /// Dependency of Resolve calls - public readonly List> - ResolveDependencies = new List>(); - - /// Errors - public readonly List> - Errors = new List>(); - } - - /// Generates expressions for specified roots and their "Resolve-call" dependencies. - /// Wraps exceptions into errors. The method does not create any actual services. - /// You may use Factory . - public static GeneratedExpressions GenerateResolutionExpressions(this IContainer container, - Func, IEnumerable> getRoots = null, bool allowRuntimeState = false) - { - var generatingContainer = container.WithExpressionGeneration(allowRuntimeState); - var regs = generatingContainer.GetServiceRegistrations(); - var roots = getRoots != null ? getRoots(regs) : regs.Select(r => r.ToServiceInfo()); - - var result = new GeneratedExpressions(); - foreach (var root in roots) - { - try - { - var request = Request.Create(generatingContainer, root); - var expr = generatingContainer.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - continue; - - result.Roots.Add(root.Pair(expr.WrapInFactoryExpression() -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToLambdaExpression() -#endif - )); - } - catch (ContainerException ex) - { - result.Errors.Add(root.Pair(ex)); - } - } - - var depExprs = generatingContainer.Rules.DependencyResolutionCallExprs.Value; - result.ResolveDependencies.AddRange(depExprs.Enumerate().Select(r => r.Key.Pair(r.Value))); - return result; - } - - /// Generates expressions for provided root services - public static GeneratedExpressions GenerateResolutionExpressions( - this IContainer container, Func condition) => - container.GenerateResolutionExpressions(regs => regs.Where(condition.ThrowIfNull()).Select(r => r.ToServiceInfo())); - - /// Generates expressions for provided root services - public static GeneratedExpressions GenerateResolutionExpressions( - this IContainer container, params ServiceInfo[] roots) => - container.GenerateResolutionExpressions(roots.ToFunc, IEnumerable>); - - /// Excluding open-generic registrations, cause you need to provide type arguments to actually create these types. - public static bool DefaultValidateCondition(ServiceRegistrationInfo reg) => !reg.ServiceType.IsOpenGeneric(); - - // todo: Should we have a version which is throws by default? - // todo: Should we break it by making the condition a mandatory? - because it the pass to avoid problems of validating the unnecessary dependencies - /// Helps to find potential problems in service registration setup. - /// Method tries to resolve the specified registrations, collects exceptions, and - /// returns them to user. Does not create any actual service objects. - /// You must specify to define your resolution roots, - /// otherwise container will try to resolve all registrations, - /// which usually is not realistic case to validate. - public static KeyValuePair[] Validate(this IContainer container, - Func condition = null) - { - var noOpenGenericsWithCondition = condition == null - ? (Func)DefaultValidateCondition - : (r => condition(r) && DefaultValidateCondition(r)); - - var roots = container.GetServiceRegistrations().Where(noOpenGenericsWithCondition).Select(r => r.ToServiceInfo()).ToArray(); - if (roots.Length == 0) - Throw.It(Error.FoundNoRootsToValidate, container); - - return container.Validate(roots); - } - - /// Helps to find potential problems when resolving the . - /// Method will collect the exceptions when resolving or injecting the specific root. - /// Does not create any actual service objects. - /// You must specify to define your resolution roots, - /// otherwise container will try to resolve all registrations, - /// which usually is not realistic case to validate. - public static KeyValuePair[] Validate( - this IContainer container, params ServiceInfo[] roots) - { - var validatingContainer = container.With(rules => rules.ForValidate()); - - List> errors = null; - for (var i = 0; i < roots.Length; i++) - { - var root = roots[i]; - try - { - var request = Request.Create(validatingContainer, root); - var expr = validatingContainer.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - continue; - } - catch (ContainerException ex) - { - if (errors == null) - errors = new List>(); - errors.Add(root.Pair(ex)); - } - } - - return errors?.ToArray() ?? ArrayTools.Empty>(); - } - - /// Re-constructs the whole request chain as request creation expression. - public static Expression GetRequestExpression(this IContainer container, Request request, - RequestFlags requestParentFlags = default(RequestFlags)) - { - if (request.IsEmpty) - return (requestParentFlags & RequestFlags.OpensResolutionScope) != 0 - ? Request.EmptyOpensResolutionScopeRequestExpr - : Request.EmptyRequestExpr; - - var flags = request.Flags | requestParentFlags; - var r = requestParentFlags == default(RequestFlags) ? request : request.WithFlags(flags); - - // When not for generation, using run-time request object to Minimize generated object graph. - if (!container.Rules.UsedForExpressionGeneration) - return Constant(r.IsolateRequestChain()); - - // recursively ask for parent expression until it is empty - var parentExpr = container.GetRequestExpression(request.DirectParent); - - var serviceType = r.ServiceType; - var ifUnresolved = r.IfUnresolved; - var requiredServiceType = r.RequiredServiceType; - var serviceKey = r.ServiceKey; - - var metadataKey = r.MetadataKey; - var metadata = r.Metadata; - - var factoryID = r.FactoryID; - var factoryType = r.FactoryType; - var implementationType = r.ImplementationType; - var decoratedFactoryID = r.DecoratedFactoryID; - - var serviceTypeExpr = Constant(serviceType); - var factoryIdExpr = Constant(factoryID); - var implTypeExpr = Constant(implementationType); - var reuseExpr = r.Reuse == null ? Constant(null) - : r.Reuse.ToExpression(it => container.GetConstantExpression(it)); - - if (ifUnresolved == IfUnresolved.Throw && - requiredServiceType == null && serviceKey == null && metadataKey == null && metadata == null && - factoryType == FactoryType.Service && flags == default(RequestFlags) && decoratedFactoryID == 0) - return Call(parentExpr, Request.PushMethodWith4Args.Value, - serviceTypeExpr, factoryIdExpr, implTypeExpr, reuseExpr); - - var requiredServiceTypeExpr = Constant(requiredServiceType); - var serviceKeyExpr = container.GetConstantExpression(serviceKey, typeof(object)); - var factoryTypeExpr = Constant(factoryType); - var flagsExpr = Constant(flags); - - if (ifUnresolved == IfUnresolved.Throw && - metadataKey == null && metadata == null && decoratedFactoryID == 0) - return Call(parentExpr, Request.PushMethodWith8Args.Value, - serviceTypeExpr, requiredServiceTypeExpr, serviceKeyExpr, - factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr); - - var ifUnresolvedExpr = Constant(ifUnresolved); - var decoratedFactoryIDExpr = Constant(decoratedFactoryID); - - if (metadataKey == null && metadata == null) - return Call(parentExpr, Request.PushMethodWith10Args.Value, - serviceTypeExpr, requiredServiceTypeExpr, serviceKeyExpr, ifUnresolvedExpr, - factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr, decoratedFactoryIDExpr); - - var metadataKeyExpr = Constant(metadataKey); - var metadataExpr = container.GetConstantExpression(metadata, typeof(object)); - - return Call(parentExpr, Request.PushMethodWith12Args.Value, - serviceTypeExpr, requiredServiceTypeExpr, serviceKeyExpr, metadataKeyExpr, metadataExpr, ifUnresolvedExpr, - factoryIdExpr, factoryTypeExpr, implTypeExpr, reuseExpr, flagsExpr, decoratedFactoryIDExpr); - } - - /// Clears delegate and expression cache for specified . - /// But does not clear instances of already resolved/created singletons and scoped services! - public static bool ClearCache(this IContainer container, FactoryType? factoryType = null, object serviceKey = null) => - container.ClearCache(typeof(T), factoryType, serviceKey); - - /// Clears delegate and expression cache for specified service. - /// But does not clear instances of already resolved/created singletons and scoped services! - public static bool ClearCache(this IContainer container, Type serviceType, - FactoryType? factoryType = null, object serviceKey = null) => - container.ClearCache(serviceType, factoryType, serviceKey); - } - - /// Interface used to convert reuse instance to expression. - public interface IConvertibleToExpression - { - /// Returns expression representation without closure. - /// Use to converting the sub-items, constants to container. - Expression ToExpression(Func fallbackConverter); - } - - /// Used to represent multiple default service keys. - /// Exposes to determine order of service added. - public sealed class DefaultKey : IConvertibleToExpression - { - /// Default value. - public static readonly DefaultKey Value = new DefaultKey(0); - - /// Allows to determine service registration order. - public readonly int RegistrationOrder; - - /// Returns the default key with specified registration order. - public static DefaultKey Of(int registrationOrder) => - registrationOrder == 0 ? Value : new DefaultKey(registrationOrder); - - private static readonly MethodInfo _ofMethod = - typeof(DefaultKey).GetTypeInfo().GetDeclaredMethod(nameof(Of)); - - /// Converts to expression - public Expression ToExpression(Func fallbackConverter) => - Call(_ofMethod, Constant(RegistrationOrder)); - - /// Returns next default key with increased . - public DefaultKey Next() => Of(RegistrationOrder + 1); - - /// Compares keys based on registration order. The null (represents default) key is considered equal. - public override bool Equals(object key) => - key == null || (key as DefaultKey)?.RegistrationOrder == RegistrationOrder; - - /// Returns registration order as hash. - public override int GetHashCode() => RegistrationOrder; - - /// Prints registration order to string. - public override string ToString() => GetType().Name + "(" + RegistrationOrder + ")"; - - private DefaultKey(int registrationOrder) - { - RegistrationOrder = registrationOrder; - } - } - - /// Represents default key for dynamic registrations - public sealed class DefaultDynamicKey : IConvertibleToExpression - { - /// Default value. - public static readonly DefaultDynamicKey Value = new DefaultDynamicKey(0); - - /// Associated ID. - public readonly int RegistrationOrder; - - /// Returns dynamic key with specified ID. - public static DefaultDynamicKey Of(int registrationOrder) => - registrationOrder == 0 ? Value : new DefaultDynamicKey(registrationOrder); - - private static readonly MethodInfo _ofMethod = - typeof(DefaultDynamicKey).GetTypeInfo().GetDeclaredMethod(nameof(Of)); - - /// Converts to expression - public Expression ToExpression(Func fallbackConverter) => - Call(_ofMethod, Constant(RegistrationOrder)); - - /// Returns next dynamic key with increased . - public DefaultDynamicKey Next() => Of(RegistrationOrder + 1); - - /// Compares key's IDs. The null (default) key is considered equal! - public override bool Equals(object key) => - key == null || (key as DefaultDynamicKey)?.RegistrationOrder == RegistrationOrder; - - /// Returns key index as hash. - public override int GetHashCode() => RegistrationOrder; - - /// Prints registration order to string. - public override string ToString() => GetType().Name + "(" + RegistrationOrder + ")"; - - private DefaultDynamicKey(int registrationOrder) - { - RegistrationOrder = registrationOrder; - } - } - - /// Extends IResolver to provide an access to scope hierarchy. - public interface IResolverContext : IResolver, IDisposable - { - /// True if container is disposed. - bool IsDisposed { get; } - - /// Parent context of the scoped context. - IResolverContext Parent { get; } - - /// The root context of the scoped context. - IResolverContext Root { get; } - - /// Singleton scope, always associated with root scope. - IScope SingletonScope { get; } - - /// Optional ambient scope context. - IScopeContext ScopeContext { get; } - - /// Current opened scope. May return the current scope from if context is not null. - IScope CurrentScope { get; } - - /// Creates the resolver context with specified current Container-OWN scope - IResolverContext WithCurrentScope(IScope scope); - - /// Put instance into the current scope or singletons. - void UseInstance(Type serviceType, object instance, IfAlreadyRegistered IfAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey); - - /// Puts instance created via the passed factory on demand into the current or singleton scope - void Use(Type serviceType, FactoryDelegate factory); - - /// For given instance resolves and sets properties and fields. - void InjectPropertiesAndFields(object instance, string[] propertyAndFieldNames); - } - - /// Provides a usable abstractions for - public static class ResolverContext - { - /// Just a sugar that allow to get root or self container. - public static IResolverContext RootOrSelf(this IResolverContext r) => r.Root ?? r; - - internal static readonly PropertyInfo ParentProperty = - typeof(IResolverContext).Property(nameof(IResolverContext.Parent)); - - internal static readonly MethodInfo OpenScopeMethod = - typeof(ResolverContext).GetTypeInfo().GetDeclaredMethod(nameof(OpenScope)); - - /// Returns root or self resolver based on request. - public static Expression GetRootOrSelfExpr(Request request) => - request.DirectParent.IsSingletonOrDependencyOfSingleton && !request.OpensResolutionScope && !request.IsDirectlyWrappedInFunc() - ? RootOrSelfExpr - : FactoryDelegateCompiler.ResolverContextParamExpr; - - /// Resolver context parameter expression in FactoryDelegate. - public static readonly Expression ParentExpr = - Property(FactoryDelegateCompiler.ResolverContextParamExpr, ParentProperty); - - /// Resolver parameter expression in FactoryDelegate. - public static readonly Expression RootOrSelfExpr = - Call(typeof(ResolverContext).GetTypeInfo().GetDeclaredMethod(nameof(RootOrSelf)), - FactoryDelegateCompiler.ResolverContextParamExpr); - - /// Resolver parameter expression in FactoryDelegate. - public static readonly Expression SingletonScopeExpr = - Property(FactoryDelegateCompiler.ResolverContextParamExpr, - typeof(IResolverContext).Property(nameof(IResolverContext.SingletonScope))); - - /// Access to scopes in FactoryDelegate. - public static readonly Expression CurrentScopeExpr = - Property(FactoryDelegateCompiler.ResolverContextParamExpr, - typeof(IResolverContext).Property(nameof(IResolverContext.CurrentScope))); - - /// Indicates that context is scoped - that's is only possible if container is not the Root one and has a Parent context - public static bool IsScoped(this IResolverContext r) => r.Parent != null; - - /// Provides access to the current scope - may return `null` if ambient scope context has it scope changed in-between - public static IScope GetCurrentScope(this IResolverContext r, bool throwIfNotFound) => - r.CurrentScope ?? (throwIfNotFound ? Throw.For(Error.NoCurrentScope, r) : null); - - /// Gets current scope matching the - public static IScope GetNamedScope(this IResolverContext r, object name, bool throwIfNotFound) - { - var currentScope = r.CurrentScope; - if (currentScope == null) - return throwIfNotFound ? Throw.For(Error.NoCurrentScope, r) : null; - - if (name == null) - return currentScope; - - if (name is IScopeName scopeName) - { - for (var s = currentScope; s != null; s = s.Parent) - if (scopeName.Match(s.Name)) - return s; - } - else - { - for (var s = currentScope; s != null; s = s.Parent) - if (ReferenceEquals(name, s.Name) || name.Equals(s.Name)) - return s; - } - - return !throwIfNotFound ? null : Throw.For(Error.NoMatchedScopeFound, name, currentScope); - } - - /// Opens scope with optional name and optional tracking of new scope in a parent scope. - /// Parent context to use. - /// (optional) - /// (optional) Instructs to additionally store the opened scope in parent, - /// so it will be disposed when parent is disposed. If no parent scope is available the scope will be tracked by Singleton scope. - /// Used to dispose a resolution scope. - /// Scoped resolver context. - /// (); - /// handler.Handle(data); - /// } - /// ]]> - public static IResolverContext OpenScope(this IResolverContext r, object name = null, bool trackInParent = false) - { - if (r.ScopeContext == null) - { - // todo: may use `r.OwnCurrentScope` when its moved to `IResolverContext` from `IContainer` - var parentScope = r.CurrentScope; - var newOwnScope = new Scope(parentScope, name); - if (trackInParent) - (parentScope ?? r.SingletonScope).TrackDisposable(newOwnScope); - return r.WithCurrentScope(newOwnScope); - } - - var newContextScope = name == null - ? r.ScopeContext.SetCurrent(parent => new Scope(parent)) - : r.ScopeContext.SetCurrent(parent => new Scope(parent, name)); - - if (trackInParent) - (newContextScope.Parent ?? r.SingletonScope).TrackDisposable(newContextScope); - return r.WithCurrentScope(null); - } - - internal static bool TryGetUsedInstance(this IResolverContext r, Type serviceType, out object instance) - { - instance = null; - return r.CurrentScope? .TryGetUsedInstance(r, serviceType, out instance) == true - || r.SingletonScope.TryGetUsedInstance(r, serviceType, out instance); - } - - /// A bit if sugar to track disposable in singleton or current scope - public static T TrackDisposable(this IResolverContext r, T instance) where T : IDisposable => - (T)(r.SingletonScope ?? r.CurrentScope).TrackDisposable(instance); - } - - /// The result delegate generated by DryIoc for service creation. - public delegate object FactoryDelegate(IResolverContext r); - - /// The stronly typed delegate for service creation registered as a Wrapper. - public delegate TService FactoryDelegate(IResolverContext r); - - /// Adds to Container support for: - /// - /// Open-generic services - /// Service generics wrappers and arrays using extension point. - /// Supported wrappers include: Func of , Lazy, Many, IEnumerable, arrays, Meta, KeyValuePair, DebugExpression. - /// All wrapper factories are added into collection of . - /// unregistered resolution rule. - /// - public static class WrappersSupport - { - /// Supported Func types. - public static readonly Type[] FuncTypes = - { - typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>), - typeof(Func<,,,,,>), typeof(Func<,,,,,,>), typeof(Func<,,,,,,,>) - }; - - /// Supported Action types. Yeah, action I can resolve or inject void returning method as action. - public static readonly Type[] ActionTypes = - { - typeof(Action), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>), - typeof(Action<,,,,>), typeof(Action<,,,,,>), typeof(Action<,,,,,,>) - }; - - /// Supported open-generic collection types - all the interfaces implemented by array. - public static readonly Type[] SupportedCollectionTypes = - typeof(object[]).GetImplementedInterfaces().Match(t => t.IsGeneric(), t => t.GetGenericTypeDefinition()); - - /// Returns true if type is supported , and false otherwise. - public static bool IsFunc(this Type type) - { - var genericDefinition = type.GetGenericDefinitionOrNull(); - return genericDefinition != null && FuncTypes.IndexOfReference(genericDefinition) != -1; - } - - internal static int CollectionWrapperID { get; private set; } - - /// Registered wrappers by their concrete or generic definition service type. - public static readonly ImMap> Wrappers = BuildSupportedWrappers(); - - private static ImMap> BuildSupportedWrappers() - { - var wrappers = ImMap>.Empty; - - var arrayExpr = new ExpressionFactory(GetArrayExpression, setup: Setup.Wrapper); - CollectionWrapperID = arrayExpr.FactoryID; - - var arrayInterfaces = SupportedCollectionTypes; - for (var i = 0; i < arrayInterfaces.Length; i++) - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(arrayInterfaces[i]), arrayInterfaces[i], arrayExpr); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(LazyEnumerable<>)), typeof(LazyEnumerable<>), - new ExpressionFactory(GetLazyEnumerableExpressionOrDefault, setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Lazy<>)), typeof(Lazy<>), - new ExpressionFactory(r => GetLazyExpressionOrDefault(r), setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(KeyValuePair<,>)), typeof(KeyValuePair<,>), - new ExpressionFactory(GetKeyValuePairExpressionOrDefault, setup: Setup.WrapperWith(1))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Meta<,>)), typeof(Meta<,>), - new ExpressionFactory(GetMetaExpressionOrDefault, setup: Setup.WrapperWith(0))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Tuple<,>)), typeof(Tuple<,>), - new ExpressionFactory(GetMetaExpressionOrDefault, setup: Setup.WrapperWith(0))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(System.Linq.Expressions.LambdaExpression)), typeof(System.Linq.Expressions.LambdaExpression), - new ExpressionFactory(GetLambdaExpressionExpressionOrDefault, setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(FactoryDelegate)), typeof(FactoryDelegate), - new ExpressionFactory(GetFactoryDelegateExpressionOrDefault, setup: Setup.Wrapper)); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(FactoryDelegate<>)), typeof(FactoryDelegate<>), - new ExpressionFactory(GetFactoryDelegateExpressionOrDefault, setup: Setup.WrapperWith(0))); - - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(Func<>)), typeof(Func<>), - new ExpressionFactory(GetFuncOrActionExpressionOrDefault, setup: Setup.Wrapper)); - - for (var i = 0; i < FuncTypes.Length; i++) - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(FuncTypes[i]), FuncTypes[i], - new ExpressionFactory(GetFuncOrActionExpressionOrDefault, setup: Setup.WrapperWith(i))); - - for (var i = 0; i < ActionTypes.Length; i++) - wrappers = wrappers.AddOrUpdate(RuntimeHelpers.GetHashCode(ActionTypes[i]), ActionTypes[i], - new ExpressionFactory(GetFuncOrActionExpressionOrDefault, - setup: Setup.WrapperWith(unwrap: typeof(void).ToFunc))); - - wrappers = wrappers.AddContainerInterfaces(); - return wrappers; - } - - private static ImMap> AddContainerInterfaces(this ImMap> wrappers) - { - var resolverContextExpr = new ExpressionFactory( - ResolverContext.GetRootOrSelfExpr, - Reuse.Transient, Setup.WrapperWith(preventDisposal: true)); - - var containerExpr = new ExpressionFactory( - r => Convert(ResolverContext.GetRootOrSelfExpr(r), r.ServiceType), - Reuse.Transient, Setup.WrapperWith(preventDisposal: true)); - - wrappers = wrappers - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IResolverContext)), typeof(IResolverContext), resolverContextExpr) - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IResolver)), typeof(IResolver), resolverContextExpr) - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IContainer)), typeof(IContainer), containerExpr) - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IRegistrator)), typeof(IRegistrator), containerExpr) -#if SUPPORTS_ISERVICE_PROVIDER - .AddOrUpdate(RuntimeHelpers.GetHashCode(typeof(IServiceProvider)), typeof(IServiceProvider), resolverContextExpr) -#endif - ; - - return wrappers; - } - - internal static readonly MethodInfo ToArrayMethod = - typeof(ArrayTools).GetTypeInfo().GetDeclaredMethod(nameof(ArrayTools.ToArrayOrSelf)); - - private static Expression GetArrayExpression(Request request) - { - var collectionType = request.GetActualServiceType(); - var container = request.Container; - var rules = container.Rules; - - var itemType = collectionType.GetArrayElementTypeOrNull() ?? collectionType.GetGenericParamsAndArgs()[0]; - - if (rules.ResolveIEnumerableAsLazyEnumerable) - { - var lazyEnumerableExpr = GetLazyEnumerableExpressionOrDefault(request); - return collectionType.GetGenericDefinitionOrNull() != typeof(IEnumerable<>) - ? Call(ToArrayMethod.MakeGenericMethod(itemType), lazyEnumerableExpr) - : lazyEnumerableExpr; - } - - var requiredItemType = container.GetWrappedType(itemType, request.RequiredServiceType); - - var items = container.GetAllServiceFactories(requiredItemType) - .Map(x => new ServiceRegistrationInfo(x.Value, requiredItemType, x.Key)) - .ToArrayOrSelf(); - - if (requiredItemType.IsClosedGeneric()) - { - var requiredItemOpenGenericType = requiredItemType.GetGenericDefinitionOrNull(); - var openGenericItems = container.GetAllServiceFactories(requiredItemOpenGenericType) - .Map(f => new ServiceRegistrationInfo(f.Value, requiredItemType, - new OpenGenericTypeKey(requiredItemType.GetGenericDefinitionOrNull(), f.Key))) - .ToArrayOrSelf(); - items = items.Append(openGenericItems); - } - - // Append registered generic types with compatible variance, - // e.g. for IHandler - IHandler is compatible with IHandler if B : A. - if (requiredItemType.IsGeneric() && - rules.VariantGenericTypesInResolvedCollection) - { - var variantGenericItems = container.GetServiceRegistrations() - .Match(x => - x.ServiceType.IsGeneric() && - x.ServiceType.GetGenericTypeDefinition() == requiredItemType.GetGenericTypeDefinition() && - x.ServiceType != requiredItemType && x.ServiceType.IsAssignableTo(requiredItemType)) - .ToArrayOrSelf(); - items = items.Append(variantGenericItems); - } - - // Composite pattern support: filter out composite parent service skip wrappers and decorators - var parent = request.Parent; - if (parent.FactoryType != FactoryType.Service) - parent = parent.FirstOrDefault(p => p.FactoryType == FactoryType.Service) ?? Request.Empty; - - // check fast for the parent of the same type - if (!parent.IsEmpty && parent.GetActualServiceType() == requiredItemType) - { - items = items.Match(parent.FactoryID, (pID, x) => x.Factory.FactoryID != pID); - if (requiredItemType.IsGeneric()) - items = items.Match(parent.FactoryID, - (pID, x) => x.Factory.FactoryGenerator?.GeneratedFactories.Enumerate().FindFirst(f => f.Value.FactoryID == pID) == null); - } - - // Return collection of single matched item if key is specified. - var serviceKey = request.ServiceKey; - if (serviceKey != null) - items = items.Match(serviceKey, (key, x) => key.Equals(x.OptionalServiceKey)); - - var metadataKey = request.MetadataKey; - var metadata = request.Metadata; - if (metadataKey != null || metadata != null) - items = items.Match(metadataKey.Pair(metadata), (m, x) => x.Factory.Setup.MatchesMetadata(m.Key, m.Value)); - - var itemExprs = Empty(); - if (!items.IsNullOrEmpty()) - { - Array.Sort(items); // to resolve the items in order of registration - - for (var i = 0; i < items.Length; i++) - { - var item = items[i]; - var itemRequest = request.Push(itemType, item.OptionalServiceKey, - IfUnresolved.ReturnDefaultIfNotRegistered, requiredServiceType: item.ServiceType); - - var itemFactory = container.ResolveFactory(itemRequest); - var itemExpr = itemFactory?.GetExpressionOrDefault(itemRequest); - if (itemExpr != null) - itemExprs = itemExprs.AppendOrUpdate(itemExpr); - } - } - - return NewArrayInit(itemType, itemExprs); - } - - private static Expression GetLazyEnumerableExpressionOrDefault(Request request) - { - var container = request.Container; - var collectionType = request.ServiceType; - var itemType = collectionType.GetArrayElementTypeOrNull() ?? collectionType.GetGenericParamsAndArgs()[0]; - var requiredItemType = container.GetWrappedType(itemType, request.RequiredServiceType); - - var resolverExpr = ResolverContext.GetRootOrSelfExpr(request); - var preResolveParentExpr = container.GetRequestExpression(request); - - var resolveManyExpr = Call(resolverExpr, Resolver.ResolveManyMethod, - Constant(itemType), - container.GetConstantExpression(request.ServiceKey), - Constant(requiredItemType), - preResolveParentExpr, - request.GetInputArgsExpr()); - - return New(typeof(LazyEnumerable<>).MakeGenericType(itemType) - .GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 1), - // cast to object is not required cause Resolve already returns IEnumerable - itemType == typeof(object) ? (Expression)resolveManyExpr : Call(_enumerableCastMethod.MakeGenericMethod(itemType), resolveManyExpr)); - } - - private static readonly MethodInfo _enumerableCastMethod = - typeof(Enumerable).GetTypeInfo().GetDeclaredMethod(nameof(Enumerable.Cast)); - - /// Gets the expression for wrapper. - /// The resolution request. - /// if set to true then check for service registration before creating resolution expression. - /// Expression: new Lazy(() => r.Resolve{TService}(key, ifUnresolved, requiredType))]]> - public static Expression GetLazyExpressionOrDefault(Request request, bool nullWrapperForUnresolvedService = false) - { - var lazyType = request.GetActualServiceType(); - var serviceType = lazyType.GetGenericParamsAndArgs()[0]; - var serviceRequest = request.Push(serviceType); - - var container = request.Container; - if (!container.Rules.FuncAndLazyWithoutRegistration) - { - var serviceFactory = container.ResolveFactory(serviceRequest); - if (serviceFactory == null) - return request.IfUnresolved == IfUnresolved.Throw ? null : Constant(null, lazyType); - serviceRequest = serviceRequest.WithResolvedFactory(serviceFactory, skipRecursiveDependencyCheck: true); - } - - // creates: r => new Lazy(() => r.Resolve(key)) - // or for singleton : r => new Lazy(() => r.Root.Resolve(key)) - var serviceExpr = Resolver.CreateResolutionExpression(serviceRequest); - - // The conversion is required in .NET 3.5 to handle lack of covariance for Func - // So that Func may be used for Func - if (serviceExpr.Type != serviceType) - serviceExpr = Convert(serviceExpr, serviceType); - - var lazyValueFactoryType = typeof(Func<>).MakeGenericType(serviceType); - var wrapperCtor = lazyType.Constructor(lazyValueFactoryType); - - return New(wrapperCtor, Lambda(lazyValueFactoryType, serviceExpr, Empty() -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , serviceType -#endif - )); - } - - private static Expression GetFuncOrActionExpressionOrDefault(Request request) - { - var wrapperType = request.GetActualServiceType(); - var isAction = wrapperType == typeof(Action); - if (!isAction) - { - var openGenericWrapperType = wrapperType.GetGenericDefinitionOrNull().ThrowIfNull(); - if (FuncTypes.IndexOfReference(openGenericWrapperType) == -1) - Throw.If(!(isAction = ActionTypes.IndexOfReference(openGenericWrapperType) != -1)); - } - - var argTypes = wrapperType.GetGenericParamsAndArgs(); - var argCount = isAction ? argTypes.Length : argTypes.Length - 1; - var serviceType = isAction ? typeof(void) : argTypes[argCount]; - - var argExprs = Empty(); // may be empty, that's OK - if (argCount != 0) - { - argExprs = new ParameterExpression[argCount]; - for (var i = 0; i < argCount; ++i) - // assign valid unique argument names for code generation - argExprs[i] = Parameter(argTypes[i], argTypes[i].Name + "@" + i); // todo: optimize string allocations - request = request.WithInputArgs(argExprs); - } - - var serviceRequest = request.Push(serviceType, flags: RequestFlags.IsWrappedInFunc | RequestFlags.IsDirectlyWrappedInFunc); - var container = request.Container; - var serviceExpr = container.Rules.FuncAndLazyWithoutRegistration && !isAction - ? Resolver.CreateResolutionExpression(serviceRequest) - : container.ResolveFactory(serviceRequest)?.GetExpressionOrDefault(serviceRequest); - - if (serviceExpr == null) - return null; - - // The conversion to handle lack of covariance for Func in .NET 3.5 - // So that Func may be used for Func - if (!isAction && serviceExpr.Type != serviceType) - serviceExpr = Convert(serviceExpr, serviceType); - - return Lambda(wrapperType, serviceExpr, argExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , isAction ? typeof(void) : serviceType -#endif - ); - } - - private static Expression GetLambdaExpressionExpressionOrDefault(Request request) - { - var serviceType = request.RequiredServiceType - .ThrowIfNull(Error.ResolutionNeedsRequiredServiceType, request); - request = request.Push(serviceType); - var expr = request.Container.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - return null; - return Constant(expr.WrapInFactoryExpression() -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToLambdaExpression() -#endif - , typeof(LambdaExpression)); - } - - private static Expression GetFactoryDelegateExpressionOrDefault(Request request) - { - Type serviceType; - var wrapperType = request.ServiceType; - if (wrapperType == typeof(FactoryDelegate)) - serviceType = request.RequiredServiceType.ThrowIfNull(Error.ResolutionNeedsRequiredServiceType, request); - else - serviceType = request.RequiredServiceType ?? wrapperType.GetGenericParamsAndArgs()[0]; - - request = request.Push(serviceType); - var container = request.Container; - var expr = container.ResolveFactory(request)?.GetExpressionOrDefault(request); - if (expr == null) - return null; - - var rules = container.Rules; - if (wrapperType == typeof(FactoryDelegate)) - return Constant(expr.CompileToFactoryDelegate(rules.UseFastExpressionCompiler, rules.UseInterpretation)); - - return Constant( - expr.CompileToFactoryDelegate(wrapperType, serviceType, rules.UseFastExpressionCompiler, rules.UseInterpretation), - wrapperType); - } - - private static Expression GetKeyValuePairExpressionOrDefault(Request request) - { - var keyValueType = request.GetActualServiceType(); - var typeArgs = keyValueType.GetGenericParamsAndArgs(); - var serviceKeyType = typeArgs[0]; - var serviceKey = request.ServiceKey; - if (serviceKey == null && serviceKeyType.IsValueType() || - serviceKey != null && !serviceKeyType.IsTypeOf(serviceKey)) - return null; - - var serviceType = typeArgs[1]; - var serviceRequest = request.Push(serviceType, serviceKey); - var serviceFactory = request.Container.ResolveFactory(serviceRequest); - var serviceExpr = serviceFactory?.GetExpressionOrDefault(serviceRequest); - if (serviceExpr == null) - return null; - - var keyExpr = request.Container.GetConstantExpression(serviceKey, serviceKeyType); - return New( - keyValueType.GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 2), - keyExpr, serviceExpr); - } - - /// Discovers and combines service with its setup metadata. - /// Works with any generic type with first Type arg - Service type and second Type arg - Metadata type, - /// and constructor with Service and Metadata arguments respectively. - /// - if service key is not specified in request then method will search for all - /// registered factories with the same metadata type ignoring keys. - /// - if metadata is IDictionary{string, object}, - /// then the First value matching the TMetadata type will be returned. - public static Expression GetMetaExpressionOrDefault(Request request) - { - var metaType = request.GetActualServiceType(); - var typeArgs = metaType.GetGenericParamsAndArgs(); - - var metaCtor = metaType.GetConstructorOrNull(typeArgs) - .ThrowIfNull(Error.NotFoundMetaCtorWithTwoArgs, typeArgs, request); - - var metadataType = typeArgs[1]; - var serviceType = typeArgs[0]; - - var container = request.Container; - var requiredServiceType = container.GetWrappedType(serviceType, request.RequiredServiceType); - - var factories = container - .GetAllServiceFactories(requiredServiceType, bothClosedAndOpenGenerics: true) - .ToArrayOrSelf(); - - if (factories.Length == 0) - return null; - - var serviceKey = request.ServiceKey; - if (serviceKey != null) - { - factories = factories.Match(serviceKey, (key, f) => key.Equals(f.Key)); - if (factories.Length == 0) - return null; - } - - // if the service keys for some reason are not unique - factories = factories - .Match(metadataType, (mType, f) => - { - var metadata = f.Value.Setup.Metadata; - if (metadata == null) - return false; - - if (mType == typeof(object)) - return true; - - var metadataDict = metadata as IDictionary; - if (metadataDict != null) - return mType == typeof(IDictionary) || metadataDict.Values.Any(m => mType.IsTypeOf(m)); - - return mType.IsTypeOf(metadata); - }); - - if (factories.Length == 0) - return null; - - // Prevent non-determinism when more than 1 factory is matching the metadata - if (factories.Length > 1) - { - if (request.IfUnresolved == IfUnresolved.Throw) - Throw.It(Error.UnableToSelectFromManyRegistrationsWithMatchingMetadata, metadataType, factories, request); - return null; - } - - var factory = factories[0]; - if (factory == null) - return null; - - serviceKey = factory.Key; - - var serviceRequest = request.Push(serviceType, serviceKey); - var serviceFactory = container.ResolveFactory(serviceRequest); - var serviceExpr = serviceFactory?.GetExpressionOrDefault(serviceRequest); - if (serviceExpr == null) - return null; - - var resultMetadata = factory.Value.Setup.Metadata; - if (metadataType != typeof(object)) - { - var resultMetadataDict = resultMetadata as IDictionary; - if (resultMetadataDict != null && metadataType != typeof(IDictionary)) - resultMetadata = resultMetadataDict.Values.FirstOrDefault(m => metadataType.IsTypeOf(m)); - } - - var metadataExpr = container.GetConstantExpression(resultMetadata, metadataType); - return New(metaCtor, serviceExpr, metadataExpr); - } - } - - /// Represents info required for dynamic registration: service key, factory, - /// and option how to combine dynamic with normal registrations. - public sealed class DynamicRegistration - { - /// Factory - public readonly Factory Factory; - - /// Optional: will be by default. - public readonly IfAlreadyRegistered IfAlreadyRegistered; - - /// Optional service key: if null the default will be used. - public readonly object ServiceKey; - - /// Constructs the info - public DynamicRegistration(Factory factory, - IfAlreadyRegistered ifAlreadyRegistered = IfAlreadyRegistered.AppendNotKeyed, object serviceKey = null) - { - Factory = factory.ThrowIfNull().DoNotCache(); - ServiceKey = serviceKey; - IfAlreadyRegistered = ifAlreadyRegistered; - } - } - - /// Defines resolution/registration rules associated with Container instance. They may be different for different containers. - public sealed class Rules - { - /// Default rules as staring point. - public static readonly Rules Default = new Rules(); - - /// Default rules as staring point. - public static readonly Rules MicrosoftDependencyInjectionRules = new Rules( - (DEFAULT_SETTINGS | Settings.TrackingDisposableTransients) & ~Settings.ThrowOnRegisteringDisposableTransient, - Rules.SelectLastRegisteredFactory(), Reuse.Transient, - Made.Of(DryIoc.FactoryMethod.ConstructorWithResolvableArguments), - IfAlreadyRegistered.AppendNotKeyed, - DefaultDependencyDepthToSplitObjectGraph, null, null, null, null, null); - - /// Default value for - public const int DefaultDependencyDepthToSplitObjectGraph = 20; - - /// Nested dependency depth to split an object graph - public int DependencyDepthToSplitObjectGraph { get; private set; } - - /// Sets . - /// Set to prevent split. - /// To disable the limit please use - public Rules WithDependencyDepthToSplitObjectGraph(int depth) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, depth < 1 ? 1 : depth, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Disables the limitation. - public Rules WithoutDependencyDepthToSplitObjectGraph() => WithDependencyDepthToSplitObjectGraph(int.MaxValue); - - /// Shorthand to - public FactoryMethodSelector FactoryMethod => _made.FactoryMethod; - - /// Shorthand to - public ParameterSelector Parameters => _made.Parameters; - - /// Shorthand to - public PropertiesAndFieldsSelector PropertiesAndFields => _made.PropertiesAndFields; - - /// Instructs to override per-registration made settings with these rules settings. - public bool OverrideRegistrationMade => - (_settings & Settings.OverrideRegistrationMade) != 0; - - /// Returns new instance of the rules new Made composed out of - /// provided factory method, parameters, propertiesAndFields. - public Rules With( - FactoryMethodSelector factoryMethod = null, - ParameterSelector parameters = null, - PropertiesAndFieldsSelector propertiesAndFields = null) => - With(Made.Of(factoryMethod, parameters, propertiesAndFields)); - - /// Returns new instance of the rules with specified . - /// New Made.Of rules. - /// Instructs to override registration level Made.Of - /// New rules. - public Rules With(Made made, bool overrideRegistrationMade = false) => - new Rules( - _settings | (overrideRegistrationMade ? Settings.OverrideRegistrationMade : 0), - FactorySelector, DefaultReuse, - _made == Made.Default - ? made - : Made.Of( - made.FactoryMethod ?? _made.FactoryMethod, - made.Parameters ?? _made.Parameters, - made.PropertiesAndFields ?? _made.PropertiesAndFields), - DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Service key to be used instead on `null` in registration. - public object DefaultRegistrationServiceKey { get; } - - /// Sets the - public Rules WithDefaultRegistrationServiceKey(object serviceKey) => - serviceKey == null ? this : - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, serviceKey); - - /// Defines single factory selector delegate. - /// Provides service request leading to factory selection. - /// Registered factories with corresponding key to select from. - /// Single selected factory, or null if unable to select. - public delegate Factory FactorySelectorRule(Request request, KeyValuePair[] factories); - - /// Rules to select single matched factory default and keyed registered factory/factories. - /// Selectors applied in specified array order, until first returns not null . - /// Default behavior is to throw on multiple registered default factories, cause it is not obvious what to use. - public FactorySelectorRule FactorySelector { get; } - - /// Sets - public Rules WithFactorySelector(FactorySelectorRule rule) => - new Rules(_settings | (rule == SelectLastRegisteredFactory ? Settings.SelectLastRegisteredFactory : default(Settings)), - rule, DefaultReuse, _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Select last registered factory from the multiple default. - public static FactorySelectorRule SelectLastRegisteredFactory() => SelectLastRegisteredFactory; - private static Factory SelectLastRegisteredFactory(Request request, KeyValuePair[] factories) - { - var serviceKey = request.ServiceKey; - for (var i = factories.Length - 1; i >= 0; i--) - { - var factory = factories[i]; - if (factory.Key.Equals(serviceKey)) - return factory.Value; - } - return null; - } - - //we are watching you...public static - /// Prefer specified service key (if found) over default key. - /// Help to override default registrations in Open Scope scenarios: - /// I may register service with key and resolve it as default in current scope. - public static FactorySelectorRule SelectKeyedOverDefaultFactory(object serviceKey) => - (r, fs) => fs.FindFirst(serviceKey, (key, f) => f.Key.Equals(key)).Value ?? - fs.FindFirst(f => f.Key.Equals(null)).Value; - - /// Specify the method signature for returning multiple keyed factories. - /// This is dynamic analog to the normal Container Registry. - /// Requested service type. - /// (optional) If null will request all factories of - /// Key-Factory pairs. - public delegate IEnumerable DynamicRegistrationProvider(Type serviceType, object serviceKey); - - /// Providers for resolving multiple not-registered services. Null by default. - public DynamicRegistrationProvider[] DynamicRegistrationProviders { get; private set; } - - // todo: Should I use Settings.UseDynamicRegistrationsAsFallback, 5 tests are failing only - /// Appends dynamic registration rules. - public Rules WithDynamicRegistrations(params DynamicRegistrationProvider[] rules) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders.Append(rules), UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Appends dynamic registration rules - /// And additionally specifies to use dynamic registrations only when no normal registrations found! - /// Rules to append. New Rules. - public Rules WithDynamicRegistrationsAsFallback(params DynamicRegistrationProvider[] rules) => - new Rules(_settings | Settings.UseDynamicRegistrationsAsFallbackOnly, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders.Append(rules), UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Specifies to use dynamic registrations only when no normal registrations found - public bool UseDynamicRegistrationsAsFallbackOnly => - (_settings & Settings.UseDynamicRegistrationsAsFallbackOnly) != 0; - - /// Defines delegate to return factory for request not resolved by registered factories or prior rules. - /// Applied in specified array order until return not null . - public delegate Factory UnknownServiceResolver(Request request); - - /// Gets rules for resolving not-registered services. Null by default. - public UnknownServiceResolver[] UnknownServiceResolvers { get; private set; } - - /// Appends resolver to current unknown service resolvers. - public Rules WithUnknownServiceResolvers(params UnknownServiceResolver[] rules) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers.Append(rules), - DefaultRegistrationServiceKey); - - /// Removes specified resolver from unknown service resolvers, and returns new Rules. - /// If no resolver was found then will stay the same instance, - /// so it could be check for remove success or fail. - public Rules WithoutUnknownServiceResolver(UnknownServiceResolver rule) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers.Remove(rule), - DefaultRegistrationServiceKey); - - /// Sugar on top of to simplify setting the diagnostic action. - /// Does not guard you from action throwing an exception. Actually can be used to throw your custom exception - /// instead of . - public Rules WithUnknownServiceHandler(Action handler) => - WithUnknownServiceResolvers(request => - { - handler(request); - return null; - }); - - /// The alternative is ConcreteTypeDynamicRegistrations - public static UnknownServiceResolver AutoResolveConcreteTypeRule(Func condition = null) => - request => - { - var concreteType = request.GetActualServiceType(); - if (!concreteType.IsImplementationType() || concreteType.IsPrimitive() || - condition != null && !condition(request)) - return null; - - var openGenericServiceType = concreteType.GetGenericDefinitionOrNull(); - if (openGenericServiceType != null && WrappersSupport.Wrappers.GetValueOrDefault(openGenericServiceType) != null) - return null; - - var factory = new ReflectionFactory(concreteType, - made: DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic); - - // to enable fallback to other rules if unresolved try to resolve expression first and return null - return factory.GetExpressionOrDefault(request.WithIfUnresolved(IfUnresolved.ReturnDefault)) != null ? factory : null; - }; - - /// Rule to automatically resolves non-registered service type which is: nor interface, nor abstract. - /// For constructor selection we are using . - /// The resolution creates transient services. - /// (optional) Condition for requested service type and key. - /// (optional) Reuse for concrete types. - /// New rule. - public static DynamicRegistrationProvider ConcreteTypeDynamicRegistrations( - Func condition = null, IReuse reuse = null) => - AutoFallbackDynamicRegistrations((serviceType, serviceKey) => - { - if (serviceType.IsAbstract() || - serviceType.IsOpenGeneric() || // service type in principle should be concrete, so should not be open-generic - condition != null && !condition(serviceType, serviceKey)) - return null; - - // exclude concrete service types which are pre-defined DryIoc wrapper types - var openGenericServiceType = serviceType.GetGenericDefinitionOrNull(); - if (openGenericServiceType != null && WrappersSupport.Wrappers.GetValueOrDefault(openGenericServiceType) != null) - return null; - - return serviceType.One(); // use concrete service type as implementation type - }, - implType => - { - ReflectionFactory factory = null; - - // the condition checks that factory is resolvable - factory = new ReflectionFactory(implType, reuse, - DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic, - Setup.With(condition: req => factory?.GetExpressionOrDefault(req.WithIfUnresolved(IfUnresolved.ReturnDefault)) != null)); - - return factory; - }); - - /// Automatically resolves non-registered service type which is: nor interface, nor abstract. - /// The resolution creates Transient services. - public Rules WithConcreteTypeDynamicRegistrations( - Func condition = null, IReuse reuse = null) => - WithDynamicRegistrationsAsFallback(ConcreteTypeDynamicRegistrations(condition, reuse)); - - /// Replaced with `WithConcreteTypeDynamicRegistrations` - public Rules WithAutoConcreteTypeResolution(Func condition = null) => - new Rules(_settings | Settings.AutoConcreteTypeResolution, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers.Append(AutoResolveConcreteTypeRule(condition)), - DefaultRegistrationServiceKey); - - /// Creates dynamic fallback registrations for the requested service type - /// with provided . - /// Fallback means that the dynamic registrations will be applied Only if no normal registrations - /// exist for the requested service type, hence the "fallback". - /// Implementation types to select for service. - /// (optional) Handler to customize the factory, e.g. - /// specify reuse or setup. Handler should not return null. - /// Registration provider. - public static DynamicRegistrationProvider AutoFallbackDynamicRegistrations( - Func> getImplementationTypes, - Func factory = null) - { - // cache factory for implementation type to enable reuse semantics - var factories = Ref.Of(ImHashMap.Empty); - - return (serviceType, serviceKey) => - { - if (!serviceType.IsServiceType()) - return Enumerable.Empty(); - - var implementationTypes = getImplementationTypes(serviceType, serviceKey); - - return implementationTypes.Match( - implType => implType.IsImplementingServiceType(serviceType), - implType => - { - var implTypeHash = RuntimeHelpers.GetHashCode(implType); - var implFactory = factories.Value.GetValueOrDefault(implTypeHash, implType); - if (implFactory == null) - { - if (factory == null) - factories.Swap(fs => (implFactory = fs.GetValueOrDefault(implTypeHash, implType)) != null ? fs - : fs.AddOrUpdate(implTypeHash, implType, implFactory = new ReflectionFactory(implType))); - else - factories.Swap(fs => (implFactory = fs.GetValueOrDefault(implTypeHash, implType)) != null ? fs - : fs.AddOrUpdate(implTypeHash, implType, implFactory = factory.Invoke(implType).ThrowIfNull())); - } - - // We nullify default keys (usually passed by ResolveMany to resolve the specific factory in order) - // so that `CombineRegisteredWithDynamicFactories` may assign the key again. - // Given that the implementation types are unchanged then the new keys assignement will be the same the last one, - // so that the factory resolution will correctly match the required factory by key. - // e.g. bitbucket issue #396 - var theKey = serviceKey is DefaultDynamicKey ? null : serviceKey; - return new DynamicRegistration(implFactory, IfAlreadyRegistered.Keep, theKey); - }); - }; - } - - /// Obsolete: replaced by - [Obsolete("Replaced by " + nameof(AutoFallbackDynamicRegistrations), false)] - public static UnknownServiceResolver AutoRegisterUnknownServiceRule(IEnumerable implTypes, - Func changeDefaultReuse = null, Func condition = null) => - request => - { - if (condition != null && !condition(request)) - return null; - - var scope = request.Container.CurrentScope; - var reuse = scope != null ? Reuse.ScopedTo(scope.Name) : Reuse.Singleton; - - if (changeDefaultReuse != null) - reuse = changeDefaultReuse(reuse, request); - - var requestedServiceType = request.GetActualServiceType(); - request.Container.RegisterMany(implTypes, reuse, - serviceTypeCondition: serviceType => - serviceType.IsOpenGeneric() && requestedServiceType.IsClosedGeneric() - ? serviceType == requestedServiceType.GetGenericTypeDefinition() - : serviceType == requestedServiceType); - - return request.Container.GetServiceFactoryOrDefault(request); - }; - - /// See - public IReuse DefaultReuse { get; } - - /// The reuse used in case if reuse is unspecified (null) in Register methods. - public Rules WithDefaultReuse(IReuse reuse) => - new Rules(_settings, FactorySelector, reuse ?? Reuse.Transient, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Replaced by WithDefaultReuse because for some cases InsteadOfTransient does not make sense. - [Obsolete("Replaced by WithDefaultReuse because for some cases ..InsteadOfTransient does not make sense.", error: false)] - public Rules WithDefaultReuseInsteadOfTransient(IReuse reuse) => WithDefaultReuse(reuse); - - /// Given item object and its type should return item "pure" expression presentation, - /// without side-effects or external dependencies. - /// e.g. for string "blah" Expression.Constant("blah", typeof(string)). - /// If unable to convert should return null. - public delegate Expression ItemToExpressionConverterRule(object item, Type itemType); - - /// . - public ItemToExpressionConverterRule ItemToExpressionConverter { get; private set; } - - /// Specifies custom rule to convert non-primitive items to their expression representation. - /// That may be required because DryIoc by default does not support non-primitive service keys and registration metadata. - /// To enable non-primitive values support DryIoc need a way to recreate them as expression tree. - public Rules WithItemToExpressionConverter(ItemToExpressionConverterRule itemToExpressionOrDefault) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, itemToExpressionOrDefault, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// . - public bool ThrowIfDependencyHasShorterReuseLifespan => - (_settings & Settings.ThrowIfDependencyHasShorterReuseLifespan) != 0; - - /// Turns off throwing exception when dependency has shorter reuse lifespan than its parent or ancestor. - /// New rules with new setting value. - public Rules WithoutThrowIfDependencyHasShorterReuseLifespan() => - WithSettings(_settings & ~Settings.ThrowIfDependencyHasShorterReuseLifespan); - - /// - public bool ThrowOnRegisteringDisposableTransient => - (_settings & Settings.ThrowOnRegisteringDisposableTransient) != 0; - - /// Turns Off the rule . - /// Allows to register disposable transient but it is up to you to handle their disposal. - /// You can use to actually track disposable transient in - /// container, so that disposal will be handled by container. - public Rules WithoutThrowOnRegisteringDisposableTransient() => - WithSettings(_settings & ~Settings.ThrowOnRegisteringDisposableTransient); - - /// - public bool TrackingDisposableTransients => - (_settings & Settings.TrackingDisposableTransients) != 0; - - /// Turns tracking of disposable transients in dependency parent scope, or in current scope if service - /// is resolved directly. - /// - /// If there is no open scope at the moment then resolved transient won't be tracked and it is up to you - /// to dispose it! That's is similar situation to creating service by new - you have full control. - /// - /// If dependency wrapped in Func somewhere in parent chain then it also won't be tracked, because - /// Func supposedly means multiple object creation and for container it is not clear what to do, so container - /// delegates that to user. Func here is the similar to Owned relationship type in Autofac library. - /// - /// Turning this setting On automatically turns off . - public Rules WithTrackingDisposableTransients() => - WithSettings((_settings | Settings.TrackingDisposableTransients) - & ~Settings.ThrowOnRegisteringDisposableTransient); - - /// . - public bool EagerCachingSingletonForFasterAccess => - (_settings & Settings.EagerCachingSingletonForFasterAccess) != 0; - - /// Turns off optimization: creating singletons during resolution of object graph. - public Rules WithoutEagerCachingSingletonForFasterAccess() => - WithSettings(_settings & ~Settings.EagerCachingSingletonForFasterAccess); - - /// . - public Ref> DependencyResolutionCallExprs { get; private set; } - - /// Indicates that container is used for generation purposes, so it should use less runtime state - public bool UsedForExpressionGeneration => (_settings & Settings.UsedForExpressionGeneration) != 0; - - private Settings GetSettingsForExpressionGeneration(bool allowRuntimeState = false) => - _settings & ~Settings.EagerCachingSingletonForFasterAccess - & ~Settings.ImplicitCheckForReuseMatchingScope - & ~Settings.UseInterpretationForTheFirstResolution - & ~Settings.UseInterpretation - | Settings.UsedForExpressionGeneration - | (allowRuntimeState ? 0 : Settings.ThrowIfRuntimeStateRequired); - - /// Specifies to generate ResolutionCall dependency creation expression and stores the result - /// in the-per rules collection. - public Rules WithExpressionGeneration(bool allowRuntimeState = false) => - new Rules(GetSettingsForExpressionGeneration(allowRuntimeState), FactorySelector, DefaultReuse, - _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - Ref.Of(ImHashMap.Empty), ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// Indicates that rules are used for the validation, e.g. the rules created in `Validate` method - public bool UsedForValidation => (_settings & Settings.UsedForValidation) != 0; - - private Settings GetSettingsForValidation() => - _settings & ~Settings.EagerCachingSingletonForFasterAccess - & ~Settings.ImplicitCheckForReuseMatchingScope - | Settings.UsedForValidation; - - /// Specifies to generate ResolutionCall dependency creation expression and stores the result - /// in the-per rules collection. - public Rules ForValidate() => - new Rules(GetSettingsForValidation(), - FactorySelector, DefaultReuse, _made, DefaultIfAlreadyRegistered, - int.MaxValue, // lifts the `DefaultDependencyDepthToSplitObjectGraph` to the Max, so we will construct the whole tree - null, - ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// - public bool ImplicitCheckForReuseMatchingScope => - (_settings & Settings.ImplicitCheckForReuseMatchingScope) != 0; - - /// Removes implicit Factory for non-transient service. - /// The Condition filters out factory without matching scope. - public Rules WithoutImplicitCheckForReuseMatchingScope() => - WithSettings(_settings & ~Settings.ImplicitCheckForReuseMatchingScope); - - /// Removes runtime optimizations preventing an expression generation. - public Rules ForExpressionGeneration(bool allowRuntimeState = false) => WithSettings(GetSettingsForExpressionGeneration()); - - /// . - public bool ResolveIEnumerableAsLazyEnumerable => - (_settings & Settings.ResolveIEnumerableAsLazyEnumerable) != 0; - - /// Specifies to resolve IEnumerable as LazyEnumerable. - public Rules WithResolveIEnumerableAsLazyEnumerable() => - WithSettings(_settings | Settings.ResolveIEnumerableAsLazyEnumerable); - - /// . - public bool VariantGenericTypesInResolvedCollection => - (_settings & Settings.VariantGenericTypesInResolvedCollection) != 0; - - /// Flag instructs to include covariant compatible types in resolved collection. - public Rules WithoutVariantGenericTypesInResolvedCollection() => - WithSettings(_settings & ~Settings.VariantGenericTypesInResolvedCollection); - - /// . - public IfAlreadyRegistered DefaultIfAlreadyRegistered { get; } - - /// Specifies default setting for container. By default is . - /// Example of use: specify Keep as a container default, then set AppendNonKeyed for explicit collection registrations. - public Rules WithDefaultIfAlreadyRegistered(IfAlreadyRegistered rule) => - new Rules(_settings, FactorySelector, DefaultReuse, - _made, rule, DependencyDepthToSplitObjectGraph, DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - /// . - public bool ThrowIfRuntimeStateRequired => - (_settings & Settings.ThrowIfRuntimeStateRequired) != 0; - - /// Specifies to throw an exception in attempt to resolve service which require runtime state for resolution. - /// Runtime state may be introduced by RegisterDelegate, RegisterInstance, or registering with non-primitive service key, or metadata. - public Rules WithThrowIfRuntimeStateRequired() => - WithSettings(_settings | Settings.ThrowIfRuntimeStateRequired); - - /// . - public bool CaptureContainerDisposeStackTrace => - (_settings & Settings.CaptureContainerDisposeStackTrace) != 0; - - /// Instructs to capture Dispose stack-trace to include it later into - /// exception for easy diagnostics. - public Rules WithCaptureContainerDisposeStackTrace() => - WithSettings(_settings | Settings.CaptureContainerDisposeStackTrace); - - /// Allows Func with args specify its own reuse (sharing) behavior. - public bool IgnoringReuseForFuncWithArgs => - (_settings & Settings.IgnoringReuseForFuncWithArgs) != 0; - - /// Allows Func with args specify its own reuse (sharing) behavior. - public Rules WithIgnoringReuseForFuncWithArgs() => - WithSettings(_settings | Settings.IgnoringReuseForFuncWithArgs); - - /// Allows Func of service to be resolved even without registered service. - public bool FuncAndLazyWithoutRegistration => - (_settings & Settings.FuncAndLazyWithoutRegistration) != 0; - - /// Allows Func of service to be resolved even without registered service. - public Rules WithFuncAndLazyWithoutRegistration() => - WithSettings(_settings | Settings.FuncAndLazyWithoutRegistration); - - /// Commands to use FastExpressionCompiler - set by default. - public bool UseFastExpressionCompiler => - (_settings & Settings.UseFastExpressionCompilerIfPlatformSupported) != 0; - - /// Fallbacks to system `Expression.Compile()` - public Rules WithoutFastExpressionCompiler() => - WithSettings(_settings & ~Settings.UseFastExpressionCompilerIfPlatformSupported); - - /// Subject-subject - public bool UseInterpretationForTheFirstResolution => - (_settings & Settings.UseInterpretationForTheFirstResolution) != 0; - - /// Fallbacks to system `Expression.Compile()` - public Rules WithoutInterpretationForTheFirstResolution() => - WithSettings(_settings & ~Settings.UseInterpretationForTheFirstResolution & ~Settings.UseInterpretation); - - /// Subject - public bool UseInterpretation => - (_settings & Settings.UseInterpretation) != 0; - - /// Uses DryIoc own interpretation mechanism or is falling back to `Compile(preferInterpretation: true)` - public Rules WithUseInterpretation() => - WithSettings(_settings | Settings.UseInterpretation | Settings.UseInterpretationForTheFirstResolution); - - /// Uses DryIoc own interpretation mechanism or is falling back to `Compile(preferInterpretation: true)` - public Rules WithoutUseInterpretation() => - WithSettings(_settings & ~Settings.UseInterpretation); - - /// If Decorator reuse is not set instructs to use `Decorator.SetupWith(useDecarateeReuse: true)` - public bool UseDecorateeReuseForDecorators => - (_settings & Settings.UseDecorateeReuseForDecorators) != 0; - - /// If Decorator reuse is not set instructs to use `Decorator.SetupWith(useDecarateeReuse: true)` - public Rules WithUseDecorateeReuseForDecorators() => - WithSettings(_settings | Settings.UseDecorateeReuseForDecorators); - - /// Outputs most notable non-default rules - public override string ToString() - { - if (this == Default) - return "Rules.Default"; - - string s = ""; - if (_settings != DEFAULT_SETTINGS) - { - var addedSettings = _settings & ~DEFAULT_SETTINGS; - if (addedSettings != 0) - s = "Rules with {" + addedSettings + "}"; - var removedSettings = DEFAULT_SETTINGS & ~_settings; - if (removedSettings != 0) - s += (s != "" ? " and without {" : "Rules without {") + removedSettings + "}"; - } - - if (DependencyDepthToSplitObjectGraph != DefaultDependencyDepthToSplitObjectGraph) - s += " with DepthToSplitObjectGraph=" + DependencyDepthToSplitObjectGraph; - - if (DefaultReuse != null && DefaultReuse != Reuse.Transient) - s += (s != "" ? NewLine : "Rules ") + " with DefaultReuse=" + DefaultReuse; - - if (FactorySelector != null) - { - s += (s != "" ? NewLine : "Rules ") + " with FactorySelector="; - if (FactorySelector == SelectLastRegisteredFactory) - s += nameof(Rules.SelectLastRegisteredFactory); - else - s += ""; - } - - if (_made != Made.Default) - s += (s != "" ? NewLine : "Rules ") + " with Made=" + _made; - - return s; - } - -#region Implementation - - private Rules() - { - _made = Made.Default; - _settings = DEFAULT_SETTINGS; - DefaultReuse = Reuse.Transient; - DefaultIfAlreadyRegistered = IfAlreadyRegistered.AppendNotKeyed; - DependencyDepthToSplitObjectGraph = DefaultDependencyDepthToSplitObjectGraph; - } - - private Rules(Settings settings, - FactorySelectorRule factorySelector, - IReuse defaultReuse, - Made made, - IfAlreadyRegistered defaultIfAlreadyRegistered, - int dependencyDepthToSplitObjectGraph, - Ref> dependencyResolutionCallExprs, - ItemToExpressionConverterRule itemToExpressionConverter, - DynamicRegistrationProvider[] dynamicRegistrationProviders, - UnknownServiceResolver[] unknownServiceResolvers, - object defaultRegistrationServiceKey) - { - _settings = settings; - _made = made; - FactorySelector = factorySelector; - DefaultReuse = defaultReuse; - DefaultIfAlreadyRegistered = defaultIfAlreadyRegistered; - DependencyDepthToSplitObjectGraph = dependencyDepthToSplitObjectGraph; - DependencyResolutionCallExprs = dependencyResolutionCallExprs; - ItemToExpressionConverter = itemToExpressionConverter; - DynamicRegistrationProviders = dynamicRegistrationProviders; - UnknownServiceResolvers = unknownServiceResolvers; - DefaultRegistrationServiceKey = defaultRegistrationServiceKey; - } - - private Rules WithSettings(Settings newSettings) => - new Rules(newSettings, - FactorySelector, DefaultReuse, _made, DefaultIfAlreadyRegistered, DependencyDepthToSplitObjectGraph, - DependencyResolutionCallExprs, ItemToExpressionConverter, - DynamicRegistrationProviders, UnknownServiceResolvers, DefaultRegistrationServiceKey); - - private readonly Made _made; - - [Flags] - private enum Settings - { - ThrowIfDependencyHasShorterReuseLifespan = 1 << 1, - ThrowOnRegisteringDisposableTransient = 1 << 2, - TrackingDisposableTransients = 1 << 3, - ImplicitCheckForReuseMatchingScope = 1 << 4, - VariantGenericTypesInResolvedCollection = 1 << 5, - ResolveIEnumerableAsLazyEnumerable = 1 << 6, - EagerCachingSingletonForFasterAccess = 1 << 7, - ThrowIfRuntimeStateRequired = 1 << 8, - CaptureContainerDisposeStackTrace = 1 << 9, - UseDynamicRegistrationsAsFallbackOnly = 1 << 10, - IgnoringReuseForFuncWithArgs = 1 << 11, - OverrideRegistrationMade = 1 << 12, - FuncAndLazyWithoutRegistration = 1 << 13, - AutoConcreteTypeResolution = 1 << 14, // informational flag - SelectLastRegisteredFactory = 1 << 15,// informational flag - UsedForExpressionGeneration = 1 << 16, - UseFastExpressionCompilerIfPlatformSupported = 1 << 17, - UseInterpretationForTheFirstResolution = 1 << 18, - UseInterpretation = 1 << 19, - UseDecorateeReuseForDecorators = 1 << 20, - UsedForValidation = 1 << 21 // informational flag, will appear in exceptions during validation - } - - private const Settings DEFAULT_SETTINGS - = Settings.ThrowIfDependencyHasShorterReuseLifespan - | Settings.ThrowOnRegisteringDisposableTransient - | Settings.ImplicitCheckForReuseMatchingScope - | Settings.VariantGenericTypesInResolvedCollection - | Settings.EagerCachingSingletonForFasterAccess - | Settings.UseFastExpressionCompilerIfPlatformSupported - | Settings.UseInterpretationForTheFirstResolution; - - private Settings _settings; - -#endregion - } - - /// Wraps constructor or factory method optionally with factory instance to create service. - public sealed class FactoryMethod - { - /// Constructor or method to use for service creation. - public readonly MemberInfo ConstructorOrMethodOrMember; - - /// Identifies factory service if factory method is instance member. - public readonly ServiceInfo FactoryServiceInfo; - - /// Alternatively you may just provide an expression for factory - public readonly Expression FactoryExpression; - - /// Contains resolved parameter expressions found when looking for most resolvable constructor - internal readonly Expression[] ResolvedParameterExpressions; - - /// Wraps method and factory instance. - /// Where is constructor, static or instance method, property or field. - public static FactoryMethod Of(MemberInfo ctorOrMethodOrMember, ServiceInfo factoryInfo = null) - { - if (ctorOrMethodOrMember == null) - Throw.It(Error.PassedCtorOrMemberIsNull); - - if (ctorOrMethodOrMember is ConstructorInfo == false && !ctorOrMethodOrMember.IsStatic()) - { - if (factoryInfo == null) - Throw.It(Error.PassedMemberIsNotStaticButInstanceFactoryIsNull, ctorOrMethodOrMember); - } - else - { - if (factoryInfo != null) - Throw.It(Error.PassedMemberIsStaticButInstanceFactoryIsNotNull, ctorOrMethodOrMember, factoryInfo); - } - - return new FactoryMethod(ctorOrMethodOrMember, factoryInfo); - } - - /// Wraps method and factory instance. - /// Where is constructor, static or instance method, property or field. - public static FactoryMethod Of(MemberInfo methodOrMember, object factoryInstance) - { - factoryInstance.ThrowIfNull(); - methodOrMember.ThrowIfNull(Error.PassedCtorOrMemberIsNull); - if (methodOrMember.IsStatic()) - Throw.It(Error.PassedMemberIsStaticButInstanceFactoryIsNotNull, methodOrMember, factoryInstance); - return new FactoryMethod(methodOrMember, Constant(factoryInstance)); - } - - /// Discovers the static factory method or member by name in . - /// Should play nice with C# operator. - public static FactoryMethod Of(string methodOrMemberName) => - Of(typeof(TFactory).GetAllMembers().FindFirst(m => m.Name == methodOrMemberName)); - - /// Pretty prints wrapped method. - public override string ToString() - { - var s = new StringBuilder("{") - .Print(ConstructorOrMethodOrMember.DeclaringType) - .Append('.').Append(ConstructorOrMethodOrMember); - if (FactoryServiceInfo != null) - s.Append(" of factory service ").Append(FactoryServiceInfo); - if (FactoryExpression != null) - s.Append(" of factory expression ").Append(FactoryExpression); - return s.Append('}').ToString(); - } - - private struct CtorWithParameters - { - public ConstructorInfo Ctor; - public ParameterInfo[] Params; - } - - private static void OrderByParamsLengthDescendingViaInsertionSort(CtorWithParameters[] items) - { - int i, j; - for (i = 1; i < items.Length; i++) - { - var tmp = items[i]; - - for (j = i; j >= 1 && tmp.Params.Length > items[j - 1].Params.Length; j--) - { - ref var target = ref items[j]; - var source = items[j - 1]; - target.Ctor = source.Ctor; - target.Params = source.Params; - } - - ref var x = ref items[j]; - x.Ctor = tmp.Ctor; - x.Params = tmp.Params; - } - } - - /// Easy way to specify non-public and most resolvable constructor. - /// (optional) Instructs to select constructor with max number of params which all are resolvable. - /// (optional) Consider the non-public constructors. - /// Constructor or null if not found. - public static FactoryMethodSelector Constructor(bool mostResolvable = false, bool includeNonPublic = false) => request => - { - var implType = request.ImplementationType; - if (implType == null) - Throw.It(Error.ImplTypeIsNotSpecifiedForAutoCtorSelection, request); - - var ctors = includeNonPublic ? implType.GetInstanceConstructors() : implType.GetPublicInstanceConstructors(); - var ctorCount = ctors.Length; - if (ctorCount == 0) - return null; - - // if there is only one constructor then use it - if (ctorCount == 1) - return new FactoryMethod(ctors[0]); - - // stop here if you need a lookup for most resolvable constructor - if (!mostResolvable) - return null; - - var rules = request.Rules; - var selector = rules.OverrideRegistrationMade - ? request.Made.Parameters.OverrideWith(rules.Parameters) - : rules.Parameters.OverrideWith(request.Made.Parameters); - var paramSelector = selector(request); - - var throwIfCtorNotFound = request.IfUnresolved != IfUnresolved.ReturnDefault; - if (throwIfCtorNotFound) - request = request.WithIfUnresolved(IfUnresolved.ReturnDefault); - - var ctorsWithParameters = new CtorWithParameters[ctors.Length]; - if (ctors.Length == 2) - { - ref var pos0 = ref ctorsWithParameters[0]; - ref var pos1 = ref ctorsWithParameters[1]; - - var ctor0Params = ctors[0].GetParameters(); - var ctor1Params = ctors[1].GetParameters(); - if (ctor1Params.Length > ctor0Params.Length) - { - pos0.Ctor = ctors[1]; - pos0.Params = ctor1Params; - pos1.Ctor = ctors[0]; - pos1.Params = ctor0Params; - } - else - { - pos0.Ctor = ctors[0]; - pos0.Params = ctor0Params; - pos1.Ctor = ctors[1]; - pos1.Params = ctor1Params; - } - } - else - { - for (var i = 0; i < ctors.Length; i++) - { - var x = ctors[i]; - ref var pos = ref ctorsWithParameters[i]; - pos.Ctor = x; - pos.Params = x.GetParameters(); - } - - OrderByParamsLengthDescendingViaInsertionSort(ctorsWithParameters); - } - - var mostUsedArgCount = -1; - ConstructorInfo mostResolvedCtor = null; - Expression[] mostResolvedExprs = null; - for (var c = 0; c < ctorsWithParameters.Length; ++c) - { - var parameters = ctorsWithParameters[c].Params; - if (parameters.Length == 0) - { - mostResolvedCtor = mostResolvedCtor ?? ctorsWithParameters[c].Ctor; - break; - } - - // If the most resolved expressions (constructor) is found and the next one has less parameters, we exit. - if (mostResolvedExprs != null && mostResolvedExprs.Length > parameters.Length) - break; - - // Otherwise for similar parameters count constructor we prefer the one with most used input args / custom values - // Should count custom values provided via `Resolve(args)`, `Func`, `Parameter.Of...(_ -> arg)`, `container.Use(arg)` - var usedInputArgOrUsedOrCustomValueCount = 0; - var inputArgs = request.InputArgExprs; - var argsUsedMask = 0; - var paramExprs = new Expression[parameters.Length]; - - for (var i = 0; i < parameters.Length; i++) - { - var param = parameters[i]; - if (inputArgs != null) - { - var inputArgExpr = - ReflectionFactory.TryGetExpressionFromInputArgs(param.ParameterType, inputArgs, - ref argsUsedMask); - if (inputArgExpr != null) - { - ++usedInputArgOrUsedOrCustomValueCount; - paramExprs[i] = inputArgExpr; - continue; - } - } - - var paramInfo = paramSelector(param) ?? ParameterServiceInfo.Of(param); - var paramRequest = request.Push(paramInfo); - var paramDetails = paramInfo.Details; - var usedOrCustomValExpr = - ReflectionFactory.TryGetUsedInstanceOrCustomValueExpression(request, paramRequest, - paramDetails); - if (usedOrCustomValExpr != null) - { - ++usedInputArgOrUsedOrCustomValueCount; - paramExprs[i] = usedOrCustomValExpr; - continue; - } - - var injectedExpr = request.Container.ResolveFactory(paramRequest)?.GetExpressionOrDefault(paramRequest); - if (injectedExpr == null || - // When param is an empty array / collection, then we may use a default value instead (#581) - paramDetails.DefaultValue != null && - injectedExpr.NodeType == System.Linq.Expressions.ExpressionType.NewArrayInit && - ((NewArrayExpression) injectedExpr).Expressions.Count == 0) - { - // Check if parameter dependency itself (without propagated parent details) - // does not allow default, then stop checking the rest of parameters. - if (paramDetails.IfUnresolved == IfUnresolved.Throw) - { - paramExprs = null; - break; - } - - injectedExpr = paramDetails.DefaultValue != null - ? request.Container.GetConstantExpression(paramDetails.DefaultValue) - : paramRequest.ServiceType.GetDefaultValueExpression(); - } - - paramExprs[i] = injectedExpr; - } - - if (paramExprs != null && usedInputArgOrUsedOrCustomValueCount > mostUsedArgCount) - { - mostUsedArgCount = usedInputArgOrUsedOrCustomValueCount; - mostResolvedCtor = ctorsWithParameters[c].Ctor; - mostResolvedExprs = paramExprs; - } - } - - if (mostResolvedCtor == null) - return Throw.For(throwIfCtorNotFound, - Error.UnableToFindCtorWithAllResolvableArgs, request.InputArgExprs, request); - - return new FactoryMethod(mostResolvedCtor, mostResolvedExprs); - }; - - /// Easy way to specify default constructor to be used for resolution. - public static FactoryMethodSelector DefaultConstructor(bool includeNonPublic = false) => request => - request.ImplementationType.ThrowIfNull(Error.ImplTypeIsNotSpecifiedForAutoCtorSelection, request) - .GetConstructorOrNull(includeNonPublic, Empty()) - ?.To(ctor => new FactoryMethod(ctor)); - - /// Better be named `ConstructorWithMostResolvableArguments`. - /// Searches for public constructor with most resolvable parameters or throws if not found. - /// Works both for resolving service and `Func{TArgs..., TService}` - public static readonly FactoryMethodSelector ConstructorWithResolvableArguments = - Constructor(mostResolvable: true); - - /// Searches for constructor (including non public ones) with most - /// resolvable parameters or throws if not found. - /// Works both for resolving service and Func{TArgs..., TService} - public static readonly FactoryMethodSelector ConstructorWithResolvableArgumentsIncludingNonPublic = - Constructor(mostResolvable: true, includeNonPublic: true); - - /// Just creates a thingy from the constructor - public FactoryMethod(ConstructorInfo constructor) => ConstructorOrMethodOrMember = constructor; - - private FactoryMethod(MemberInfo constructorOrMethodOrMember, ServiceInfo factoryServiceInfo = null) - { - ConstructorOrMethodOrMember = constructorOrMethodOrMember; - FactoryServiceInfo = factoryServiceInfo; - } - - internal FactoryMethod(MemberInfo constructorOrMethodOrMember, Expression factoryExpression) - { - ConstructorOrMethodOrMember = constructorOrMethodOrMember; - FactoryExpression = factoryExpression; - } - - internal FactoryMethod(ConstructorInfo ctor, Expression[] resolvedParameterExpressions) - { - ConstructorOrMethodOrMember = ctor; - ResolvedParameterExpressions = resolvedParameterExpressions; - } - } - - /// Rules how to: - /// Select constructor for creating service with . - /// Specify how to resolve constructor parameters with . - /// Specify what properties/fields to resolve and how with . - /// - public class Made - { - /// Returns delegate to select constructor based on provided request. - public FactoryMethodSelector FactoryMethod { get; private set; } - - /// Return type of strongly-typed factory method expression. - public Type FactoryMethodKnownResultType { get; private set; } - - [Flags] - private enum MadeDetails - { - NoConditionals = 0, - ImplTypeDependsOnRequest = 1 << 1, - ImplMemberDependsOnRequest = 1 << 3, - HasCustomDependencyValue = 1 << 4 - } - - private readonly MadeDetails _details; - - /// Has any conditional flags - public bool IsConditional => _details != MadeDetails.NoConditionals; - - /// True is made has properties or parameters with custom value. - /// That's mean the whole made become context based which affects caching. - public bool HasCustomDependencyValue => (_details & MadeDetails.HasCustomDependencyValue) != 0; - - /// Indicates that the implementation type depends on request. - public bool IsConditionalImplementation => (_details & MadeDetails.ImplTypeDependsOnRequest) != 0; - - /// Indicates that the member depends on request - public bool IsImplMemberDependsOnRequest => (_details & MadeDetails.ImplMemberDependsOnRequest) != 0; - - /// Specifies how constructor parameters should be resolved: - /// parameter service key and type, throw or return default value if parameter is unresolved. - public ParameterSelector Parameters { get; private set; } - - /// Specifies what should be used when resolving property or field. - public PropertiesAndFieldsSelector PropertiesAndFields { get; private set; } - - /// Outputs whatever is possible (known) for Made - public override string ToString() - { - if (this == Default) - return "Made.Default"; - - var s = "{"; - if (FactoryMethod != null) - { - s += (s == "{" ? "" : ", ") + "FactoryMethod="; - if (FactoryMethod == DryIoc.FactoryMethod.ConstructorWithResolvableArguments) - s += nameof(DryIoc.FactoryMethod.ConstructorWithResolvableArguments); - else if (FactoryMethod == DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic) - s += nameof(DryIoc.FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic); - else - s += ""; - } - - if (FactoryMethodKnownResultType != null) - s += (s == "{" ? "" : ", ") + "FactoryMethodKnownResultType=" + FactoryMethodKnownResultType; - if (HasCustomDependencyValue) - s += (s == "{" ? "" : ", ") + "HasCustomDependencyValue=true"; - if (PropertiesAndFields != null) - s += (s == "{" ? "" : ", ") + "PropertiesAndFields="; - if (Parameters != null) - s += (s == "{" ? "" : ", ") + "ParameterSelector="; - return s + "}"; - } - - /// Container will use some sensible defaults for service creation. - public static readonly Made Default = new Made(); - - /// Creates rules with only specified. - public static implicit operator Made(FactoryMethodSelector factoryMethod) => - Of(factoryMethod); - - /// Creates rules with only specified. - public static implicit operator Made(ParameterSelector parameters) => - Of(parameters: parameters); - - /// Creates rules with only specified. - public static implicit operator Made(PropertiesAndFieldsSelector propertiesAndFields) => - Of(propertiesAndFields: propertiesAndFields); - - /// Specifies injections rules for Constructor, Parameters, Properties and Fields. If no rules specified returns rules. - public static Made Of(FactoryMethodSelector factoryMethod = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null, - bool isConditionalImlementation = false) => - factoryMethod == null && parameters == null && propertiesAndFields == null && !isConditionalImlementation ? Default : - new Made(factoryMethod, parameters, propertiesAndFields, isConditionalImlementation: isConditionalImlementation); - - /// Specifies injections rules for Constructor, Parameters, Properties and Fields. If no rules specified returns rules. - /// Known factory method. - /// (optional) (optional) - /// New injection rules. - public static Made Of(FactoryMethod factoryMethod, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) - { - var methodReturnType = factoryMethod.ThrowIfNull() - .ConstructorOrMethodOrMember.GetReturnTypeOrDefault(); - - // Normalizes open-generic type to open-generic definition, - // because for base classes and return types it may not be the case (they may be partially closed). - if (methodReturnType != null && methodReturnType.IsOpenGeneric()) - methodReturnType = methodReturnType.GetGenericTypeDefinition(); - - return new Made(factoryMethod.ToFunc, parameters, propertiesAndFields, methodReturnType); - } - - /// Creates factory method specification - public static Made Of(MemberInfo factoryMethodOrMember, ServiceInfo factoryInfo = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - Of(DryIoc.FactoryMethod.Of(factoryMethodOrMember, factoryInfo), parameters, propertiesAndFields); - - /// Creates factory specification with implementation type, conditionally depending on request. - public static Made Of(Func getImplType, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - Of(r => DryIoc.FactoryMethod.Of(getImplType(r).SingleConstructor()), - parameters, propertiesAndFields, isConditionalImlementation: true); - - /// Creates factory specification with method or member selector based on request. - /// Where is method, or constructor, or member selector. - public static Made Of(Func getMethodOrMember, ServiceInfo factoryInfo = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - new Made(r => DryIoc.FactoryMethod.Of(getMethodOrMember(r), factoryInfo), - parameters, propertiesAndFields, isImplMemberDependsOnRequest: true); - - /// Creates factory specification with method or member selector based on request. - /// Where Method, or constructor, or member selector. - public static Made Of(Func getMethodOrMember, Func factoryInfo, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null) => - new Made(r => DryIoc.FactoryMethod.Of(getMethodOrMember(r), factoryInfo(r)), - parameters, propertiesAndFields, isImplMemberDependsOnRequest: true); - - /// Defines how to select constructor from implementation type. - /// Where is delegate taking implementation type as input - /// and returning selected constructor info. - public static Made Of(Func getConstructor, ParameterSelector parameters = null, - PropertiesAndFieldsSelector propertiesAndFields = null) => - Of(r => DryIoc.FactoryMethod.Of(getConstructor(r.ImplementationType).ThrowIfNull(Error.GotNullConstructorFromFactoryMethod, r)), - parameters, propertiesAndFields); - - /// Defines factory method using expression of constructor call (with properties), or static method call. - /// Type with constructor or static method. - /// Expression tree with call to constructor with properties: - /// new Car(Arg.Of()) { Color = Arg.Of("CarColor") }]]> - /// or static method call Car.Create(Arg.Of())]]> - /// (optional) Primitive custom values for dependencies. - /// New Made specification. - public static TypedMade Of( - System.Linq.Expressions.Expression> serviceReturningExpr, - params Func[] argValues) => - FromExpression(member => _ => DryIoc.FactoryMethod.Of(member), serviceReturningExpr, argValues); - - /// Defines creation info from factory method call Expression without using strings. - /// You can supply any/default arguments to factory method, they won't be used, it is only to find the . - /// Factory type. Factory product type. - /// Returns or resolves factory instance. - /// Method, property or field expression returning service. - /// (optional) Primitive custom values for dependencies. - /// New Made specification. - public static TypedMade Of( - Func> getFactoryInfo, - System.Linq.Expressions.Expression> serviceReturningExpr, - params Func[] argValues) - where TFactory : class - { - getFactoryInfo.ThrowIfNull(); - return FromExpression(member => request => DryIoc.FactoryMethod.Of(member, getFactoryInfo(request)), - serviceReturningExpr, argValues); - } - - /// Composes Made.Of expression with known factory instance and expression to get a service - public static TypedMade Of( - TFactory factoryInstance, - System.Linq.Expressions.Expression> serviceReturningExpr, - params Func[] argValues) - where TFactory : class - { - factoryInstance.ThrowIfNull(); - return FromExpression( - member => request => DryIoc.FactoryMethod.Of(member, factoryInstance), - serviceReturningExpr, argValues); - } - - private static TypedMade FromExpression( - Func getFactoryMethodSelector, - System.Linq.Expressions.LambdaExpression serviceReturningExpr, params Func[] argValues) - { - var callExpr = serviceReturningExpr.ThrowIfNull().Body; - if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.Convert) // proceed without Cast expression. - return FromExpression(getFactoryMethodSelector, - System.Linq.Expressions.Expression.Lambda(((System.Linq.Expressions.UnaryExpression)callExpr).Operand, - Empty()), - argValues); - - MemberInfo ctorOrMethodOrMember; - IList argExprs = null; - IList memberBindingExprs = null; - ParameterInfo[] parameters = null; - - if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.New || - callExpr.NodeType == System.Linq.Expressions.ExpressionType.MemberInit) - { - var newExpr = callExpr as System.Linq.Expressions.NewExpression ?? ((System.Linq.Expressions.MemberInitExpression)callExpr).NewExpression; - ctorOrMethodOrMember = newExpr.Constructor; - parameters = newExpr.Constructor.GetParameters(); - argExprs = newExpr.Arguments; - if (callExpr is System.Linq.Expressions.MemberInitExpression) - memberBindingExprs = ((System.Linq.Expressions.MemberInitExpression)callExpr).Bindings; - } - else if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.Call) - { - var methodCallExpr = (System.Linq.Expressions.MethodCallExpression)callExpr; - ctorOrMethodOrMember = methodCallExpr.Method; - parameters = methodCallExpr.Method.GetParameters(); - argExprs = methodCallExpr.Arguments; - } - else if (callExpr.NodeType == ExprType.Invoke) - { - var invokeExpr = (System.Linq.Expressions.InvocationExpression)callExpr; - var invokedDelegateExpr = invokeExpr.Expression; - var invokeMethod = invokedDelegateExpr.Type.GetTypeInfo().GetDeclaredMethod(nameof(Action.Invoke)); - ctorOrMethodOrMember = invokeMethod; - parameters = invokeMethod.GetParameters(); - argExprs = invokeExpr.Arguments; - } - - else if (callExpr.NodeType == System.Linq.Expressions.ExpressionType.MemberAccess) - { - var member = ((System.Linq.Expressions.MemberExpression)callExpr).Member; - Throw.If(!(member is PropertyInfo) && !(member is FieldInfo), - Error.UnexpectedFactoryMemberExpressionInMadeOf, member, serviceReturningExpr); - ctorOrMethodOrMember = member; - } - else return Throw.For>(Error.NotSupportedMadeOfExpression, callExpr); - - var hasCustomValue = false; - - var parameterSelector = parameters.IsNullOrEmpty() ? null - : ComposeParameterSelectorFromArgs(ref hasCustomValue, - serviceReturningExpr, parameters, argExprs, argValues); - - var propertiesAndFieldsSelector = - memberBindingExprs == null || memberBindingExprs.Count == 0 ? null - : ComposePropertiesAndFieldsSelector(ref hasCustomValue, - serviceReturningExpr, memberBindingExprs, argValues); - - return new TypedMade(getFactoryMethodSelector(ctorOrMethodOrMember), - parameterSelector, propertiesAndFieldsSelector, hasCustomValue); - } - - /// Typed version of specified with statically typed expression tree. - public sealed class TypedMade : Made - { - internal TypedMade(FactoryMethodSelector factoryMethod = null, - ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null, - bool hasCustomValue = false) - : base(factoryMethod, parameters, propertiesAndFields, typeof(TService), hasCustomValue) - { } - } - -#region Implementation - - internal Made( - FactoryMethodSelector factoryMethod = null, ParameterSelector parameters = null, PropertiesAndFieldsSelector propertiesAndFields = null, - Type factoryMethodKnownResultType = null, bool hasCustomValue = false, bool isConditionalImlementation = false, - bool isImplMemberDependsOnRequest = false) - { - FactoryMethod = factoryMethod; - Parameters = parameters; - PropertiesAndFields = propertiesAndFields; - FactoryMethodKnownResultType = factoryMethodKnownResultType; - - var details = default(MadeDetails); - if (hasCustomValue) - details |= MadeDetails.HasCustomDependencyValue; - if (isConditionalImlementation) - details |= MadeDetails.ImplTypeDependsOnRequest; - if (isImplMemberDependsOnRequest) - details |= MadeDetails.ImplMemberDependsOnRequest; - _details = details; - } - - internal Made(FactoryMethod factoryMethod, Type factoryReturnType) - { - FactoryMethod = factoryMethod.ToFunc; - FactoryMethodKnownResultType = factoryReturnType; - } - - private static ParameterSelector ComposeParameterSelectorFromArgs(ref bool hasCustomValue, - System.Linq.Expressions.Expression wholeServiceExpr, ParameterInfo[] paramInfos, - IList argExprs, - params Func[] argValues) - { - var paramSelector = DryIoc.Parameters.Of; - for (var i = 0; i < argExprs.Count; i++) - { - var paramInfo = paramInfos[i]; - var methodCallExpr = argExprs[i] as System.Linq.Expressions.MethodCallExpression; - if (methodCallExpr != null) - { - Throw.If(methodCallExpr.Method.DeclaringType != typeof(Arg), - Error.UnexpectedExpressionInsteadOfArgMethodInMadeOf, methodCallExpr, wholeServiceExpr); - - if (methodCallExpr.Method.Name == Arg.ArgIndexMethodName) - { - var getArgValue = GetArgCustomValueProvider(wholeServiceExpr, methodCallExpr, argValues); - paramSelector = paramSelector.Details((r, p) => p.Equals(paramInfo) ? ServiceDetails.Of(getArgValue(r)) : null); - hasCustomValue = true; - } - else // handle service details - { - var defaultValue = paramInfo.IsOptional ? paramInfo.DefaultValue : null; - var argDetails = GetArgServiceDetails(wholeServiceExpr, - methodCallExpr, paramInfo.ParameterType, IfUnresolved.Throw, defaultValue); - paramSelector = paramSelector.Details((r, p) => p.Equals(paramInfo) ? argDetails : null); - } - } - else - { - var customValue = GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[i]); - paramSelector = paramSelector.Details((r, p) => p.Equals(paramInfo) ? ServiceDetails.Of(customValue) : null); - } - } - return paramSelector; - } - - private static PropertiesAndFieldsSelector ComposePropertiesAndFieldsSelector(ref bool hasCustomValue, - System.Linq.Expressions.Expression wholeServiceExpr, IList memberBindings, - params Func[] argValues) - { - var propertiesAndFields = DryIoc.PropertiesAndFields.Of; - for (var i = 0; i < memberBindings.Count; i++) - { - var memberAssignment = (memberBindings[i] as System.Linq.Expressions.MemberAssignment).ThrowIfNull(); - var member = memberAssignment.Member; - - var methodCallExpr = memberAssignment.Expression as System.Linq.Expressions.MethodCallExpression; - if (methodCallExpr == null) // not an Arg.Of: e.g. constant or variable - { - var customValue = GetArgExpressionValueOrThrow(wholeServiceExpr, memberAssignment.Expression); - propertiesAndFields = propertiesAndFields.OverrideWith(r => - PropertyOrFieldServiceInfo.Of(member).WithDetails(ServiceDetails.Of(customValue)).One()); - } - else - { - Throw.If(methodCallExpr.Method.DeclaringType != typeof(Arg), - Error.UnexpectedExpressionInsteadOfArgMethodInMadeOf, methodCallExpr, wholeServiceExpr); - - if (methodCallExpr.Method.Name == Arg.ArgIndexMethodName) // handle custom value - { - var getArgValue = GetArgCustomValueProvider(wholeServiceExpr, methodCallExpr, argValues); - propertiesAndFields = propertiesAndFields.OverrideWith(req => - PropertyOrFieldServiceInfo.Of(member).WithDetails(ServiceDetails.Of(getArgValue(req))).One()); - hasCustomValue = true; - } - else - { - var memberType = member.GetReturnTypeOrDefault(); - var argServiceDetails = GetArgServiceDetails(wholeServiceExpr, methodCallExpr, memberType, IfUnresolved.ReturnDefault, null); - propertiesAndFields = propertiesAndFields.OverrideWith(r => - PropertyOrFieldServiceInfo.Of(member).WithDetails(argServiceDetails).One()); - } - } - } - return propertiesAndFields; - } - - private static Func GetArgCustomValueProvider( - System.Linq.Expressions.Expression wholeServiceExpr, - System.Linq.Expressions.MethodCallExpression methodCallExpr, Func[] argValues) - { - Throw.If(argValues.IsNullOrEmpty(), Error.ArgValueIndexIsProvidedButNoArgValues, wholeServiceExpr); - - var argIndex = (int)GetArgExpressionValueOrThrow(wholeServiceExpr, methodCallExpr.Arguments[0]); - if (argIndex < 0 || argIndex >= argValues.Length) - Throw.It(Error.ArgValueIndexIsOutOfProvidedArgValues, argIndex, argValues, wholeServiceExpr); - - return argValues[argIndex]; - } - - private static ServiceDetails GetArgServiceDetails( - System.Linq.Expressions.Expression wholeServiceExpr, - System.Linq.Expressions.MethodCallExpression methodCallExpr, - Type dependencyType, IfUnresolved defaultIfUnresolved, object defaultValue) - { - var requiredServiceType = methodCallExpr.Method.GetGenericArguments().Last(); - if (requiredServiceType == dependencyType) - requiredServiceType = null; - - var serviceKey = default(object); - var metadataKey = default(string); - var metadata = default(object); - var ifUnresolved = defaultIfUnresolved; - - var hasPrevArg = false; - - var argExprs = methodCallExpr.Arguments; - if (argExprs.Count == 2 && - argExprs[0].Type == typeof(string) && - argExprs[1].Type != typeof(IfUnresolved)) // matches the Of overload for metadata - { - metadataKey = (string)GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[0]); - metadata = GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[1]); - } - else - { - for (var a = 0; a < argExprs.Count; a++) - { - var argValue = GetArgExpressionValueOrThrow(wholeServiceExpr, argExprs[a]); - if (argValue != null) - { - if (argValue is IfUnresolved) - { - ifUnresolved = (IfUnresolved)argValue; - if (hasPrevArg) // the only possible argument is default value. - { - defaultValue = serviceKey; - serviceKey = null; - } - } - else - { - serviceKey = argValue; - hasPrevArg = true; - } - } - } - } - - return ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata); - } - - private static object GetArgExpressionValueOrThrow( - System.Linq.Expressions.Expression wholeServiceExpr, - System.Linq.Expressions.Expression argExpr) - { - var valueExpr = argExpr as System.Linq.Expressions.ConstantExpression; - if (valueExpr != null) - return valueExpr.Value; - - var convert = argExpr as System.Linq.Expressions.UnaryExpression; // e.g. (object)SomeEnum.Value - if (convert != null && convert.NodeType == ExprType.Convert) - return GetArgExpressionValueOrThrow(wholeServiceExpr, - convert.Operand as System.Linq.Expressions.ConstantExpression); - - var member = argExpr as System.Linq.Expressions.MemberExpression; - if (member != null) - { - var memberOwner = member.Expression as System.Linq.Expressions.ConstantExpression; - if (memberOwner != null && memberOwner.Type.IsClosureType() && member.Member is FieldInfo) - return ((FieldInfo)member.Member).GetValue(memberOwner.Value); - } - - var newArrExpr = argExpr as System.Linq.Expressions.NewArrayExpression; - if (newArrExpr != null) - { - var itemExprs = newArrExpr.Expressions; - var items = new object[itemExprs.Count]; - for (var i = 0; i < itemExprs.Count; i++) - items[i] = GetArgExpressionValueOrThrow(wholeServiceExpr, itemExprs[i]); - - return Converter.ConvertMany(items, newArrExpr.Type.GetElementType()); - } - - return Throw.For(Error.UnexpectedExpressionInsteadOfConstantInMadeOf, - argExpr, wholeServiceExpr); - } - -#endregion - } - - /// Class for defining parameters/properties/fields service info in expressions. - /// Arg methods are NOT actually called, they just used to reflect service info from call expression. - public static class Arg - { - /// Specifies required service type of parameter or member. If required type is the same as parameter/member type, - /// the method is just a placeholder to help detect constructor or factory method, and does not have additional meaning. - public static TRequired Of() => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of() => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies if-unresolved policy. - public static TRequired Of(IfUnresolved ifUnresolved) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(IfUnresolved ifUnresolved) => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies service key. - public static TRequired Of(object serviceKey) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(object serviceKey) => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies service key. - public static TRequired Of(string metadataKey, object metadata) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(string metadataKey, object metadata) => default(TService); - - /// Specifies required service type of parameter or member. Plus specifies if-unresolved policy. Plus specifies service key. - public static TRequired Of(IfUnresolved ifUnresolved, object serviceKey) => default(TRequired); - - /// Specifies both service and required service types. - public static TService Of(IfUnresolved ifUnresolved, object serviceKey) => default(TService); - - /// Specifies required service type, default value and . - public static TRequired Of(TRequired defaultValue, IfUnresolved ifUnresolved) => default(TRequired); - - /// Specifies required service type, default value and . - public static TRequired Of(TRequired defaultValue, IfUnresolved ifUnresolved, object serviceKey) => default(TRequired); - - /// Specifies argument index starting from 0 to use corresponding custom value factory, - /// similar to String.Format "{0}, {1}, etc". - public static T Index(int argIndex) => default(T); - - /// Name is close to method itself to not forget when renaming the method. - public static string ArgIndexMethodName = "Index"; - } - - /// Contains extension methods to simplify general use cases. - public static class Registrator - { - /// The base method for registering servce with its implementation factory. Allows to specify all possible options. - public static void Register(this IRegistrator registrator, Type serviceType, Factory factory, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, false); - - /// Registers service with corresponding . - public static void Register(this IRegistrator registrator, Type serviceType, Type implementationType, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new ReflectionFactory(implementationType, reuse, made, setup), - serviceType, serviceKey, ifAlreadyRegistered, false); - - /// Registers service of . - /// ServiceType may be the same as . - public static void Register(this IRegistrator registrator, Type serviceAndMayBeImplementationType, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new ReflectionFactory(serviceAndMayBeImplementationType, reuse, made, setup), - serviceAndMayBeImplementationType, serviceKey, ifAlreadyRegistered, false); - - /// Registers service of type - /// implemented by type. - public static void Register(this IRegistrator registrator, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) - where TImplementation : TService => - registrator.Register(new ReflectionFactory(typeof(TImplementation), reuse, made, setup), - typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers implementation type with itself as service type. - public static void Register(this IRegistrator registrator, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(reuse, made, setup, ifAlreadyRegistered, serviceKey); - - /// Registers service type returned by Made expression. - public static void Register(this IRegistrator registrator, - Made.TypedMade made, IReuse reuse = null, Setup setup = null, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) where TMadeResult : TService => - registrator.Register(new ReflectionFactory(default(Type), reuse, made, setup), - typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers service returned by Made expression. - public static void Register(this IRegistrator registrator, - Made.TypedMade made, IReuse reuse = null, Setup setup = null, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.Register(made, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// - /// Registers the instance creating a "normal" DryIoc registration so you can check it via `IsRegestered`, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstance(this IRegistrator registrator, bool isChecked, Type serviceType, object instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) - { - registrator.Register(new RegisteredInstanceFactory(instance, DryIoc.Reuse.Singleton, setup), - serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: false); - - // done after registration to pass all the registration validation checks - if (instance is IDisposable && (setup == null || (!setup.PreventDisposal && !setup.WeaklyReferenced))) - (registrator as IResolverContext)?.SingletonScope.TrackDisposable(instance); - } - - /// - /// Registers the instance creating a "normal" DryIoc registration so you can check it via `IsRegestered`, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstance(this IRegistrator registrator, Type serviceType, object instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) => - registrator.RegisterInstance(false, serviceType, instance, ifAlreadyRegistered, setup, serviceKey); - - /// - /// Registers the instance creating a "normal" DryIoc registration so you can check it via `IsRegestered`, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstance(this IRegistrator registrator, T instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) => - registrator.RegisterInstance(true, typeof(T), instance, ifAlreadyRegistered, setup, serviceKey); - - /// - /// Registers the instance with possible multiple service types creating a "normal" DryIoc registration - /// so you can check it via `IsRegestered` for each service type, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstanceMany(this IRegistrator registrator, Type implType, object instance, - bool nonPublicServiceTypes = false, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) - { - instance.ThrowIfNull(); - if (implType != null) - instance.ThrowIfNotInstanceOf(implType); - else - implType = instance.GetType(); - - var serviceTypes = implType.GetImplementedServiceTypes(nonPublicServiceTypes); - - if (serviceTypes.Length == 0) - Throw.It(Error.NoServicesWereRegisteredByRegisterMany, implType.One()); - - var factory = new RegisteredInstanceFactory(instance, DryIoc.Reuse.Singleton, setup); - foreach (var serviceType in serviceTypes) - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - if (instance is IDisposable && (setup == null || (!setup.PreventDisposal && !setup.WeaklyReferenced))) - (registrator as IResolverContext)?.SingletonScope.TrackDisposable(instance); - } - - /// - /// Registers the instance with possible multiple service types creating a "normal" DryIoc registration - /// so you can check it via `IsRegestered` for each service type, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstanceMany(this IRegistrator registrator, T instance, - bool nonPublicServiceTypes = false, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) => - registrator.RegisterInstanceMany(instance.GetType(), instance, - nonPublicServiceTypes, ifAlreadyRegistered, setup, serviceKey); - - /// - /// Registers the instance with possible multiple service types creating a "normal" DryIoc registration - /// so you can check it via `IsRegestered` for each service type, - /// apply wrappers and decorators, etc. - /// Additionally, if instance is `IDisposable`, then it tracks it in a singleton scope. - /// NOTE: Look at the `Use` method to put instance directly into current or singleton scope, - /// though without ability to use decorators and wrappers on it. - /// - public static void RegisterInstanceMany(this IRegistrator registrator, Type[] serviceTypes, object instance, - IfAlreadyRegistered? ifAlreadyRegistered = null, Setup setup = null, object serviceKey = null) - { - var instanceType = instance.GetType(); - if (serviceTypes.IsNullOrEmpty()) - Throw.It(Error.NoServicesWereRegisteredByRegisterMany, instance); - - var factory = new RegisteredInstanceFactory(instance, DryIoc.Reuse.Singleton, setup); - - foreach (var serviceType in serviceTypes) - { - serviceType.ThrowIfNotImplementedBy(instanceType); - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - } - - if (instance is IDisposable && (setup == null || (!setup.PreventDisposal && !setup.WeaklyReferenced))) - (registrator as IResolverContext)?.SingletonScope.TrackDisposable(instance); - } - - /// List of types excluded by default from RegisterMany convention. - public static readonly string[] ExcludedGeneralPurposeServiceTypes = - { - "System.Object", - "System.IDisposable", - "System.ValueType", - "System.ICloneable", - "System.IEquatable", - "System.IComparable", - "System.Runtime.Serialization.ISerializable", - "System.Collections.IStructuralEquatable", - "System.Collections.IEnumerable", - "System.Collections.IList", - "System.Collections.ICollection", - }; - - /// Checks that type is not in the list of . - public static bool IsExcludedGeneralPurposeServiceType(this Type type) => - ExcludedGeneralPurposeServiceTypes.IndexOf((type.Namespace + "." + type.Name).Split('`')[0]) != -1; - - /// Checks that type can be used a service type. - public static bool IsServiceType(this Type type) => - !type.IsPrimitive() && !type.IsCompilerGenerated() && !type.IsExcludedGeneralPurposeServiceType(); - - /// Checks if type can be used as implementation type for reflection factory, - /// and therefore registered to container. Usually used to discover implementation types from assembly. - public static bool IsImplementationType(this Type type) => - type.IsClass() && !type.IsAbstract() && !type.IsCompilerGenerated(); - - /// Returns only those types that could be used as service types of . - /// It means that for open-generic its service type should supply all type arguments. - public static Type[] GetImplementedServiceTypes(this Type type, bool nonPublicServiceTypes = false) - { - var implementedTypes = type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType); - - var serviceTypes = nonPublicServiceTypes - ? implementedTypes.Match(t => t.IsServiceType()) - : implementedTypes.Match(t => t.IsPublicOrNestedPublic() && t.IsServiceType()); - - if (type.IsGenericDefinition()) - serviceTypes = serviceTypes.Match(type.GetGenericParamsAndArgs(), - (paramsAndArgs, x) => x.ContainsAllGenericTypeParameters(paramsAndArgs), - (_, x) => x.GetGenericDefinitionOrNull()); - - return serviceTypes; - } - - /// The same `GetImplementedServiceTypes` but instead of collecting the service types just check the is implemented - public static bool IsImplementingServiceType(this Type type, Type serviceType) - { - if (serviceType == type || serviceType == typeof(object)) - return true; - - var implTypeInfo = type.GetTypeInfo(); - if (!implTypeInfo.IsGenericTypeDefinition) - { - if (serviceType.IsInterface()) - { - foreach (var iface in implTypeInfo.ImplementedInterfaces) - if (iface == serviceType) - return true; - } - else - { - var baseType = implTypeInfo.BaseType; - for (; baseType != null && baseType != typeof(object); baseType = baseType.GetTypeInfo().BaseType) - if (serviceType == baseType) - return true; - } - } - else if (serviceType.IsGenericDefinition()) - { - var implTypeParams = implTypeInfo.GenericTypeParameters; - if (serviceType.IsInterface()) - { - foreach (var iface in implTypeInfo.ImplementedInterfaces) - if (iface.GetGenericDefinitionOrNull() == serviceType && - iface.ContainsAllGenericTypeParameters(implTypeParams)) - return true; - } - else - { - var baseType = implTypeInfo.BaseType; - for (; baseType != null && baseType != typeof(object); baseType = baseType.GetTypeInfo().BaseType) - if (baseType.GetGenericDefinitionOrNull() == serviceType && - baseType.ContainsAllGenericTypeParameters(implTypeParams)) - return true; - } - } - - return false; - } - - /// Returns the sensible services automatically discovered for RegisterMany implementation type. - /// Excludes the collection wrapper interfaces. The may be concrete, abstract or - /// generic definition. - public static Type[] GetRegisterManyImplementedServiceTypes(this Type type, bool nonPublicServiceTypes = false) => - GetImplementedServiceTypes(type, nonPublicServiceTypes) - .Match(t => !t.IsGenericDefinition() || WrappersSupport.SupportedCollectionTypes.IndexOfReference(t) == -1); - - /// Returns the types suitable to be an implementation types for : - /// actually a non abstract and not compiler generated classes. - public static IEnumerable GetImplementationTypes(this Assembly assembly) => - Portable.GetAssemblyTypes(assembly).Where(IsImplementationType); - - /// Returns the types suitable to be an implementation types for : - /// actually a non abstract and not compiler generated classes. - public static IEnumerable GetImplementationTypes(this Assembly assembly, Func condition) => - Portable.GetAssemblyTypes(assembly).Where(t => condition(t) && t.IsImplementationType()); - - /// Sugar, so you can say (Registrator.Interfaces)]]> - public static Func Interfaces = ReflectionTools.IsInterface; - - /// Checks if implements a service type, - /// along the checking if is a valid implementation type. - public static bool ImplementsServiceType(this Type type, Type serviceType) => - type.IsImplementationType() && type.IsImplementingServiceType(serviceType); - - /// Checks if implements a service type, - /// along the checking if and service type - /// are valid implementation and service types. - public static bool ImplementsServiceType(this Type type) => - type.ImplementsServiceType(typeof(TService)); - - /// Wraps the implementation type in factory. - public static Factory ToFactory(this Type implType) => new ReflectionFactory(implType); - - /// Wraps the implementation type in factory plus allow to provide factory parameters. - public static Factory ToFactory(this Type implType, IReuse reuse, Made made = null, Setup setup = null) => - new ReflectionFactory(implType, reuse, made, setup); - - /// A primary (basic) method for batch registering of implementations with possibly many service types. - /// The default factory is the with default reuse. - public static void RegisterMany(this IRegistrator registrator, - IEnumerable implTypes, Func getServiceTypes, - Func getImplFactory = null, Func getServiceKey = null, - IfAlreadyRegistered? ifAlreadyRegistered = null) - { - getImplFactory = getImplFactory ?? ToFactory; - - bool isSomeoneRegistered = false; - bool anyImplTypes = false; - foreach (var implType in implTypes) - { - anyImplTypes = true; - var serviceTypes = getServiceTypes(implType); - if (!serviceTypes.IsNullOrEmpty()) - { - var factory = getImplFactory(implType); - for (var i = 0; i < serviceTypes.Length; i++) - { - var t = serviceTypes[i]; - registrator.Register(t, factory, ifAlreadyRegistered, getServiceKey?.Invoke(implType, t)); - isSomeoneRegistered = true; - } - } - } - - if (anyImplTypes && !isSomeoneRegistered) - Throw.It(Error.NoServicesWereRegisteredByRegisterMany, implTypes); - } - - /// Batch registers implementation with possibly many service types. - public static void RegisterMany(this IRegistrator registrator, - Type[] serviceTypes, Type implType, - IReuse reuse = null, Made made = null, Setup setup = null, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.RegisterMany(new[] { implType }, serviceTypes.ToFunc, - t => t.ToFactory(reuse, made, setup), (_, __) => serviceKey, ifAlreadyRegistered); - - /// Batch registers assemblies of implementation types with possibly many service types. - /// The default factory is the with default reuse. - public static void RegisterMany(this IRegistrator registrator, - IEnumerable implTypeAssemblies, Func getServiceTypes, - Func getImplFactory = null, Func getServiceKey = null, - IfAlreadyRegistered? ifAlreadyRegistered = null) => - registrator.RegisterMany(implTypeAssemblies.ThrowIfNull().SelectMany(GetImplementationTypes), - getServiceTypes, getImplFactory, getServiceKey, ifAlreadyRegistered); - - /// Registers many implementations with their auto-figured service types. - public static void RegisterMany(this IRegistrator registrator, - IEnumerable implTypeAssemblies, Func serviceTypeCondition, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - bool nonPublicServiceTypes = false, object serviceKey = null) => - registrator.RegisterMany(implTypeAssemblies.ThrowIfNull().SelectMany(GetImplementationTypes), - reuse, made, setup, ifAlreadyRegistered, serviceTypeCondition, nonPublicServiceTypes, serviceKey); - - /// Registers many implementations with auto-figured service types. - public static void RegisterMany(this IRegistrator registrator, IEnumerable implTypes, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - Func serviceTypeCondition = null, bool nonPublicServiceTypes = false, - object serviceKey = null) => - registrator.RegisterMany(implTypes, - t => t.GetRegisterManyImplementedServiceTypes(nonPublicServiceTypes).Match(serviceTypeCondition ?? Fun.Always), - reuse == null && made == null && setup == null ? default(Func) : t => t.ToFactory(reuse, made, setup), - serviceKey == null ? default(Func) : (i, s) => serviceKey, - ifAlreadyRegistered); - - /// Registers single registration for all implemented public interfaces and base classes. - public static void RegisterMany(this IRegistrator registrator, - IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - Func serviceTypeCondition = null, bool nonPublicServiceTypes = false, - object serviceKey = null) => - registrator.RegisterMany(typeof(TImplementation).One(), - reuse, made, setup, ifAlreadyRegistered, serviceTypeCondition, nonPublicServiceTypes, serviceKey); - - /// Registers single registration for all implemented public interfaces and base classes. - public static void RegisterMany(this IRegistrator registrator, - Made.TypedMade made, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - Func serviceTypeCondition = null, bool nonPublicServiceTypes = false, - object serviceKey = null) => - registrator.RegisterMany(reuse, made.ThrowIfNull(), setup, - ifAlreadyRegistered, serviceTypeCondition, nonPublicServiceTypes, serviceKey); - - /// Registers a factory delegate for creating an instance of . - /// Delegate can use resolver context parameter to resolve any required dependencies, e.g.: - /// (r => new Car(r.Resolve()))]]> - /// The alternative to this method please consider using instead: - /// (Made.Of(() => new Car(Arg.Of())))]]>. - /// - public static void RegisterDelegate(this IRegistrator registrator, - Func factoryDelegate, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new DelegateFactory(factoryDelegate.ToFactoryDelegate, reuse, setup), - typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers delegate to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Type serviceType, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc>(r, serviceType, - dep1 => factory(dep1).ThrowIfNotInstanceOf(serviceType, Error.RegisteredDelegateResultIsNotOfServiceType), - reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - /// Registers delegate with explicit arguments to be injected by container avoiding the ServiceLocator anti-pattern - public static void RegisterDelegate( - this IRegistrator r, Func factory, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - RegisterDelegateFunc(r, typeof(TService), factory, reuse, setup, ifAlreadyRegistered, serviceKey); - - private const string InvokeMethodName = "Invoke"; - private static void RegisterDelegateFunc(IRegistrator r, Type serviceType, - TFunc factory, IReuse reuse, Setup setup, IfAlreadyRegistered? ifAlreadyRegistered, object serviceKey) - { - var invokeMethod = typeof(TFunc).GetTypeInfo().GetDeclaredMethod(InvokeMethodName); - var made = new Made(new FactoryMethod(invokeMethod, Constant(factory)), serviceType); - r.Register(new ReflectionFactory(serviceType, reuse, made, setup), - serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - } - - /// Minimizes the number of allocations when converting from Func to named delegate - public static object ToFactoryDelegate(this Func f, IResolverContext r) => f(r); - - /// Lifts the result to the factory delegate without allocations on capturing value in lambda closure - public static object ToFactoryDelegate(this object result, IResolverContext _) => result; - - /// Registers a factory delegate for creating an instance of . - /// Delegate can use resolver context parameter to resolve any required dependencies, e.g.: - /// (r => new Car(r.Resolve()))]]> - /// IMPORTANT: The method should be used as the last resort only! Though powerful it is a black-box for container, - /// which prevents diagnostics, plus it is easy to get memory leaks (due variables captured in delegate closure), - /// and impossible to use in compile-time scenarios. - /// Consider using instead: - /// (Made.Of(() => new Car(Arg.Of())))]]> - /// - public static void RegisterDelegate(this IRegistrator registrator, - Type serviceType, Func factoryDelegate, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) - { - if (serviceType.IsOpenGeneric()) - Throw.It(Error.RegisteringOpenGenericRequiresFactoryProvider, serviceType); - - FactoryDelegate checkedDelegate = r => factoryDelegate(r) - .ThrowIfNotInstanceOf(serviceType, Error.RegisteredDelegateResultIsNotOfServiceType); - - var factory = new DelegateFactory(checkedDelegate, reuse, setup); - - registrator.Register(factory, serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: false); - } - - /// A special performant version mostly for integration with other libraries, - /// that already check compatiblity between delegate result and the service type - public static void RegisterDelegate(this IRegistrator registrator, - bool isChecked, Type serviceType, Func factoryDelegate, - IReuse reuse = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null, - object serviceKey = null) => - registrator.Register(new DelegateFactory(factoryDelegate.ToFactoryDelegate, reuse, setup), - serviceType, serviceKey, ifAlreadyRegistered, isStaticallyChecked: true); - - /// Registers decorator function that gets decorated value as input and returns decorator. - /// Note: Delegate decorator will use of decoratee service. - public static void RegisterDelegateDecorator(this IRegistrator registrator, - Func> getDecorator, Func condition = null) - { - getDecorator.ThrowIfNull(); - - // unique key to bind decorator factory and decorator registrations - var factoryKey = new object(); - - registrator.RegisterDelegate( - _ => new DecoratorDelegateFactory(getDecorator), - serviceKey: factoryKey); - - registrator.Register(Made.Of( - _ => ServiceInfo.Of>(serviceKey: factoryKey), - f => f.Decorate(Arg.Of(), Arg.Of())), - setup: Setup.DecoratorWith(condition, useDecorateeReuse: true)); - } - - internal sealed class DecoratorDelegateFactory - { - private readonly Func> _getDecorator; - public DecoratorDelegateFactory(Func> getDecorator) { _getDecorator = getDecorator; } - public TDecoratee Decorate(TDecoratee decoratee, IResolverContext r) => _getDecorator(r).Invoke(decoratee); - } - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, TService instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(typeof(TService), instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IRegistrator r, TService instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(typeof(TService), instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IContainer c, TService instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - c.UseInstance(typeof(TService), instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, Type serviceType, object instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IRegistrator r, Type serviceType, object instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IContainer c, Type serviceType, object instance, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - c.UseInstance(serviceType, instance, IfAlreadyRegistered.Replace, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, TService instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(typeof(TService), instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IResolverContext r, Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// Will become OBSOLETE! in the next major version: - /// Please use `RegisterInstance` or `Use` method instead. - public static void UseInstance(this IRegistrator r, Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - r.UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// - /// Will become OBSOLETE in the next major version! - /// Please use `RegisterInstance` or `Use` method instead. - /// - public static void UseInstance(this IContainer c, Type serviceType, object instance, IfAlreadyRegistered ifAlreadyRegistered, - bool preventDisposal = false, bool weaklyReferenced = false, object serviceKey = null) => - c.UseInstance(serviceType, instance, ifAlreadyRegistered, preventDisposal, weaklyReferenced, serviceKey); - - /// Adding the factory directly to scope for resolution - public static void Use(this IResolverContext r, Func factory) => - r.Use(typeof(TService), factory.ToFactoryDelegate); - - /// Adding the instance directly to the scope for resolution - public static void Use(this IResolverContext r, Type serviceType, object instance) => - r.Use(serviceType, instance.ToFactoryDelegate); - - /// Adding the instance directly to the scope for resolution - public static void Use(this IResolverContext r, TService instance) => - r.Use(typeof(TService), instance.ToFactoryDelegate); - - /// Adding the factory directly to the scope for resolution - public static void Use(this IRegistrator r, Func factory) => - r.Use(typeof(TService), factory.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IRegistrator r, Type serviceType, object instance) => - r.Use(serviceType, instance.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IRegistrator r, TService instance) => - r.Use(typeof(TService), instance.ToFactoryDelegate); - - /// Adding the factory directly to scope for resolution - public static void Use(this IContainer c, Func factory) => - ((IResolverContext)c).Use(typeof(TService), factory.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IContainer c, Type serviceType, object instance) => - ((IResolverContext)c).Use(serviceType, instance.ToFactoryDelegate); - - /// Adding the instance directly to scope for resolution - public static void Use(this IContainer c, TService instance) => - ((IResolverContext)c).Use(typeof(TService), instance.ToFactoryDelegate); - - /// - /// Registers initializing action that will be called after service is resolved - /// just before returning it to the caller. You can register multiple initializers for single service. - /// Or you can register initializer for type to be applied - /// for all services and use to specify the target services. - /// - public static void RegisterInitializer(this IRegistrator registrator, - Action initialize, Func condition = null) - { - initialize.ThrowIfNull(); - registrator.Register( - made: Made.Of(r => _initializerMethod.MakeGenericMethod(typeof(TTarget), r.ServiceType), - // specify ResolverContext as parameter to prevent applying initializer for injected resolver too - parameters: Parameters.Of - .Type(r => r.IsSingletonOrDependencyOfSingleton && !r.OpensResolutionScope - ? r.Container.RootOrSelf() : r.Container) - .Type(initialize.ToFunc>)), - setup: Setup.DecoratorWith( - r => r.ServiceType.IsAssignableTo() && (condition == null || condition(r)), - useDecorateeReuse: true, // issue BitBucket #230 - ensures the initialization to happen once on construction - preventDisposal: true)); // issue #215 - ensures that the initialized / decorated object does not added for the disposal twice - } - - private static readonly MethodInfo _initializerMethod = - typeof(Registrator).SingleMethod(nameof(Initializer), includeNonPublic: true); - - internal static TService Initializer( - TService service, IResolverContext resolver, Action initialize) where TService : TTarget - { - initialize(service, resolver); - return service; - } - - /// Registers dispose action for reused target service. - public static void RegisterDisposer(this IRegistrator registrator, - Action dispose, Func condition = null) - { - dispose.ThrowIfNull(); - - var disposerKey = new object(); - - registrator.RegisterDelegate(_ => new Disposer(dispose), - serviceKey: disposerKey, - // tracking instead of parent reuse, so that I can use one disposer for multiple services - setup: Setup.With(trackDisposableTransient: true)); - - var disposerType = typeof(Disposer<>).MakeGenericType(typeof(TService)); - registrator.Register( - made: Made.Of( - r => disposerType.SingleMethod("TrackForDispose").MakeGenericMethod(r.ServiceType), - ServiceInfo.Of(disposerType, serviceKey: disposerKey)), - setup: Setup.DecoratorWith( - r => r.ServiceType.IsAssignableTo() && (condition == null || condition(r)), - useDecorateeReuse: true)); - } - - internal sealed class Disposer : IDisposable - { - private readonly Action _dispose; - private int _state; - private const int TRACKED = 1, DISPOSED = 2; - private T _item; - - public Disposer(Action dispose) - { - _dispose = dispose; - } - - public S TrackForDispose(S item) where S : T - { - if (Interlocked.CompareExchange(ref _state, TRACKED, 0) != 0) - Throw.It(Error.DisposerTrackForDisposeError, _state == TRACKED ? " tracked" : "disposed"); - _item = item; - return item; - } - - public void Dispose() - { - if (Interlocked.CompareExchange(ref _state, DISPOSED, TRACKED) != TRACKED) - return; - var item = _item; - if (item != null) - { - _dispose(item); - _item = default(T); - } - } - } - - /// Returns true if is registered in container OR - /// its open generic definition is registered in container. - /// The additional implementation factory may be specified to narrow the search. - public static bool IsRegistered(this IRegistrator registrator, Type serviceType, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.IsRegistered(serviceType, serviceKey, factoryType, condition); - - /// Returns true if is registered in container OR - /// its open generic definition is registered in container. - /// The additional implementation factory may be specified to narrow the search. - public static bool IsRegistered(this IRegistrator registrator, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.IsRegistered(typeof(TService), serviceKey, factoryType, condition); - - /// Removes specified registration from container. - /// It also tries to remove the cached resolutions for the removed registration, But it may not work depending on context. - /// Check the docs for more info: https://bitbucket.org/dadhi/dryioc/wiki/UnregisterAndResolutionCache - public static void Unregister(this IRegistrator registrator, Type serviceType, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.Unregister(serviceType, serviceKey, factoryType, condition); - - /// Removes specified registration from container. - /// It also tries to remove the cached resolutions for the removed registration, But it may not work depending on context. - /// Check the docs for more info: https://bitbucket.org/dadhi/dryioc/wiki/UnregisterAndResolutionCache - public static void Unregister(this IRegistrator registrator, - object serviceKey = null, FactoryType factoryType = FactoryType.Service, Func condition = null) => - registrator.Unregister(typeof(TService), serviceKey, factoryType, condition); - - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Registrator New service type. - /// Existing registered service type. - /// (optional) (optional) - /// (optional) By default is - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IRegistrator registrator, Type serviceType, Type registeredServiceType, - object serviceKey = null, object registeredServiceKey = null, FactoryType factoryType = FactoryType.Service) - { - var factories = registrator.GetRegisteredFactories(registeredServiceType, registeredServiceKey, factoryType); - - if (factories.IsNullOrEmpty()) - Throw.It(Error.RegisterMappingNotFoundRegisteredService, registeredServiceType, registeredServiceKey); - - if (factories.Length > 1) - Throw.It(Error.RegisterMappingUnableToSelectFromMultipleFactories, serviceType, serviceKey, factories); - - registrator.Register(factories[0], serviceType, serviceKey, IfAlreadyRegistered.Keep, false); - } - - /// Registers new service type with factory for registered service type. - /// Throw if no such registered service type in container. - /// Registrator - /// New service type. - /// Existing registered service type. - /// (optional) (optional) - /// (optional) By default is - /// Does nothing if registration is already exists. - public static void RegisterMapping(this IRegistrator registrator, - object serviceKey = null, object registeredServiceKey = null, FactoryType factoryType = FactoryType.Service) => - registrator.RegisterMapping(typeof(TService), typeof(TRegisteredService), serviceKey, registeredServiceKey); - - /// Register a service without implementation which can be provided later in terms - /// of normal registration with IfAlreadyRegistered.Replace parameter. - /// When the implementation is still not provided when the placeholder service is accessed, - /// then the exception will be thrown. - /// This feature allows you to postpone decision on implementation until it is later known. - /// Internally the empty factory is registered with the setup asResolutionCall set to true. - /// That means, instead of placing service instance into graph expression we put here redirecting call to - /// container Resolve. - public static void RegisterPlaceholder(this IRegistrator registrator, Type serviceType, - IfAlreadyRegistered? ifAlreadyRegistered = null, object serviceKey = null) => - registrator.Register(FactoryPlaceholder.Default, serviceType, serviceKey, ifAlreadyRegistered, true); - } - - /// Extension methods for . - public static class Resolver - { - internal static readonly MethodInfo ResolveFastMethod = - typeof(IResolver).Method(nameof(IResolver.Resolve), typeof(Type), typeof(IfUnresolved)); - - internal static readonly MethodInfo ResolveMethod = - typeof(IResolver).Method(nameof(IResolver.Resolve), typeof(Type), typeof(object), - typeof(IfUnresolved), typeof(Type), typeof(Request), typeof(object[])); - - internal static readonly MethodInfo ResolveManyMethod = - typeof(IResolver).GetTypeInfo().GetDeclaredMethod(nameof(IResolver.ResolveMany)); - - /// Resolves instance of service type from container. Throws exception if unable to resolve. - public static object Resolve(this IResolver resolver, Type serviceType) => - resolver.Resolve(serviceType, IfUnresolved.Throw); - - /// Resolves instance of service type from container. - public static object Resolve(this IResolver resolver, Type serviceType, IfUnresolved ifUnresolved) => - resolver.Resolve(serviceType, ifUnresolved); - - /// Resolves instance of type TService from container. - public static TService Resolve(this IResolver resolver, - IfUnresolved ifUnresolved = IfUnresolved.Throw) => - (TService)resolver.Resolve(typeof(TService), ifUnresolved); - - /// Tries to resolve instance of service type from container. - public static object Resolve(this IResolver resolver, Type serviceType, bool ifUnresolvedReturnDefault) => - resolver.Resolve(serviceType, ifUnresolvedReturnDefault ? IfUnresolved.ReturnDefault : IfUnresolved.Throw); - - /// Tries to resolve instance of TService from container. - public static object Resolve(this IResolver resolver, bool ifUnresolvedReturnDefault) => - resolver.Resolve(typeof(TService), ifUnresolvedReturnDefault); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc. - /// allow you to specify wrapped service type. - /// (); - /// var services = container.Resolve(typeof(IEnumerable), typeof(IService)); - /// ]]> - public static object Resolve(this IResolver resolver, Type serviceType, Type requiredServiceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object[] args = null, object serviceKey = null) => - resolver.Resolve(serviceType, serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc. - /// allow you to specify wrapped service type. - /// (); - /// var services = container.Resolve>(typeof(IService)); - /// ]]> - public static TService Resolve(this IResolver resolver, Type requiredServiceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object[] args = null, object serviceKey = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc. - /// allow you to specify wrapped service type. - /// (); - /// var services = container.Resolve, IService>(); - /// ]]> - public static TService Resolve(this IResolver resolver, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object[] args = null, object serviceKey = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, typeof(TRequiredService), Request.Empty, args); - - /// Returns instance of searching for . - /// In case of being generic wrapper like Func, Lazy, IEnumerable, etc., - /// could specify wrapped service type. - /// Using implicitly support Covariance for generic wrappers even in .Net 3.5. - /// (); - /// var services = container.Resolve(typeof(Lazy), "someKey", requiredServiceType: typeof(IService)); - /// ]]> - public static object Resolve(this IResolver resolver, Type serviceType, object serviceKey, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object[] args = null) => - resolver.Resolve(serviceType, serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns instance of type. - /// The type of the requested service. - /// The requested service instance. - /// Using implicitly support Covariance for generic wrappers even in .Net 3.5. - public static TService Resolve(this IResolver resolver, object serviceKey, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object[] args = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Resolves the service supplying all or some of its dependencies - /// (including nested) with the . The rest of dependencies is injected from - /// container. - public static object Resolve(this IResolver resolver, Type serviceType, object[] args, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object serviceKey = null) => - resolver.Resolve(serviceType, serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Resolves the service supplying all or some of its dependencies - /// (including nested) with the . The rest of dependencies is injected from - /// container. - public static TService Resolve(this IResolver resolver, object[] args, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - object serviceKey = null) => - (TService)resolver.Resolve(typeof(TService), serviceKey, ifUnresolved, requiredServiceType, Request.Empty, args); - - /// Returns all registered services instances including all keyed and default registrations. - /// Use to return either all registered services at the moment of resolve (dynamic fresh view) or - /// the same services that were returned with first call (fixed view). - /// Return collection item type. - /// It denotes registered service type if is not specified. - /// The same result could be achieved by directly calling: - /// >(); // for dynamic result - default behavior - /// container.Resolve(); // for fixed array - /// container.Resolve>(); // same as fixed array - /// ]]> - /// - public static IEnumerable ResolveMany(this IResolver resolver, - Type requiredServiceType = null, ResolveManyBehavior behavior = ResolveManyBehavior.AsLazyEnumerable, - object[] args = null, object serviceKey = null) => - behavior == ResolveManyBehavior.AsLazyEnumerable - ? resolver.ResolveMany(typeof(TService), serviceKey, requiredServiceType, Request.Empty, args).Cast() - : resolver.Resolve>(serviceKey, IfUnresolved.Throw, requiredServiceType, args); - - /// Returns all registered services as objects, including all keyed and default registrations. - public static IEnumerable ResolveMany(this IResolver resolver, Type serviceType, - ResolveManyBehavior behavior = ResolveManyBehavior.AsLazyEnumerable, - object[] args = null, object serviceKey = null) => - resolver.ResolveMany(serviceType, behavior, args, serviceKey); - - internal static readonly ConstructorInfo ResolutionScopeNameCtor = - typeof(ResolutionScopeName).GetTypeInfo().DeclaredConstructors.First(); - - internal static Expression CreateResolutionExpression(Request request, bool opensResolutionScope = false) - { - if (request.Rules.DependencyResolutionCallExprs != null && - request.Factory != null && !request.Factory.HasRuntimeState) - PopulateDependencyResolutionCallExpressions(request); - - var container = request.Container; - var serviceType = request.ServiceType; - - var serviceTypeExpr = Constant(serviceType, typeof(Type)); - var ifUnresolvedExpr = Constant(request.IfUnresolved, typeof(IfUnresolved)); - var requiredServiceTypeExpr = Constant(request.RequiredServiceType, typeof(Type)); - var serviceKeyExpr = container.GetConstantExpression(request.ServiceKey, typeof(object)); - - var resolverExpr = ResolverContext.GetRootOrSelfExpr(request); - - if (opensResolutionScope) - { - // Generates the code below. That means the service opening the scope is scoped to this scope. - // - // r => r.OpenScope(new ResolutionScopeName(serviceType, serviceKey), trackInParent: true) - // .Resolve(serviceType, serviceKey) - // - var actualServiceTypeExpr = Constant(request.GetActualServiceType(), typeof(Type)); - var scopeNameExpr = New(ResolutionScopeNameCtor, actualServiceTypeExpr, serviceKeyExpr); - var trackInParent = Constant(true); - - resolverExpr = Call(ResolverContext.OpenScopeMethod, - FactoryDelegateCompiler.ResolverContextParamExpr, scopeNameExpr, trackInParent); - } - - var parentFlags = default(RequestFlags); - if (opensResolutionScope) - parentFlags |= RequestFlags.OpensResolutionScope; - if ((request.Flags & RequestFlags.StopRecursiveDependencyCheck) != 0) - parentFlags |= RequestFlags.StopRecursiveDependencyCheck; - - // Only parent is converted to be passed to Resolve. - // The current request is formed by rest of Resolve parameters. - var preResolveParentExpr = container.GetRequestExpression(request.DirectParent, parentFlags); - - var resolveCallExpr = Call(resolverExpr, ResolveMethod, serviceTypeExpr, serviceKeyExpr, - ifUnresolvedExpr, requiredServiceTypeExpr, preResolveParentExpr, request.GetInputArgsExpr()); - - if (serviceType == typeof(object)) - return resolveCallExpr; - - return Convert(resolveCallExpr, serviceType); - } - - private static void PopulateDependencyResolutionCallExpressions(Request request) - { - // Actually calls nested Resolve and stores produced expression in collection inside the container Rules. - // Stops on recursive dependency, e.g. - // `new A(new Lazy(r => r.Resolve())` and `new B(new A())` - for (var p = request.DirectParent; !p.IsEmpty; p = p.DirectParent) - if (p.FactoryID == request.FactoryID) - return; - - var factory = request.Container.ResolveFactory(request); - if (factory == null || factory is FactoryPlaceholder) - return; - - // Prevents infinite recursion when generating the resolution dependency #579 - if ((request.Flags & RequestFlags.IsGeneratedResolutionDependencyExpression) != 0) - return; - - request.Flags |= RequestFlags.IsGeneratedResolutionDependencyExpression; - - var factoryExpr = factory.GetExpressionOrDefault(request)?.NormalizeExpression(); - if (factoryExpr == null) - return; - - request.Container.Rules.DependencyResolutionCallExprs.Swap(request, factoryExpr, - (x, req, facExpr) => x.AddOrUpdate(req, facExpr -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToExpression() -#endif - )); - } - } - - /// Specifies result of : either dynamic(lazy) or fixed view. - public enum ResolveManyBehavior - { - /// Lazy/dynamic item resolve. - AsLazyEnumerable, - /// Fixed array of item at time of resolve, newly registered/removed services won't be listed. - AsFixedArray - } - - /// Provides information required for service resolution: service type - /// and optional - public interface IServiceInfo - { - /// The required piece of info: service type. - Type ServiceType { get; } - - /// Additional optional details: service key, if-unresolved policy, required service type. - ServiceDetails Details { get; } - - /// Creates info from service type and details. - IServiceInfo Create(Type serviceType, ServiceDetails details); - } - - /// Provides optional service resolution details: service key, required service type, what return when service is unresolved, - /// default value if service is unresolved, custom service value. - public class ServiceDetails - { - /// Default details if not specified, use default setting values, e.g. - public static readonly ServiceDetails Default = - new ServiceDetails(null, IfUnresolved.Throw, null, null, null, null, false); - - /// Default details with option. - public static readonly ServiceDetails IfUnresolvedReturnDefault = - new ServiceDetails(null, IfUnresolved.ReturnDefault, null, null, null, null, false); - - /// Default details with option. - public static readonly ServiceDetails IfUnresolvedReturnDefaultIfNotRegistered = - new ServiceDetails(null, IfUnresolved.ReturnDefaultIfNotRegistered, null, null, null, null, false); - - /// Creates new details out of provided settings, or returns default if all settings have default value. - public static ServiceDetails Of(Type requiredServiceType = null, - object serviceKey = null, IfUnresolved ifUnresolved = IfUnresolved.Throw, - object defaultValue = null, string metadataKey = null, object metadata = null) - { - if (defaultValue != null) - { - // IfUnresolved.Throw does not make sense when default value is provided, so normalizing it to ReturnDefault - if (ifUnresolved == IfUnresolved.Throw) - ifUnresolved = IfUnresolved.ReturnDefault; - } - else if (requiredServiceType == null && serviceKey == null && - metadataKey == null && metadata == null) - { - if (ifUnresolved == IfUnresolved.Throw) - return Default; - if (ifUnresolved == IfUnresolved.ReturnDefault) - return IfUnresolvedReturnDefault; - if (ifUnresolved == IfUnresolved.ReturnDefaultIfNotRegistered) - return IfUnresolvedReturnDefaultIfNotRegistered; - } - - return new ServiceDetails(requiredServiceType, ifUnresolved, - serviceKey, metadataKey, metadata, defaultValue, hasCustomValue: false); - } - - /// Sets custom value for service. This setting is orthogonal to the rest. - /// Using default value with invalid ifUnresolved.Throw option to indicate custom value. - public static ServiceDetails Of(object value) => - new ServiceDetails(null, IfUnresolved.Throw, null, null, null, value, hasCustomValue: true); - - /// Service type to search in registry. Should be assignable to user requested service type. - public readonly Type RequiredServiceType; - - /// Service key provided with registration. - public readonly object ServiceKey; - - /// Metadata key to find in metadata dictionary in resolved service. - public readonly string MetadataKey; - - /// Metadata value to find in resolved service. - public readonly object Metadata; - - /// Policy to deal with unresolved request. - public readonly IfUnresolved IfUnresolved; - - /// Indicates that the custom value is specified. - public readonly bool HasCustomValue; - - /// Either default or custom value depending on setting. - private readonly object _value; - - /// Value to use in case is set to not Throw. - public object DefaultValue => IfUnresolved != IfUnresolved.Throw ? _value : null; - - /// Custom value specified for dependency. The IfUnresolved.Throw is the marker of custom value comparing to default value. - public object CustomValue => IfUnresolved == IfUnresolved.Throw ? _value : null; - - /// Pretty prints service details to string for debugging and errors. Details string. - public override string ToString() - { - var s = new StringBuilder(); - - if (HasCustomValue) - return s.Append("{CustomValue=").Print(CustomValue ?? "null").Append("}").ToString(); - - if (RequiredServiceType != null) - s.Append("RequiredServiceType=").Print(RequiredServiceType); - if (ServiceKey != null) - (s.Length == 0 ? s.Append('{') : s.Append(", ")).Append("ServiceKey=").Print(ServiceKey); - if (MetadataKey != null || Metadata != null) - (s.Length == 0 ? s.Append('{') : s.Append(", ")).Append("Metadata=").Append(MetadataKey.Pair(Metadata)); - if (IfUnresolved != IfUnresolved.Throw) - { - s = (s.Length == 0 ? s.Append('{') : s.Append(", ")).Print(IfUnresolved); - s = _value == null ? s : s.Append(", DefaultValue=").Print(_value); - } - - return (s.Length == 0 ? s : s.Append('}')).ToString(); - } - - private ServiceDetails(Type requiredServiceType, IfUnresolved ifUnresolved, - object serviceKey, string metadataKey, object metadata, - object value, bool hasCustomValue) - { - RequiredServiceType = requiredServiceType; - IfUnresolved = ifUnresolved; - ServiceKey = serviceKey; - MetadataKey = metadataKey; - Metadata = metadata; - _value = value; - HasCustomValue = hasCustomValue; - } - } - - /// Contains tools for combining or propagating of independent of its concrete implementations. - public static class ServiceInfoTools - { - /// Creates service info with new type but keeping the details. - public static IServiceInfo With(this IServiceInfo source, Type serviceType) => - source.Create(serviceType, source.Details); - - /// Creates new info with new IfUnresolved behavior or returns the original info if behavior is not different, - /// or the passed info is not a . - public static IServiceInfo WithIfUnresolved(this IServiceInfo source, IfUnresolved ifUnresolved) - { - var details = source.Details; - if (details.IfUnresolved == ifUnresolved || details.HasCustomValue) - return source; - - if (details == ServiceDetails.Default) - details = ifUnresolved == IfUnresolved.ReturnDefault - ? ServiceDetails.IfUnresolvedReturnDefault - : ServiceDetails.IfUnresolvedReturnDefaultIfNotRegistered; - else - details = ServiceDetails.Of(details.RequiredServiceType, details.ServiceKey, - ifUnresolved, details.DefaultValue, details.MetadataKey, details.Metadata); - - return source.Create(source.ServiceType, details); - } - - // todo: Should be renamed or better to be removed, the whole operation should be hidden behind abstraction - /// Combines service info with details. The main goal is to combine service and required service type. - public static T WithDetails(this T serviceInfo, ServiceDetails details) - where T : IServiceInfo - { - details = details ?? ServiceDetails.Default; - var sourceDetails = serviceInfo.Details; - if (!details.HasCustomValue && - sourceDetails != ServiceDetails.Default && - sourceDetails != details) - { - var serviceKey = details.ServiceKey ?? sourceDetails.ServiceKey; - var metadataKey = details.MetadataKey ?? sourceDetails.MetadataKey; - var metadata = metadataKey == details.MetadataKey ? details.Metadata : sourceDetails.Metadata; - var defaultValue = details.DefaultValue ?? sourceDetails.DefaultValue; - - details = ServiceDetails.Of(details.RequiredServiceType, serviceKey, - details.IfUnresolved, defaultValue, metadataKey, metadata); - } - - var serviceType = serviceInfo.ServiceType; - var requiredServiceType = details.RequiredServiceType; - - if (requiredServiceType != null && requiredServiceType == serviceType) - details = ServiceDetails.Of(null, - details.ServiceKey, details.IfUnresolved, details.DefaultValue, - details.MetadataKey, details.Metadata); - - // if service type unchanged and details absent, or details are the same return original info, otherwise create new one - return serviceType == serviceInfo.ServiceType - && (details == null || details == serviceInfo.Details) - ? serviceInfo - : (T)serviceInfo.Create(serviceType, details); - } - - // todo: May operate directly on ServiceType and ServiceDetails instead of IServiceInfo interface - /// Enables propagation/inheritance of info between dependency and its owner: - /// for instance for wrappers. - public static IServiceInfo InheritInfoFromDependencyOwner(this IServiceInfo dependency, - IServiceInfo owner, IContainer container, FactoryType ownerType = FactoryType.Service) - { - var ownerDetails = owner.Details; - if (ownerDetails == null || ownerDetails == ServiceDetails.Default) - return dependency; - - var dependencyDetails = dependency.Details; - - var ownerIfUnresolved = ownerDetails.IfUnresolved; - var ifUnresolved = dependencyDetails.IfUnresolved; - if (ownerIfUnresolved == IfUnresolved.ReturnDefault) // ReturnDefault is always inherited - ifUnresolved = ownerIfUnresolved; - - var serviceType = dependency.ServiceType; - var requiredServiceType = dependencyDetails.RequiredServiceType; - var ownerRequiredServiceType = ownerDetails.RequiredServiceType; - - var serviceKey = dependencyDetails.ServiceKey; - var metadataKey = dependencyDetails.MetadataKey; - var metadata = dependencyDetails.Metadata; - - // Inherit some things through wrappers and decorators - if (ownerType == FactoryType.Wrapper || - ownerType == FactoryType.Decorator && - container.GetWrappedType(serviceType, requiredServiceType).IsAssignableTo(owner.ServiceType)) - { - if (ownerIfUnresolved == IfUnresolved.ReturnDefaultIfNotRegistered) - ifUnresolved = ownerIfUnresolved; - - if (serviceKey == null) - serviceKey = ownerDetails.ServiceKey; - - if (metadataKey == null && metadata == null) - { - metadataKey = ownerDetails.MetadataKey; - metadata = ownerDetails.Metadata; - } - } - - if (ownerType != FactoryType.Service && ownerRequiredServiceType != null && - requiredServiceType == null) // if only dependency does not have its own - requiredServiceType = ownerRequiredServiceType; - - if (serviceKey == dependencyDetails.ServiceKey && - metadataKey == dependencyDetails.MetadataKey && metadata == dependencyDetails.Metadata && - ifUnresolved == dependencyDetails.IfUnresolved && requiredServiceType == dependencyDetails.RequiredServiceType) - return dependency; - - if (serviceType == requiredServiceType) - requiredServiceType = null; - - var serviceDetails = ServiceDetails.Of(requiredServiceType, - serviceKey, ifUnresolved, dependencyDetails.DefaultValue, - metadataKey, metadata); - - return dependency.Create(serviceType, serviceDetails); - } - - /// Returns required service type if it is specified and assignable to service type, - /// otherwise returns service type. - public static Type GetActualServiceType(this IServiceInfo info) - { - var requiredServiceType = info.Details.RequiredServiceType; - return requiredServiceType != null && - info.ServiceType.GetTypeInfo().IsAssignableFrom(requiredServiceType.GetTypeInfo()) - ? requiredServiceType : info.ServiceType; - } - - /// Appends info string representation into provided builder. - public static StringBuilder Print(this StringBuilder s, IServiceInfo info) - { - s.Print(info.ServiceType); - var details = info.Details.ToString(); - return details == string.Empty ? s : s.Append(' ').Append(details); - } - } - - /// Represents custom or resolution root service info, there is separate representation for parameter, - /// property and field dependencies. - public class ServiceInfo : IServiceInfo - { - /// Empty service info for convenience. - public static readonly IServiceInfo Empty = new ServiceInfo(null); - - /// Creates info out of provided settings - public static ServiceInfo Of(Type serviceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null) => - Of(serviceType, null, ifUnresolved, serviceKey); - - /// Creates info out of provided settings - public static ServiceInfo Of(Type serviceType, Type requiredServiceType, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null, - string metadataKey = null, object metadata = null) - { - (serviceType ?? requiredServiceType).ThrowIfNull(); - - // remove unnecessary details if service and required type are the same - if (serviceType == requiredServiceType) - requiredServiceType = null; - - return serviceKey == null && requiredServiceType == null - && metadataKey == null && metadata == null - ? (ifUnresolved == IfUnresolved.Throw ? new ServiceInfo(serviceType) - : ifUnresolved == IfUnresolved.ReturnDefault ? new WithDetails(serviceType, ServiceDetails.IfUnresolvedReturnDefault) - : new WithDetails(serviceType, ServiceDetails.IfUnresolvedReturnDefaultIfNotRegistered)) - : new WithDetails(serviceType, - ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, null, metadataKey, metadata)); - } - - /// Creates service info using typed . - public static Typed Of(IfUnresolved ifUnresolved = IfUnresolved.Throw, object serviceKey = null) => - serviceKey == null && ifUnresolved == IfUnresolved.Throw - ? new Typed() - : new TypedWithDetails(ServiceDetails.Of(null, serviceKey, ifUnresolved)); - - /// Strongly-typed version of Service Info. Service type. - public class Typed : ServiceInfo - { - /// Creates service info object. - public Typed() : base(typeof(TService)) { } - } - - /// Type of service to create. Indicates registered service in registry. - public Type ServiceType { get; } - - /// Shortcut access to service key - public object ServiceKey => Details.ServiceKey; - - /// Additional settings. If not specified uses . - public virtual ServiceDetails Details => ServiceDetails.Default; - - /// Creates info from service type and details. - public IServiceInfo Create(Type serviceType, ServiceDetails details) => - details == ServiceDetails.Default ? new ServiceInfo(serviceType) : new WithDetails(serviceType, details); - - /// Prints info to string using . Printed string. - public override string ToString() => - new StringBuilder().Print(this).ToString(); - -#region Implementation - - private ServiceInfo(Type serviceType) { ServiceType = serviceType; } - - private class WithDetails : ServiceInfo - { - public override ServiceDetails Details => _details; - public WithDetails(Type serviceType, ServiceDetails details) : base(serviceType) { _details = details; } - private readonly ServiceDetails _details; - } - - private class TypedWithDetails : Typed - { - public override ServiceDetails Details => _details; - public TypedWithDetails(ServiceDetails details) { _details = details; } - private readonly ServiceDetails _details; - } - -#endregion - } - - /// Provides for parameter, - /// by default using parameter name as . - /// For parameter default setting is . - public class ParameterServiceInfo : IServiceInfo - { - /// Creates service info from parameter alone, setting service type to parameter type, - /// and setting resolution policy to if parameter is optional. - /// Parameter to create info for. - /// Parameter service info. - [MethodImpl((MethodImplOptions)256)] - public static ParameterServiceInfo Of(ParameterInfo parameter) - { - if (!parameter.IsOptional) - return new ParameterServiceInfo(parameter); - return new WithDetails(parameter, parameter.DefaultValue == null - ? ServiceDetails.IfUnresolvedReturnDefault - : ServiceDetails.Of(ifUnresolved: IfUnresolved.ReturnDefault, defaultValue: parameter.DefaultValue)); - } - - /// Service type specified by . - public virtual Type ServiceType => Parameter.ParameterType; - - /// Optional service details. - public virtual ServiceDetails Details => ServiceDetails.Default; - - /// Creates info from service type and details. - public IServiceInfo Create(Type serviceType, ServiceDetails details) => - serviceType == ServiceType ? new WithDetails(Parameter, details) : new TypeWithDetails(Parameter, serviceType, details); - - /// Parameter info. - public readonly ParameterInfo Parameter; - - /// Prints info to string using . Printed string. - public override string ToString() => - new StringBuilder().Print(this).Append(" as parameter ").Print(Parameter.Name).ToString(); - -#region Implementation - - private ParameterServiceInfo(ParameterInfo parameter) { Parameter = parameter; } - - private class WithDetails : ParameterServiceInfo - { - public override ServiceDetails Details { get { return _details; } } - public WithDetails(ParameterInfo parameter, ServiceDetails details) - : base(parameter) - { _details = details; } - private readonly ServiceDetails _details; - } - - private sealed class TypeWithDetails : WithDetails - { - public override Type ServiceType { get { return _serviceType; } } - public TypeWithDetails(ParameterInfo parameter, Type serviceType, ServiceDetails details) - : base(parameter, details) - { _serviceType = serviceType; } - private readonly Type _serviceType; - } - -#endregion - } - - /// Base class for property and field dependency info. - public abstract class PropertyOrFieldServiceInfo : IServiceInfo - { - /// Create member info out of provide property or field. - /// Member is either property or field. Created info. - public static PropertyOrFieldServiceInfo Of(MemberInfo member) => - member.ThrowIfNull() is PropertyInfo - ? (PropertyOrFieldServiceInfo)new Property((PropertyInfo)member) - : new Field((FieldInfo)member); - - /// The required service type. It will be either or . - public abstract Type ServiceType { get; } - - /// Optional details: service key, if-unresolved policy, required service type. - public virtual ServiceDetails Details => ServiceDetails.IfUnresolvedReturnDefaultIfNotRegistered; - - /// Creates info from service type and details. - /// Required service type. Optional details. Create info. - public abstract IServiceInfo Create(Type serviceType, ServiceDetails details); - - /// Either or . - public abstract MemberInfo Member { get; } - - /// Sets property or field value on provided holder object. - /// Holder of property or field. Value to set. - public abstract void SetValue(object holder, object value); - -#region Implementation - - private class Property : PropertyOrFieldServiceInfo - { - public override Type ServiceType => _property.PropertyType; - - public override IServiceInfo Create(Type serviceType, ServiceDetails details) => - serviceType == ServiceType ? new WithDetails(_property, details) : new TypeWithDetails(_property, serviceType, details); - - public override MemberInfo Member => _property; - - public override void SetValue(object holder, object value) => _property.SetValue(holder, value, null); - - public override string ToString() => - new StringBuilder().Print(this).Append(" as property ").Print(_property.Name).ToString(); - - private readonly PropertyInfo _property; - public Property(PropertyInfo property) { _property = property; } - - private class WithDetails : Property - { - public override ServiceDetails Details { get; } - - public WithDetails(PropertyInfo property, ServiceDetails details) : base(property) { Details = details; } - } - - private sealed class TypeWithDetails : WithDetails - { - public override Type ServiceType { get; } - - public TypeWithDetails(PropertyInfo property, Type serviceType, ServiceDetails details) - : base(property, details) { ServiceType = serviceType; } - } - } - - private class Field : PropertyOrFieldServiceInfo - { - public override Type ServiceType => _field.FieldType; - - public override IServiceInfo Create(Type serviceType, ServiceDetails details) => - serviceType == null ? new WithDetails(_field, details) : new TypeWithDetails(_field, serviceType, details); - - public override MemberInfo Member => _field; - - public override void SetValue(object holder, object value) => _field.SetValue(holder, value); - - public override string ToString() => - new StringBuilder().Print(this).Append(" as field ").Print(_field.Name).ToString(); - - private readonly FieldInfo _field; - public Field(FieldInfo field) { _field = field; } - - private class WithDetails : Field - { - public override ServiceDetails Details { get; } - - public WithDetails(FieldInfo field, ServiceDetails details) : base(field) { Details = details; } - } - - private sealed class TypeWithDetails : WithDetails - { - public override Type ServiceType { get; } - - public TypeWithDetails(FieldInfo field, Type serviceType, ServiceDetails details) : base(field, details) - { ServiceType = serviceType; } - } - } - -#endregion - } - - /// Stored check results of two kinds: inherited down dependency chain and not. - [Flags] - public enum RequestFlags - { - /// Not inherited - TracksTransientDisposable = 1 << 1, - - /// Inherited - IsSingletonOrDependencyOfSingleton = 1 << 3, - - /// Inherited - IsWrappedInFunc = 1 << 4, - - /// Indicates that the request is the one from Resolve call. - IsResolutionCall = 1 << 5, - - /// Non inherited - OpensResolutionScope = 1 << 6, - - /// Non inherited - StopRecursiveDependencyCheck = 1 << 7, - - /// Non inherited. Marks the expression to be added to generated resolutions to prevent infinite recursion - IsGeneratedResolutionDependencyExpression = 1 << 8, - - /// Non inherited. Indicates the root service inside the function. - IsDirectlyWrappedInFunc = 1 << 9 - } - - /// Helper extension methods to use on the bunch of factories instead of lambdas to minimize allocations - internal static class RequestTools - { - public static bool MatchFactoryConditionAndMetadata(this Request request, Factory factory) - { - if (!factory.CheckCondition(request)) - return false; - - var metadataKey = request.MetadataKey; - var metadata = request.Metadata; - return (metadataKey == null && metadata == null) || factory.Setup.MatchesMetadata(metadataKey, metadata); - } - - public static bool MatchFactoryConditionAndMetadata(this Request r, KV f) => - r.MatchFactoryConditionAndMetadata(f.Value); - - public static bool MatchFactoryReuse(this Request r, KV f) => - f.Value.Reuse?.CanApply(r) ?? true; - - public static bool MatchGeneratedFactory(this Request r, KV f) => - f.Value.FactoryGenerator == null || f.Value.FactoryGenerator.GetGeneratedFactory(r, ifErrorReturnDefault: true) != null; - } - - internal sealed class RequestStack - { - public static RequestStack Get(int index = 0) - { - var capacity = 4; - while (index >= capacity) - capacity <<= 1; - return new RequestStack(capacity); - } - - public Request[] Items; - private RequestStack(int capacity) => Items = new Request[capacity]; - - public ref Request GetOrPushRef(int index) - { - if (index < Items.Length) - return ref Items[index]; - - Items = Expand(Items, index); - return ref Items[index]; - } - - private static Request[] Expand(Request[] items, int index) - { - var count = items.Length; - var newCount = count << 1; - - // ensure that the index is always in range - while (index >= newCount) - newCount <<= 1; - - var newItems = new Request[newCount]; // count x 2 - Array.Copy(items, 0, newItems, 0, count); - return newItems; - } - } - - /// Tracks the requested service and resolved factory details in a chain of nested dependencies. - public sealed class Request : IEnumerable - { - internal static readonly RequestFlags InheritedFlags - = RequestFlags.IsSingletonOrDependencyOfSingleton - | RequestFlags.IsWrappedInFunc; - - private const RequestFlags DefaultFlags = default; - - /// Empty terminal request. - public static readonly Request Empty = - new Request(null, null, 0, null, DefaultFlags, ServiceInfo.Empty, null); - - internal static readonly Expression EmptyRequestExpr = - Field(null, typeof(Request).Field(nameof(Empty))); - - /// Empty request which opens resolution scope. - public static readonly Request EmptyOpensResolutionScope = - new Request(null, null, 0, null, DefaultFlags | RequestFlags.OpensResolutionScope | RequestFlags.IsResolutionCall, - ServiceInfo.Empty, null); - - internal static readonly Expression EmptyOpensResolutionScopeRequestExpr = - Field(null, typeof(Request).Field(nameof(EmptyOpensResolutionScope))); - - /// Creates the Resolve request. The container initiated the Resolve is stored with request. - public static Request Create(IContainer container, IServiceInfo serviceInfo, - Request preResolveParent = null, RequestFlags flags = DefaultFlags, object[] inputArgs = null) - { - var serviceType = serviceInfo.ThrowIfNull().ServiceType; - if (serviceType != null && serviceType.IsOpenGeneric()) - Throw.It(Error.ResolvingOpenGenericServiceTypeIsNotPossible, serviceType); - - flags |= RequestFlags.IsResolutionCall; - - // inherit some flags and service details from parent (if any) - preResolveParent = preResolveParent ?? Empty; - if (!preResolveParent.IsEmpty) - { - serviceInfo = serviceInfo.InheritInfoFromDependencyOwner( - preResolveParent._serviceInfo, container, preResolveParent.FactoryType); - - flags |= preResolveParent.Flags & InheritedFlags; - } - - var inputArgExprs = inputArgs?.Map(a => Constant(a)); - - var stack = RequestStack.Get(); - ref var req = ref stack.GetOrPushRef(0); - - // we are re-starting the dependency depth count from `1` - if (req == null) - req = new Request(container, preResolveParent, 1, stack, flags, serviceInfo, inputArgExprs); - else - req.SetServiceInfo(container, preResolveParent, 1, stack, flags, serviceInfo, inputArgExprs); - return req; - } - - /// Creates the Resolve request. The container initiated the Resolve is stored with request. - public static Request Create(IContainer container, Type serviceType, - object serviceKey = null, IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, - Request preResolveParent = null, RequestFlags flags = DefaultFlags, object[] inputArgs = null) => - Create(container, ServiceInfo.Of(serviceType, requiredServiceType, ifUnresolved, serviceKey), - preResolveParent, flags, inputArgs); - - // todo: Make a property in v5.0 - /// Available in runtime only, provides access to container initiated the request. - public IContainer Container; - - /// Request immediate parent. - public Request DirectParent; - - internal RequestStack RequestStack; - internal int IndexInStack => DependencyDepth - 1; - - // note: mutable because of RequestFlags.AddedToResolutionExpressions - /// Persisted request conditions - public RequestFlags Flags; - - /// mutable, so that the ServiceKey or IfUnresolved can be changed in place. - internal IServiceInfo _serviceInfo; - - /// Service details part of service info - public ServiceDetails ServiceDetails => _serviceInfo.Details; - - //internal IServiceInfo _serviceDetails; // todo: use this as much as possible instead of `_serviceInfo` to avoid virtual calls - - /// Input arguments provided with `Resolve` - internal Expression[] InputArgExprs; - - /// Runtime known resolve factory, otherwise is null - internal Factory Factory; - - /// Resolved factory ID, used to identify applied decorator. - public int FactoryID { get; private set; } - - // based on FactoryID - private int _hashCode; - - /// Type of factory: Service, Wrapper, or Decorator. - public FactoryType FactoryType { get; private set; } - - /// Service implementation type if known. - public Type ImplementationType => _factoryImplType ?? Factory?.ImplementationType; - private Type _factoryImplType; - - /// Service reuse. - public IReuse Reuse { get; private set; } - - /// ID of decorated factory in case of decorator factory type - public int DecoratedFactoryID { get; private set; } - - /// Number of nested dependencies. Set with each new Push. - public int DependencyDepth; - - /// Indicates that request is empty initial request. - public bool IsEmpty => DirectParent == null; - - /// Returns true if request is First in First Resolve call. - public bool IsResolutionRoot => DirectParent != null && DirectParent.DirectParent == null; - - /// Returns true if request is First in Resolve call. - public bool IsResolutionCall => DirectParent != null && (Flags & RequestFlags.IsResolutionCall) != 0; - - /// Not the root resolution call. - public bool IsNestedResolutionCall => IsResolutionCall && DirectParent.DirectParent != null; - - /// Returns true if request is First in First Resolve call. - public bool OpensResolutionScope => DirectParent != null && (DirectParent.Flags & RequestFlags.OpensResolutionScope) != 0; - - /// Checks if the request Or its parent is wrapped in Func. - /// Use for the direct Func wrapper. - public bool IsWrappedInFunc() => (Flags & RequestFlags.IsWrappedInFunc) != 0; - - /// Checks if the request is directly wrapped in Func - public bool IsDirectlyWrappedInFunc() => (Flags & RequestFlags.IsDirectlyWrappedInFunc) != 0; - - /// Checks if request has parent with service type of Func with arguments. - public bool IsWrappedInFuncWithArgs() => InputArgExprs != null; - - /// Returns expression for func arguments. - public Expression GetInputArgsExpr() => - InputArgExprs == null ? Constant(null, typeof(object[])) - : (Expression)NewArrayInit(typeof(object), InputArgExprs.Map(x => x.Type.IsValueType() ? Convert(x, typeof(object)) : x)); - - /// Indicates that requested service is transient disposable that should be tracked. - public bool TracksTransientDisposable => (Flags & RequestFlags.TracksTransientDisposable) != 0; - - /// Indicates the request is singleton or has singleton upper in dependency chain. - public bool IsSingletonOrDependencyOfSingleton => (Flags & RequestFlags.IsSingletonOrDependencyOfSingleton) != 0; - - /// [Obsolete("Unused - hides more than abstracts")] - public bool ShouldSplitObjectGraph() => - FactoryType == FactoryType.Service && - DependencyDepth > Rules.DependencyDepthToSplitObjectGraph; - - /// Current scope - public IScope CurrentScope => Container.CurrentScope; - - /// Singletons - public IScope SingletonScope => Container.SingletonScope; - - /// Shortcut to issued container rules. - public Rules Rules => Container.Rules; - - /// (optional) Made spec used for resolving request. - public Made Made => Factory?.Made; - - /// Returns service parent skipping wrapper if any. To get direct parent use . - public Request Parent - { - get - { - var p = DirectParent; - if (p != null) - while (p.DirectParent != null && p.FactoryType == FactoryType.Wrapper) - p = p.DirectParent; - return p; - } - } - - /// Requested service type. - public Type ServiceType => _serviceInfo.ServiceType; - - /// Compatible required or service type. - public Type GetActualServiceType() => _actualServiceType; - private Type _actualServiceType; - - /// Optional service key to identify service of the same type. - public object ServiceKey => _serviceInfo.Details.ServiceKey; - - /// Metadata key to find in metadata dictionary in resolved service. - public string MetadataKey => _serviceInfo.Details.MetadataKey; - - /// Metadata or the value (if key specified) to find in resolved service. - public object Metadata => _serviceInfo.Details.Metadata; - - /// Policy to deal with unresolved service. - public IfUnresolved IfUnresolved => _serviceInfo.Details.IfUnresolved; - - /// Required service type if specified. - public Type RequiredServiceType => _serviceInfo.Details.RequiredServiceType; - - /// Relative number representing reuse lifespan. - public int ReuseLifespan => Reuse?.Lifespan ?? 0; - - /// Known implementation, or otherwise actual service type. - public Type GetKnownImplementationOrServiceType() => ImplementationType ?? GetActualServiceType(); - - /// Creates new request with provided info, and links current request as a parent. - /// Allows to set some additional flags. Existing/parent request should be resolved to - /// factory via `WithResolvedFactory` before pushing info into it. - public Request Push(IServiceInfo info, RequestFlags additionalFlags = DefaultFlags) - { - if (FactoryID == 0) - Throw.It(Error.PushingToRequestWithoutFactory, info, this); - - var flags = Flags & InheritedFlags | additionalFlags; - var serviceInfo = info.ThrowIfNull().InheritInfoFromDependencyOwner(_serviceInfo, Container, FactoryType); - - var stack = RequestStack; - var indexInStack = IndexInStack + 1; - if (stack == null) - { - stack = RequestStack.Get(indexInStack); - - // traverse all the requests up including the resolution root and set the new stack to them - Request parent = null; - do - { - parent = parent == null ? this : parent.DirectParent; - parent.RequestStack = stack; - } - while ((parent.Flags & RequestFlags.IsResolutionCall) == 0 && !parent.DirectParent.IsEmpty); - } - - ref var req = ref stack.GetOrPushRef(indexInStack); - if (req == null) - req = new Request(Container, this, DependencyDepth + 1, RequestStack, flags, serviceInfo, InputArgExprs); - else - req.SetServiceInfo(Container, this, DependencyDepth + 1, RequestStack, flags, serviceInfo, InputArgExprs); - return req; - } - - /// Composes service description into and Pushes the new request. - public Request Push(Type serviceType, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, Type requiredServiceType = null, RequestFlags flags = DefaultFlags) => - Push(ServiceInfo.Of(serviceType.ThrowIfNull().ThrowIf(serviceType.IsOpenGeneric(), Error.ResolvingOpenGenericServiceTypeIsNotPossible), - requiredServiceType, ifUnresolved, serviceKey), flags); - -#region Used in generated expression - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, int factoryID, Type implementationType, IReuse reuse) => - Push(serviceType, null, null, null, null, IfUnresolved.Throw, - factoryID, FactoryType.Service, implementationType, reuse, DefaultFlags, 0); - - internal static readonly Lazy PushMethodWith4Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), typeof(Type), typeof(int), typeof(Type), typeof(IReuse))); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, Type requiredServiceType, object serviceKey, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags) => - Push(serviceType, requiredServiceType, serviceKey, null, null, IfUnresolved.Throw, - factoryID, factoryType, implementationType, reuse, flags, 0); - - internal static readonly Lazy PushMethodWith8Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), typeof(Type), typeof(Type), typeof(object), - typeof(int), typeof(FactoryType), typeof(Type), typeof(IReuse), typeof(RequestFlags))); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push(Type serviceType, Type requiredServiceType, object serviceKey, IfUnresolved ifUnresolved, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags, - int decoratedFactoryID) => - Push(serviceType, requiredServiceType, serviceKey, null, null, ifUnresolved, - factoryID, factoryType, implementationType, reuse, flags, decoratedFactoryID); - - internal static readonly Lazy PushMethodWith10Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), - typeof(Type), typeof(Type), typeof(object), typeof(IfUnresolved), - typeof(int), typeof(FactoryType), typeof(Type), typeof(IReuse), typeof(RequestFlags), typeof(int))); - - /// Creates info by supplying all the properties and chaining it with current (parent) info. - public Request Push( - Type serviceType, Type requiredServiceType, object serviceKey, string metadataKey, object metadata, IfUnresolved ifUnresolved, - int factoryID, FactoryType factoryType, Type implementationType, IReuse reuse, RequestFlags flags, int decoratedFactoryID) - { - return new Request(Container, this, DependencyDepth + 1, null, flags, - ServiceInfo.Of(serviceType, requiredServiceType, ifUnresolved, serviceKey, metadataKey, metadata), - InputArgExprs, implementationType, null, // factory cannot be supplied in generated code - factoryID, factoryType, reuse, decoratedFactoryID); - } - - internal static readonly Lazy PushMethodWith12Args = Lazy.Of(() => - typeof(Request).Method(nameof(Push), - typeof(Type), typeof(Type), typeof(object), typeof(string), typeof(object), typeof(IfUnresolved), - typeof(int), typeof(FactoryType), typeof(Type), typeof(IReuse), typeof(RequestFlags), typeof(int))); - -#endregion - - /// Allow to switch current service info to the new one, e.g. in decorators. - /// If info did not change then return the same this request. - public Request WithChangedServiceInfo(Func getInfo) - { - var newServiceInfo = getInfo(_serviceInfo); - return newServiceInfo == _serviceInfo ? this - : new Request(Container, - DirectParent, DependencyDepth, RequestStack, Flags, newServiceInfo, InputArgExprs, - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - } - - /// Produces the new request with the changed `ifUnresolved` or returns original request otherwise - public Request WithIfUnresolved(IfUnresolved ifUnresolved) => - IfUnresolved == ifUnresolved ? this - : new Request(Container, - DirectParent, DependencyDepth, RequestStack, Flags, - _serviceInfo.WithIfUnresolved(ifUnresolved), InputArgExprs, - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - - // todo: in place mutation? - /// Updates the flags - public Request WithFlags(RequestFlags newFlags) => - new Request(Container, - DirectParent, DependencyDepth, RequestStack, newFlags, _serviceInfo, InputArgExprs, - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - - // note: Mutates the request, required for proper caching - /// Sets service key to passed value. Required for multiple default services to change null key to - /// actual - public void ChangeServiceKey(object serviceKey) - { - var info = _serviceInfo; - var details = info.Details; - _serviceInfo = info.Create(info.ServiceType, - ServiceDetails.Of(details.RequiredServiceType, serviceKey, details.IfUnresolved, details.DefaultValue)); - } - - /// Prepends input arguments to existing arguments in request. It is done because the - /// nested Func/Action input argument has a priority over outer argument. - /// The arguments are provided by Func and Action wrappers, or by `args` parameter in Resolve call. - public Request WithInputArgs(Expression[] inputArgs) => - new Request(Container, - DirectParent, DependencyDepth, RequestStack, Flags, _serviceInfo, inputArgs.Append(InputArgExprs), - _factoryImplType, Factory, FactoryID, FactoryType, Reuse, DecoratedFactoryID); - - /// Returns new request with set implementation details. - /// Factory to which request is resolved. - /// (optional) does not check for recursive dependency. - /// Use with caution. Make sense for Resolution expression. - /// (optional) allows to skip reuse mismatch aka captive dependency check. - /// Make a defensive copy of request. - /// New request with set factory. - public Request WithResolvedFactory(Factory factory, - bool skipRecursiveDependencyCheck = false, bool skipCaptiveDependencyCheck = false, bool copyRequest = false) - { - var factoryId = factory.FactoryID; - var factoryType = factory.FactoryType; - var decoratedFactoryID = 0; - if (Factory != null) // resolving the factory for the second time, usually happens in decorators - { - if (Factory.FactoryID == factoryId) - return this; // stop resolving to the same factory twice - if (factoryType == FactoryType.Decorator && Factory.FactoryType != FactoryType.Decorator) - decoratedFactoryID = FactoryID; - } - - // It is required to nullify the transient disposable tracking when factory is resolved multiple times, - // e.g. for the decorator - var flags = Flags & ~RequestFlags.TracksTransientDisposable; - if (skipRecursiveDependencyCheck) - flags |= RequestFlags.StopRecursiveDependencyCheck; - - var reuse = IsWrappedInFuncWithArgs() && Rules.IgnoringReuseForFuncWithArgs - ? DryIoc.Reuse.Transient - : factory.Reuse ?? CalculateDefaultReuse(factory); - - var reuseLifespan = reuse.Lifespan; - skipCaptiveDependencyCheck = skipCaptiveDependencyCheck - || reuseLifespan == 0 || !Rules.ThrowIfDependencyHasShorterReuseLifespan || factory.Setup.OpenResolutionScope; - - skipRecursiveDependencyCheck = skipRecursiveDependencyCheck - || factoryType != FactoryType.Service; - - // For the dependency we nned to check the recursive and the captive dependency - for (var parent = DirectParent; - !parent.IsEmpty && (!skipCaptiveDependencyCheck || !skipRecursiveDependencyCheck); - parent = parent.DirectParent) - { - if (!skipCaptiveDependencyCheck) - { - if (parent.OpensResolutionScope || - // ignores the ScopedOrSingleton inside Scoped or Singleton - (reuse as CurrentScopeReuse)?.ScopedOrSingleton == true && parent.Reuse is SingletonReuse || - parent.FactoryType == FactoryType.Wrapper && parent.GetActualServiceType().IsFunc()) - skipCaptiveDependencyCheck = true; - else if ( - parent.FactoryType == FactoryType.Service && - parent.ReuseLifespan > reuseLifespan) - Throw.It(Error.DependencyHasShorterReuseLifespan, PrintCurrent(), reuse, parent); - } - - if (!skipRecursiveDependencyCheck) - { - if ((parent.Flags & RequestFlags.StopRecursiveDependencyCheck) != 0) - skipRecursiveDependencyCheck = true; // stops further upward checking - else if (parent.FactoryID == factoryId) - Throw.It(Error.RecursiveDependencyDetected, Print(factoryId)); - } - } - - if (reuse == DryIoc.Reuse.Singleton) - { - flags |= RequestFlags.IsSingletonOrDependencyOfSingleton; - } - else if (reuse == DryIoc.Reuse.Transient) // check for disposable transient - { - reuse = GetTransientDisposableTrackingReuse(factory); - if (reuse != DryIoc.Reuse.Transient) - flags |= RequestFlags.TracksTransientDisposable; - } - - if (copyRequest) - { - IsolateRequestChain(); - return new Request(Container, DirectParent, DependencyDepth, null, flags, _serviceInfo, - InputArgExprs, null, factory, factoryId, factoryType, reuse, decoratedFactoryID); - } - - Flags = flags; - SetResolvedFactory(null, factory, factoryId, factoryType, reuse, decoratedFactoryID); - return this; - } - - /// Check for the parents. - public bool HasRecursiveParent(int factoryID) - { - for (var parent = DirectParent; !parent.IsEmpty; parent = parent.DirectParent) - { - if ((parent.Flags & RequestFlags.StopRecursiveDependencyCheck) != 0) - break; // stops further upward checking - if (parent.FactoryID == factoryID) - return true; - } - return false; - } - - private IReuse CalculateDefaultReuse(Factory factory) - { - if (factory.Setup.UseParentReuse) - return GetFirstParentNonTransientReuseUntilFunc(); - - if (factory.FactoryType == FactoryType.Decorator) - { - if (factory.Setup.To().UseDecorateeReuse || - Rules.UseDecorateeReuseForDecorators) - return Reuse; // use reuse of resolved service factory for decorator - } - - return factory.FactoryType == FactoryType.Wrapper - ? DryIoc.Reuse.Transient : Rules.DefaultReuse; - } - - private IReuse GetTransientDisposableTrackingReuse(Factory factory) - { - // Track transient disposable in parent scope (if any), or open scope (if any) - var setup = factory.Setup; - var tracksTransientDisposable = - !setup.PreventDisposal && - (setup.TrackDisposableTransient || !setup.AllowDisposableTransient && Rules.TrackingDisposableTransients) && - (factory.ImplementationType ?? GetActualServiceType()).IsAssignableTo(); - - if (!tracksTransientDisposable) - return DryIoc.Reuse.Transient; - - var parentReuse = GetFirstParentNonTransientReuseUntilFunc(); - if (parentReuse != DryIoc.Reuse.Transient) - return parentReuse; - - if (IsWrappedInFunc()) - return DryIoc.Reuse.Transient; - - // If no parent with reuse found, then track in current open scope or in singletons scope - return DryIoc.Reuse.ScopedOrSingleton; - } - - private IReuse GetFirstParentNonTransientReuseUntilFunc() - { - for (var parent = DirectParent; parent.DirectParent != null; parent = parent.DirectParent) - { - if (parent.FactoryType == FactoryType.Wrapper && parent.GetActualServiceType().IsFunc()) - break; - if (parent.FactoryType != FactoryType.Wrapper && parent.Reuse != DryIoc.Reuse.Transient) - return parent.Reuse; - } - - return DryIoc.Reuse.Transient; - } - - /// If request corresponds to dependency injected into parameter, - /// then method calls handling and returns its result. - /// If request corresponds to property or field, then method calls respective handler. - /// If request does not correspond to dependency, then calls handler. - public TResult Is( - Func root = null, - Func parameter = null, - Func property = null, - Func field = null) - { - var info = _serviceInfo; - if (info is ParameterServiceInfo) - { - if (parameter != null) - return parameter(((ParameterServiceInfo)info).Parameter); - } - else if (info is PropertyOrFieldServiceInfo) - { - var propertyOrFieldServiceInfo = (PropertyOrFieldServiceInfo)info; - var propertyInfo = propertyOrFieldServiceInfo.Member as PropertyInfo; - if (propertyInfo != null) - { - if (property != null) - return property(propertyInfo); - } - else if (field != null) - return field((FieldInfo)propertyOrFieldServiceInfo.Member); - } - else if (root != null) - return root(); - - return default(TResult); - } - - /// Obsolete: now request is directly implements the . - public IEnumerable Enumerate() => this; - - /// Enumerates self and all request stack parents. - public IEnumerator GetEnumerator() - { - for (var r = this; !r.IsEmpty; r = r.DirectParent) - yield return r; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// Prints current request info only (no parents printed) to provided builder. - public StringBuilder PrintCurrent(StringBuilder s = null) - { - s = s ?? new StringBuilder(); - - if (IsEmpty) - return s.Append(""); - - if (IsNestedResolutionCall) - s.Append("Resolution call dependency "); - else if (IsResolutionRoot) - s.Append("Resolution root "); - - if (FactoryID != 0) // request is with resolved factory - { - if (Reuse != DryIoc.Reuse.Transient) - s.Append(Reuse is SingletonReuse ? "singleton" : "scoped").Append(' '); - - if (FactoryType != FactoryType.Service) - s.Append(FactoryType.ToString().ToLower()).Append(' '); - - var implType = ImplementationType; - if (implType != null && implType != ServiceType) - s.Print(implType).Append(": "); - } - - s.Append(_serviceInfo); - - if (Factory != null && Factory is ReflectionFactory == false) - s.Append(' ').Append(Factory.GetType().Name).Append(' '); - - if (FactoryID != 0) - s.Append(" FactoryId=").Append(FactoryID); - - if (DecoratedFactoryID != 0) - s.Append(" decorating FactoryId=").Append(DecoratedFactoryID); - - if (!InputArgExprs.IsNullOrEmpty()) - s.AppendFormat(" with passed arguments [{0}]", InputArgExprs); - - // todo: exclude IsResolutionCall cause it is printed by above - if (Flags != default(RequestFlags)) - s.Append(" (").Append(Flags).Append(')'); - - return s; - } - - /// Prints full stack of requests starting from current one using . - public StringBuilder Print(int recursiveFactoryID = 0) - { - if (IsEmpty) - return new StringBuilder(""); - - var s = PrintCurrent(new StringBuilder()); - - s = recursiveFactoryID == 0 ? s : s.Append(" <--recursive"); - foreach (var parent in DirectParent) - { - s = parent.PrintCurrent(s.AppendLine().Append(" in ")); - if (parent.FactoryID == recursiveFactoryID) - s = s.Append(" <--recursive"); - } - - if (Container != null) - s.AppendLine().Append(" from ").Append(Container); - - return s; - } - - /// Prints whole request chain. - public override string ToString() => Print().ToString(); - - /// Returns true if request info and passed object are equal, and their parents recursively are equal. - public override bool Equals(object obj) => Equals(obj as Request); - - /// Returns true if request info and passed info are equal, and their parents recursively are equal. - public bool Equals(Request other) => - other != null && EqualsWithoutParent(other) - && (DirectParent == null && other.DirectParent == null - || (DirectParent != null && DirectParent.EqualsWithoutParent(other.DirectParent))); - - // todo: Should we include InputArgs and DecoratedFactoryID and what about flags? - // todo: Should we add and rely on Equals of ServiceInfo and Reuse? - // todo: The equals calculated differently comparing to HashCode, may be we can use FactoryID for Equals as well? - /// Compares self properties but not the parents. - public bool EqualsWithoutParent(Request other) => - other.ServiceType == ServiceType - && other.RequiredServiceType == RequiredServiceType - && other.IfUnresolved == IfUnresolved - - && other.FactoryType == FactoryType - && other.ImplementationType == ImplementationType - - && Equals(other.ServiceKey, ServiceKey) - && Equals(other.MetadataKey, MetadataKey) - && Equals(other.Metadata, Metadata) - - // todo: Move to Reuse? - && other.Reuse?.GetType() == Reuse?.GetType() - && other.Reuse?.Lifespan == Reuse?.Lifespan - && Equals(other.Reuse?.Name, Reuse?.Name); - - /// Calculates the combined hash code based on factory IDs. - public override int GetHashCode() => _hashCode; - - // Initial request without factory info yet - private Request(IContainer container, Request parent, - int dependencyDepth, RequestStack stack, RequestFlags flags, IServiceInfo serviceInfo, Expression[] inputArgExprs) - { - DirectParent = parent; - DependencyDepth = dependencyDepth; - RequestStack = stack; - - _serviceInfo = serviceInfo; - _actualServiceType = serviceInfo.GetActualServiceType(); - - Flags = flags; - - // runtime state - InputArgExprs = inputArgExprs; - Container = container; - } - - // Request with resolved factory state - private Request(IContainer container, - Request parent, int dependencyDepth, RequestStack stack, - RequestFlags flags, IServiceInfo serviceInfo, Expression[] inputArgExprs, - Type factoryImplType, Factory factory, int factoryID, FactoryType factoryType, IReuse reuse, int decoratedFactoryID) - : this(container, parent, dependencyDepth, stack, flags, serviceInfo, inputArgExprs) - { - SetResolvedFactory(factoryImplType, factory , factoryID, factoryType, reuse, decoratedFactoryID); - } - - /// Severe the connection with the request pool up to the parent so that noone can change the Request state - internal Request IsolateRequestChain() - { - Request r = null; - do - { - r = r == null ? this : r.DirectParent; - if (r.RequestStack != null) - { - // severe the requests links with the stack - r.RequestStack.Items[r.IndexInStack] = null; - r.RequestStack = null; - } - - } while ((r.Flags & RequestFlags.IsResolutionCall) == 0 && !r.DirectParent.IsEmpty); - - return this; - } - - private void SetServiceInfo(IContainer container, Request parent, - int dependencyDepth, RequestStack stack, RequestFlags flags, IServiceInfo serviceInfo, Expression[] inputArgExprs) - { - DirectParent = parent; - DependencyDepth = dependencyDepth; - RequestStack = stack; - - _serviceInfo = serviceInfo; - _actualServiceType = serviceInfo.GetActualServiceType(); - - Flags = flags; - - // runtime state - InputArgExprs = inputArgExprs; - Container = container; - - // reset factory info - SetResolvedFactory(null, null, 0, FactoryType.Service, null, 0); - } - - private void SetResolvedFactory(Type factoryImplType, - Factory factory, int factoryID, FactoryType factoryType, IReuse reuse, int decoratedFactoryID) - { - FactoryID = factoryID; - FactoryType = factoryType; - Reuse = reuse; - DecoratedFactoryID = decoratedFactoryID; - _hashCode = Hasher.Combine(DirectParent?._hashCode ?? 0, FactoryID); - - _factoryImplType = factoryImplType; // should be set from the runtime known `Factory` object - Factory = factory; // runtime state - } - } - - /// Type of services supported by Container. - public enum FactoryType - { - /// (default) Defines normal service factory - Service, - /// Defines decorator factory - Decorator, - /// Defines wrapper factory. - Wrapper - }; - - /// Base class to store optional settings. - public abstract class Setup - { - /// Factory type is required to be specified by concrete setups as in - /// , , . - public abstract FactoryType FactoryType { get; } - - /// Predicate to check if factory could be used for resolved request. - public Func Condition { get; } - - /// Relative disposal order when defined. Greater number, later dispose. - public int DisposalOrder { get; } - - /// Arbitrary metadata object associated with Factory/Implementation, may be a dictionary of key-values. - public virtual object Metadata => null; - - /// Returns true if passed meta key and value match the setup metadata. - public bool MatchesMetadata(string metadataKey, object metadata) - { - if (metadataKey == null) - return Equals(metadata, Metadata); - - object metaValue; - var metaDict = Metadata as IDictionary; - return metaDict != null - && metaDict.TryGetValue(metadataKey, out metaValue) - && Equals(metadata, metaValue); - } - - /// Indicates that injected expression should be: - /// (...)]]> - /// instead of: - public bool AsResolutionCall => (_settings & Settings.AsResolutionCall) != 0; - - /// Setup with the only setting: `AsResolutionCall` - internal static readonly Setup AsResolutionCallSetup = - new ServiceSetup { _settings = Settings.AsResolutionCall }; - - internal Setup WithAsResolutionCall() - { - if (AsResolutionCall) - return this; - - if (this == Default) - return AsResolutionCallSetup; - - var setupClone = (Setup)MemberwiseClone(); - setupClone._settings |= Settings.AsResolutionCall; - return setupClone; - } - - /// Works as `AsResolutionCall` but only with `Rules.UsedForExpressionGeneration` - public bool AsResolutionCallForExpressionGeneration => (_settings & Settings.AsResolutionCallForExpressionGeneration) != 0; - - /// Specifies to use `asResolutionCall` but only in expression generation context, e.g. for compile-time generation - internal static readonly Setup AsResolutionCallForGeneratedExpressionSetup = - new ServiceSetup { _settings = Settings.AsResolutionCallForExpressionGeneration }; - - internal Setup WithAsResolutionCallForGeneratedExpression() - { - if (AsResolutionCallForExpressionGeneration) - return this; - - if (this == Default) - return AsResolutionCallForGeneratedExpressionSetup; - - var setupClone = (Setup)MemberwiseClone(); - setupClone._settings |= Settings.AsResolutionCallForExpressionGeneration; - return setupClone; - } - - /// Marks service (not a wrapper or decorator) registration that is expected to be resolved via Resolve call. - public bool AsResolutionRoot => (_settings & Settings.AsResolutionRoot) != 0; - - /// Opens scope, also implies . - public bool OpenResolutionScope => (_settings & Settings.OpenResolutionScope) != 0; - - /// Stores reused instance as WeakReference. - public bool WeaklyReferenced => (_settings & Settings.WeaklyReferenced) != 0; - - /// Allows registering transient disposable. - public bool AllowDisposableTransient => (_settings & Settings.AllowDisposableTransient) != 0; - - /// Turns On tracking of disposable transient dependency in parent scope or in open scope if resolved directly. - public bool TrackDisposableTransient => (_settings & Settings.TrackDisposableTransient) != 0; - - /// Instructs to use parent reuse. Applied only if is not specified. - public bool UseParentReuse => (_settings & Settings.UseParentReuse) != 0; - - /// Prevents disposal of reused instance if it is disposable. - public bool PreventDisposal => (_settings & Settings.PreventDisposal) != 0; - - /// When single service is resolved, but multiple candidates found, this options will be used to prefer this one. - public bool PreferInSingleServiceResolve => (_settings & Settings.PreferInSingleServiceResolve) != 0; - - private Setup() { } - - private Setup(Func condition, - bool openResolutionScope, bool asResolutionCall, bool asResolutionRoot, bool preventDisposal, bool weaklyReferenced, - bool allowDisposableTransient, bool trackDisposableTransient, bool useParentReuse, int disposalOrder, - bool preferOverMultipleResolved = false, bool asResolutionCallForExpressionGeneration = false) - { - Condition = condition; - DisposalOrder = disposalOrder; - - if (asResolutionCall) - _settings |= Settings.AsResolutionCall; - if (openResolutionScope) - { - _settings |= Settings.OpenResolutionScope; - _settings |= Settings.AsResolutionCall; - } - if (preventDisposal) - _settings |= Settings.PreventDisposal; - if (weaklyReferenced) - _settings |= Settings.WeaklyReferenced; - if (allowDisposableTransient) - _settings |= Settings.AllowDisposableTransient; - if (trackDisposableTransient) - { - _settings |= Settings.TrackDisposableTransient; - _settings |= Settings.AllowDisposableTransient; - } - if (asResolutionRoot) - _settings |= Settings.AsResolutionRoot; - if (useParentReuse) - _settings |= Settings.UseParentReuse; - if (preferOverMultipleResolved) - _settings |= Settings.PreferInSingleServiceResolve; - if (asResolutionCallForExpressionGeneration) - _settings |= Settings.AsResolutionCallForExpressionGeneration; - } - - [Flags] - private enum Settings - { - AsResolutionCall = 1 << 1, - OpenResolutionScope = 1 << 2, - PreventDisposal = 1 << 3, - WeaklyReferenced = 1 << 4, - AllowDisposableTransient = 1 << 5, - TrackDisposableTransient = 1 << 6, - AsResolutionRoot = 1 << 7, - UseParentReuse = 1 << 8, - PreferInSingleServiceResolve = 1 << 9, - AsResolutionCallForExpressionGeneration = 1 << 10 - } - - private Settings _settings; // note: mutable because of setting the AsResolutionCall - - /// Default setup for service factories. - public static readonly Setup Default = new ServiceSetup(); - - /// Constructs setup object out of specified settings. - /// If all settings are default then setup will be returned. - /// is metadata object or Func returning metadata object. - public static Setup With( - object metadataOrFuncOfMetadata = null, Func condition = null, - bool openResolutionScope = false, bool asResolutionCall = false, bool asResolutionRoot = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - bool useParentReuse = false, int disposalOrder = 0, bool preferInSingleServiceResolve = false) - { - if (metadataOrFuncOfMetadata == null && condition == null && - !openResolutionScope && !asResolutionRoot && - !preventDisposal && !weaklyReferenced && !allowDisposableTransient && !trackDisposableTransient && - !useParentReuse && disposalOrder == 0 && !preferInSingleServiceResolve) - return !asResolutionCall ? Default : AsResolutionCallSetup; - - return new ServiceSetup(condition, - metadataOrFuncOfMetadata, openResolutionScope, asResolutionCall, asResolutionRoot, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, - useParentReuse, disposalOrder, preferInSingleServiceResolve); - } - - /// Default setup which will look for wrapped service type as single generic parameter. - public static readonly Setup Wrapper = new WrapperSetup(); - - // todo: rename to WrapperOf - /// Returns generic wrapper setup. - /// Default for is -1 for generic wrapper with single type argument. - /// Index need to be set for multiple type arguments. need to be set - /// when generic wrapper type arguments should be ignored. - public static Setup WrapperWith(int wrappedServiceTypeArgIndex = -1, - bool alwaysWrapsRequiredServiceType = false, Func unwrap = null, - bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - bool useParentReuse = false, Func condition = null, int disposalOrder = 0) => - wrappedServiceTypeArgIndex == -1 && !alwaysWrapsRequiredServiceType && unwrap == null && - !openResolutionScope && !asResolutionCall && !preventDisposal && !weaklyReferenced && - !allowDisposableTransient && !trackDisposableTransient && condition == null && disposalOrder == 0 - ? Wrapper - : new WrapperSetup(wrappedServiceTypeArgIndex, alwaysWrapsRequiredServiceType, unwrap, - condition, openResolutionScope, asResolutionCall, preventDisposal, weaklyReferenced, - allowDisposableTransient, trackDisposableTransient, useParentReuse, disposalOrder); - - /// Default decorator setup: decorator is applied to service type it registered with. - public static readonly Setup Decorator = new DecoratorSetup(); - - // todo: Make decorateeReuse a default? - /// Creates setup with optional condition. - /// The specifies relative decorator position in decorators chain. - /// Greater number means further from decoratee - specify negative number to stay closer. - /// Decorators without order (Order is 0) or with equal order are applied in registration order - /// - first registered are closer decoratee. - public static Setup DecoratorWith( - Func condition = null, int order = 0, bool useDecorateeReuse = false, - bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - int disposalOrder = 0) => - condition == null && order == 0 && !useDecorateeReuse && - !openResolutionScope && !asResolutionCall && - !preventDisposal && !weaklyReferenced && !allowDisposableTransient && !trackDisposableTransient && - disposalOrder == 0 - ? Decorator - : new DecoratorSetup(condition, order, useDecorateeReuse, openResolutionScope, asResolutionCall, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, disposalOrder); - - /// Creates a condition for both , and additional condition - public static Func GetDecorateeCondition(Type decorateeType, - object decorateeServiceKey = null, Func condition = null) - { - if (decorateeType == null && decorateeServiceKey == null) - return condition; - - Func decorateeCondition; - if (decorateeServiceKey == null) - decorateeCondition = r => r.GetKnownImplementationOrServiceType().IsAssignableTo(decorateeType); - else if (decorateeType == null) - decorateeCondition = r => decorateeServiceKey.Equals(r.ServiceKey); - else - decorateeCondition = r => decorateeServiceKey.Equals(r.ServiceKey) && - r.GetKnownImplementationOrServiceType().IsAssignableTo(decorateeType); - return condition == null ? decorateeCondition : r => decorateeCondition(r) && condition(r); - } - - /// Setup for decorator of type . - public static Setup DecoratorOf(Type decorateeType = null, - int order = 0, bool useDecorateeReuse = false, bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, bool allowDisposableTransient = false, - bool trackDisposableTransient = false, int disposalOrder = 0, object decorateeServiceKey = null) => - DecoratorWith(GetDecorateeCondition(decorateeType, decorateeServiceKey), order, useDecorateeReuse, openResolutionScope, asResolutionCall, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, disposalOrder); - - /// Setup for decorator of type . - public static Setup DecoratorOf( - int order = 0, bool useDecorateeReuse = false, bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, bool allowDisposableTransient = false, - bool trackDisposableTransient = false, int disposalOrder = 0, object decorateeServiceKey = null) => - DecoratorOf(typeof(TDecoratee), order, useDecorateeReuse, openResolutionScope, asResolutionCall, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, - disposalOrder, decorateeServiceKey); - - /// Service setup. - internal sealed class ServiceSetup : Setup - { - /// - public override FactoryType FactoryType => FactoryType.Service; - - /// Evaluates metadata if it specified as Func of object, and replaces Func with its result!. - /// Otherwise just returns metadata object. - /// Invocation of Func metadata is Not thread-safe. Please take care of that inside the Func. - public override object Metadata => - _metadataOrFuncOfMetadata is Func metaFactory - ? (_metadataOrFuncOfMetadata = metaFactory()) - : _metadataOrFuncOfMetadata; - - /// All settings are set to defaults. - public ServiceSetup() { } - - /// Specify all the individual settings. - public ServiceSetup(Func condition = null, object metadataOrFuncOfMetadata = null, - bool openResolutionScope = false, bool asResolutionCall = false, bool asResolutionRoot = false, - bool preventDisposal = false, bool weaklyReferenced = false, bool allowDisposableTransient = false, - bool trackDisposableTransient = false, bool useParentReuse = false, int disposalOrder = 0, - bool preferOverMultipleResolved = false, bool asResolutionCallForExpressionGeneration = false) - : base(condition, openResolutionScope, asResolutionCall, asResolutionRoot, - preventDisposal, weaklyReferenced, allowDisposableTransient, trackDisposableTransient, - useParentReuse, disposalOrder, preferOverMultipleResolved, asResolutionCallForExpressionGeneration) - { - _metadataOrFuncOfMetadata = metadataOrFuncOfMetadata; - } - - private object _metadataOrFuncOfMetadata; - } - - /// Setup applied for wrappers. - internal sealed class WrapperSetup : Setup - { - /// Returns type. - public override FactoryType FactoryType => FactoryType.Wrapper; - - /// Delegate to get wrapped type from provided wrapper type. - /// If wrapper is generic, then wrapped type is usually a generic parameter. - public readonly int WrappedServiceTypeArgIndex; - - /// Per name. - public readonly bool AlwaysWrapsRequiredServiceType; - - /// Delegate returning wrapped type from wrapper type. Overwrites other options. - public readonly Func Unwrap; - - /// Default setup - /// Default is -1 for generic wrapper with single type argument. - /// Need to be set for multiple type arguments. - public WrapperSetup(int wrappedServiceTypeArgIndex = -1) - { - WrappedServiceTypeArgIndex = wrappedServiceTypeArgIndex; - } - - /// Returns generic wrapper setup. - /// Default for is -1 for generic wrapper with single type argument. - /// Index need to be set for multiple type arguments. need to be set - /// when generic wrapper type arguments should be ignored. - public WrapperSetup(int wrappedServiceTypeArgIndex, bool alwaysWrapsRequiredServiceType, Func unwrap, - Func condition, - bool openResolutionScope, bool asResolutionCall, - bool preventDisposal, bool weaklyReferenced, bool allowDisposableTransient, bool trackDisposableTransient, - bool useParentReuse, int disposalOrder) - : base(condition, openResolutionScope, asResolutionCall, false, preventDisposal, weaklyReferenced, - allowDisposableTransient, trackDisposableTransient, useParentReuse, disposalOrder) - { - WrappedServiceTypeArgIndex = wrappedServiceTypeArgIndex; - AlwaysWrapsRequiredServiceType = alwaysWrapsRequiredServiceType; - Unwrap = unwrap; - } - - internal void ThrowIfInvalidRegistration(Type serviceType) - { - if (AlwaysWrapsRequiredServiceType || Unwrap != null || !serviceType.IsGeneric()) - return; - - var typeArgCount = serviceType.GetGenericParamsAndArgs().Length; - var typeArgIndex = WrappedServiceTypeArgIndex; - Throw.If(typeArgCount > 1 && typeArgIndex == -1, - Error.GenericWrapperWithMultipleTypeArgsShouldSpecifyArgIndex, serviceType); - - var index = typeArgIndex != -1 ? typeArgIndex : 0; - Throw.If(index > typeArgCount - 1, - Error.GenericWrapperTypeArgIndexOutOfBounds, serviceType, index); - } - - /// Unwraps service type or returns the as-is. - public Type GetWrappedTypeOrNullIfWrapsRequired(Type serviceType) - { - if (Unwrap != null) - return Unwrap(serviceType); - - if (AlwaysWrapsRequiredServiceType || !serviceType.IsGeneric()) - return null; - - var typeArgs = serviceType.GetGenericParamsAndArgs(); - var typeArgIndex = WrappedServiceTypeArgIndex; - serviceType.ThrowIf(typeArgs.Length > 1 && typeArgIndex == -1, - Error.GenericWrapperWithMultipleTypeArgsShouldSpecifyArgIndex); - - typeArgIndex = typeArgIndex != -1 ? typeArgIndex : 0; - serviceType.ThrowIf(typeArgIndex > typeArgs.Length - 1, - Error.GenericWrapperTypeArgIndexOutOfBounds, typeArgIndex); - - return typeArgs[typeArgIndex]; - } - } - - /// Setup applied to decorators. - internal sealed class DecoratorSetup : Setup - { - /// Returns Decorator factory type. - public override FactoryType FactoryType => FactoryType.Decorator; - - /// If provided specifies relative decorator position in decorators chain. - /// Greater number means further from decoratee - specify negative number to stay closer. - /// Decorators without order (Order is 0) or with equal order are applied in registration order - /// - first registered are closer decoratee. - public readonly int Order; - - // todo: It does not consider the keys of decorated services, - // therefore it will be shared between all services in collection - /// Instructs to use decorated service reuse. Decorated service may be decorator itself. - public readonly bool UseDecorateeReuse; - - /// Default setup. - public DecoratorSetup() { } - - /// Creates decorator setup with optional condition. applied to - /// decorated service to find that service is the decorator target. specifies - /// relative decorator position in decorators chain. Greater number means further from decoratee - - /// specify negative number to stay closer. Decorators without order (Order is 0) or with equal order - /// are applied in registration order - first registered are closer decoratee. - public DecoratorSetup(Func condition, int order, bool useDecorateeReuse, - bool openResolutionScope = false, bool asResolutionCall = false, - bool preventDisposal = false, bool weaklyReferenced = false, - bool allowDisposableTransient = false, bool trackDisposableTransient = false, - int disposalOrder = 0) - : base(condition, openResolutionScope, asResolutionCall, false, preventDisposal, weaklyReferenced, - allowDisposableTransient, trackDisposableTransient, false, disposalOrder) - { - Order = order; - UseDecorateeReuse = useDecorateeReuse; - } - } - } - - /// Facility for creating concrete factories from some template/prototype. Example: - /// creating closed-generic type reflection factory from registered open-generic prototype factory. - public interface IConcreteFactoryGenerator - { - /// Generated factories so far, identified by the service type and key pair. - ImHashMap, ReflectionFactory> GeneratedFactories { get; } - - /// Returns factory per request. May track already generated factories and return one without regenerating. - Factory GetGeneratedFactory(Request request, bool ifErrorReturnDefault = false); - } - - /// Instructs how to deal with factory result expression: - public enum FactoryCaching - { /// Is up to DryIoc to decide, - Default = 0, - /// Prevents DryIoc to set `DoNotCache`. - PleaseDontSetDoNotCache, - /// If set, the expression won't be cached - DoNotCache - } - - /// Base class for different ways to instantiate service: - /// - /// Through reflection - - /// Using custom delegate - - /// Using custom expression - - /// A placeholder for future actual implementation - - /// - /// For all of the types Factory should provide result as and . - /// Factories are supposed to be immutable and stateless. - /// Each created factory has an unique ID set in . - public abstract class Factory - { - /// Get next factory ID in a atomic way.The ID. - public static int GetNextID() => Interlocked.Increment(ref _lastFactoryID); - - /// Unique factory id generated from static seed. - public int FactoryID { get; internal set; } - - /// Reuse policy for created services. - public virtual IReuse Reuse => _reuse; - - /// Setup may contain different/non-default factory settings. - public virtual Setup Setup - { - get => _setup; - internal set { _setup = value ?? Setup.Default; } - } - - /// Checks that condition is met for request or there is no condition setup. - public bool CheckCondition(Request request) => Setup.Condition?.Invoke(request) != false; - - /// Shortcut for . - public FactoryType FactoryType => Setup.FactoryType; - - /// Non-abstract closed implementation type. May be null if not known beforehand, e.g. in . - public virtual Type ImplementationType => null; - - /// Allow inheritors to define lazy implementation type - public virtual bool CanAccessImplementationType => true; - - /// Indicates that Factory is factory provider and - /// consumer should call to get concrete factory. - public virtual IConcreteFactoryGenerator FactoryGenerator => null; - - /// Registration order. - public virtual int RegistrationOrder => FactoryID; - - /// Settings (if any) to select Constructor/FactoryMethod, Parameters, Properties and Fields. - public virtual Made Made => Made.Default; - - /// The factory inserts the runtime-state into result expression, e.g. delegate or pre-created instance. - public virtual bool HasRuntimeState => false; - - /// Indicates how to deal with the result expression - public FactoryCaching Caching { get; set; } - - /// Instructs to skip caching the factory unless it really wants to do so via `PleaseDontSetDoNotCache` - public Factory DoNotCache() - { - if (Caching != FactoryCaching.PleaseDontSetDoNotCache) - Caching = FactoryCaching.DoNotCache; - return this; - } - - /// Initializes reuse and setup. Sets the - /// (optional) (optional) - protected Factory(IReuse reuse = null, Setup setup = null) - { - FactoryID = GetNextID(); - _reuse = reuse; - _setup = setup ?? Setup.Default; - } - - /// The main factory method to create service expression, e.g. "new Client(new Service())". - /// If has specified, they could be used in expression. - /// Service request. - /// Created expression. - public abstract Expression CreateExpressionOrDefault(Request request); - - /// Returns service expression: either by creating it with or taking expression from cache. - /// Before returning method may transform the expression by applying , or/and decorators if found any. - public virtual Expression GetExpressionOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - - // First look for decorators if it is not already a decorator - var container = request.Container; - if (FactoryType != FactoryType.Decorator) - { - var decoratorExpr = container.GetDecoratorExpressionOrDefault(request); - if (decoratorExpr != null) - return decoratorExpr; - } - - // Then optimize for already resolved singleton object, otherwise goes normal ApplyReuse route - var setup = Setup; - var rules = container.Rules; - if (rules.EagerCachingSingletonForFasterAccess && - request.Reuse is SingletonReuse && !setup.PreventDisposal && !setup.WeaklyReferenced) - { - var itemRef = ((Scope)container.SingletonScope)._maps[FactoryID & Scope.MAP_COUNT_SUFFIX_MASK].GetEntryOrDefault(FactoryID); - if (itemRef != null && itemRef.Value != Scope.NoItem) - return Constant(itemRef.Value); - } - - if ((request.Flags & RequestFlags.IsGeneratedResolutionDependencyExpression) == 0 && - !request.OpensResolutionScope && ( - setup.OpenResolutionScope || - !request.IsResolutionCall && ( - setup.AsResolutionCall || - setup.AsResolutionCallForExpressionGeneration && rules.UsedForExpressionGeneration || - setup.UseParentReuse || - request.FactoryType == FactoryType.Service && request.DependencyDepth > rules.DependencyDepthToSplitObjectGraph - ) && - request.GetActualServiceType() != typeof(void)) - ) - return Resolver.CreateResolutionExpression(request, setup.OpenResolutionScope); - - var mayCache = Caching != FactoryCaching.DoNotCache && - FactoryType == FactoryType.Service && - !request.IsResolutionRoot && - (!request.IsSingletonOrDependencyOfSingleton || rules.UsedForValidation) && - !request.IsDirectlyWrappedInFunc() && - !request.IsWrappedInFuncWithArgs() && - !setup.UseParentReuse && - !Made.IsConditional; - - ImMapEntry cacheEntry = null; - if (mayCache) - { - var cachedExpr = ((Container)container).GetCachedFactoryExpression(FactoryID, request, out cacheEntry); - if (cachedExpr != null) - return cachedExpr; - } - - // Creates an object graph expression with all of the dependencies created - var serviceExpr = CreateExpressionOrDefault(request); - if (serviceExpr != null) - { - if (request.Reuse != DryIoc.Reuse.Transient && - request.GetActualServiceType() != typeof(void) && - // we don't need the reuse expression when we are validating the object graph - !rules.UsedForValidation) - { - var originalServiceExprType = serviceExpr.Type; - - serviceExpr = ApplyReuse(serviceExpr, request); - - if (serviceExpr.Type != originalServiceExprType) - serviceExpr = Convert(serviceExpr, originalServiceExprType); - } - - if (mayCache) - ((Container)container).CacheFactoryExpression(FactoryID, request, serviceExpr, cacheEntry); - } - else Container.TryThrowUnableToResolve(request); - return serviceExpr; - } - - /// Applies reuse to created expression, by wrapping passed expression into scoped access - /// and producing the result expression. - protected virtual Expression ApplyReuse(Expression serviceExpr, Request request) - { - // Optimization: eagerly creates a singleton during the construction of object graph - if (request.Reuse is SingletonReuse && - request.Rules.EagerCachingSingletonForFasterAccess && - !request.TracksTransientDisposable && - !request.IsWrappedInFunc()) - { - var container = request.Container; - var scope = (Scope)container.SingletonScope; - if (scope.IsDisposed) - Throw.It(Error.ScopeIsDisposed, scope.ToString()); - - var factoryId = FactoryID; - ref var map = ref scope._maps[factoryId & Scope.MAP_COUNT_SUFFIX_MASK]; - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(factoryId, Scope.NoItem), m) != m) - Ref.Swap(ref map, factoryId, (x, i) => x.AddOrKeep(i, Scope.NoItem)); - - var itemRef = map.GetEntryOrDefault(factoryId); - if (itemRef.Value == Scope.NoItem) - { - object singleton = null; - lock (itemRef) - { - if (itemRef.Value == Scope.NoItem) - { - var useFec = container.Rules.UseFastExpressionCompiler; - if (!Interpreter.TryInterpretAndUnwrapContainerException(container, serviceExpr, useFec, out singleton)) - singleton = serviceExpr.CompileToFactoryDelegate(useFec, container.Rules.UseInterpretation)(container); - - if (Setup.WeaklyReferenced) - singleton = new WeakReference(singleton); - else if (Setup.PreventDisposal) // todo: we don't need it here because because we just don't need to AddDisposable - singleton = new HiddenDisposable(singleton); - - itemRef.Value = singleton; - } - } - - if (singleton is IDisposable disp && disp != this) - { - if (Setup.DisposalOrder == 0) - scope.AddUnorderedDisposable(disp); - else - scope.AddDisposable(disp, Setup.DisposalOrder); - } - } - - serviceExpr = Constant(itemRef.Value); - } - else - { - // Wrap service expression in WeakReference or HiddenDisposable - if (Setup.WeaklyReferenced) - serviceExpr = New(typeof(WeakReference).Constructor(typeof(object)), serviceExpr); - else if (Setup.PreventDisposal) - serviceExpr = New(HiddenDisposable.Ctor, serviceExpr); - - serviceExpr = request.Reuse.Apply(request, serviceExpr); - } - - // Unwrap WeakReference or HiddenDisposable - if (Setup.WeaklyReferenced) - { - serviceExpr = Call( - typeof(ThrowInGeneratedCode).GetTypeInfo().GetDeclaredMethod(nameof(ThrowInGeneratedCode.WeakRefReuseWrapperGCed)), - Property(Convert(serviceExpr, typeof(WeakReference)), - typeof(WeakReference).Property(nameof(WeakReference.Target)))); - } - else if (Setup.PreventDisposal) - { - serviceExpr = Field(Convert(serviceExpr, typeof(HiddenDisposable)), HiddenDisposable.ValueField); - } - - return serviceExpr; - } - - // todo: remove this - /// [Obsolete("Not need to control on the factory level, the remaining UseInstanceFactory will be removed")] - public virtual bool UseInterpretation(Request request) => request.Rules.UseInterpretationForTheFirstResolution; - - /// Creates factory delegate from service expression and returns it. - public virtual FactoryDelegate GetDelegateOrDefault(Request request) => - GetExpressionOrDefault(request) - ?.CompileToFactoryDelegate(request.Rules.UseFastExpressionCompiler, request.Rules.UseInterpretation); - - internal virtual bool ValidateAndNormalizeRegistration(Type serviceType, object serviceKey, bool isStaticallyChecked, Rules rules) - { - if (!isStaticallyChecked) - serviceType.ThrowIfNull(); - - var setup = Setup; - if (setup.FactoryType == FactoryType.Service) - { - // Warn about registering disposable transient - var reuse = Reuse ?? rules.DefaultReuse; - if (reuse != DryIoc.Reuse.Transient) - return true; - - if (setup.AllowDisposableTransient || - !rules.ThrowOnRegisteringDisposableTransient) - return true; - - if (setup.UseParentReuse || - setup.FactoryType == FactoryType.Decorator && ((Setup.DecoratorSetup)setup).UseDecorateeReuse) - return true; - - var knownImplOrServiceType = CanAccessImplementationType ? ImplementationType : serviceType; - if (knownImplOrServiceType.IsAssignableTo()) - Throw.It(Error.RegisteredDisposableTransientWontBeDisposedByContainer, - serviceType, serviceKey ?? "{no key}", this); - } - else if (setup.FactoryType == FactoryType.Wrapper) - { - ((Setup.WrapperSetup)setup).ThrowIfInvalidRegistration(serviceType); - } - else if (setup.FactoryType == FactoryType.Decorator) - { - if (serviceKey != null) - Throw.It(Error.DecoratorShouldNotBeRegisteredWithServiceKey, serviceKey); - } - - return true; - } - - /// Returns nice string representation of factory. - public override string ToString() - { - var s = new StringBuilder().Append("{FactoryID=").Append(FactoryID); - if (ImplementationType != null) - s.Append(", ImplType=").Print(ImplementationType); - if (Reuse != null) - s.Append(", Reuse=").Print(Reuse); - if (Setup.FactoryType != Setup.Default.FactoryType) - s.Append(", FactoryType=").Append(Setup.FactoryType); - if (Setup.Metadata != null) - s.Append(", Metadata=").Print(Setup.Metadata); - if (Setup.Condition != null) - s.Append(", HasCondition"); - - if (Setup.OpenResolutionScope) - s.Append(", OpensResolutionScope"); - else if (Setup.AsResolutionCall) - s.Append(", AsResolutionCall"); - - return s.Append("}").ToString(); - } - -#region Implementation - - internal static int _lastFactoryID; - private IReuse _reuse; - private Setup _setup; - -#endregion - } - - /// Declares delegate to get single factory method or constructor for resolved request. - public delegate FactoryMethod FactoryMethodSelector(Request request); - - /// Specifies how to get parameter info for injected parameter and resolved request - public delegate Func ParameterSelector(Request request); - - /// Specifies what properties or fields to inject and how. - public delegate IEnumerable PropertiesAndFieldsSelector(Request request); - - /// DSL for specifying injection rules. - public static class Parameters - { - /// Returns default service info wrapper for each parameter info. - public static ParameterSelector Of = request => ParameterServiceInfo.Of; - - /// Returns service info which considers each parameter as optional. - public static ParameterSelector IfUnresolvedReturnDefault = - request => pi => ParameterServiceInfo.Of(pi).WithDetails(ServiceDetails.IfUnresolvedReturnDefault); - - /// Combines source selector with other. Other is used as fallback when source returns null. - public static ParameterSelector OverrideWith(this ParameterSelector source, ParameterSelector other) => - source == null || source == Of ? other ?? Of - : other == null || other == Of ? source - : req => paramInfo => other(req)?.Invoke(paramInfo) ?? source(req)?.Invoke(paramInfo); - - /// Obsolete: please use - [Obsolete("Replaced with OverrideWith", false)] - public static ParameterSelector And(this ParameterSelector source, ParameterSelector other) => - source.OverrideWith(other); - - /// Overrides source parameter rules with specific parameter details. - /// If it is not your parameter just return null. - /// Original parameters rules - /// Should return specific details or null. - /// New parameters rules. - public static ParameterSelector Details(this ParameterSelector source, Func getDetailsOrNull) - { - getDetailsOrNull.ThrowIfNull(); - return source.OverrideWith(request => p => getDetailsOrNull(request, p)?.To(ParameterServiceInfo.Of(p).WithDetails)); - } - - /// Adds to selector service info for parameter identified by . - /// Original parameters rules. Name to identify parameter. - /// (optional) (optional) - /// (optional) By default throws exception if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// New parameters rules. - public static ParameterSelector Name(this ParameterSelector source, string name, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Details((r, p) => !p.Name.Equals(name) ? null - : ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata)); - - /// Specify parameter by name and set custom value to it. - public static ParameterSelector Name(this ParameterSelector source, - string name, Func getServiceDetails) => - source.Details((r, p) => p.Name.Equals(name) ? getServiceDetails(r, p) : null); - - /// Specify parameter by name and set custom value to it. - public static ParameterSelector Name(this ParameterSelector source, - string name, Func getCustomValue) => - source.Name(name, (r, p) => ServiceDetails.Of(getCustomValue(r))); - - /// Adds to selector service info for parameter identified by type . - /// Source selector. The type of the parameter. - /// (optional) (optional) - /// (optional) By default throws exception if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// Combined selector. - public static ParameterSelector Type(this ParameterSelector source, Type parameterType, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Details((r, p) => !parameterType.IsAssignableTo(p.ParameterType) ? null - : ServiceDetails.Of(requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata)); - - /// Adds to selector service info for parameter identified by type . - /// Type of parameter. Source selector. - /// (optional) (optional) - /// (optional) By default throws exception if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// Combined selector. - public static ParameterSelector Type(this ParameterSelector source, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.Throw, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Type(typeof(T), requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata); - - /// Specify parameter by type and set its details. - public static ParameterSelector Type(this ParameterSelector source, - Func getServiceDetails) => - source.Details((r, p) => p.ParameterType == typeof(T) ? getServiceDetails(r, p) : null); - - /// Specify parameter by type and set custom value to it. - public static ParameterSelector Type(this ParameterSelector source, Func getCustomValue) => - source.Type((r, p) => ServiceDetails.Of(getCustomValue(r))); - - /// Specify parameter by type and set custom value to it. - /// Original parameters rules. - /// The type of the parameter. - /// Custom value provider. - /// New parameters rules. - public static ParameterSelector Type(this ParameterSelector source, - Type parameterType, Func getCustomValue) => - source.Details((r, p) => p.ParameterType == parameterType ? ServiceDetails.Of(getCustomValue(r)) : null); - } - - /// DSL for specifying injection rules. - public static partial class PropertiesAndFields - { - /// Say to not resolve any properties or fields. - public static PropertiesAndFieldsSelector Of = request => null; - - /// Public assignable instance members of any type except object, string, primitives types, and arrays of those. - public static PropertiesAndFieldsSelector Auto = All(withNonPublic: false, withPrimitive: false); - - /// Public, declared, assignable, non-primitive properties. - public static PropertiesAndFieldsSelector Properties( - bool withNonPublic = false, bool withBase = false, - IfUnresolved ifUnresolved = IfUnresolved.ReturnDefaultIfNotRegistered) => - All(withNonPublic: withNonPublic, withPrimitive: false, withFields: false, withBase: withBase, ifUnresolved: ifUnresolved); - - /// Should return service info for input member (property or field). - public delegate PropertyOrFieldServiceInfo GetServiceInfo(MemberInfo member, Request request); - - /// Generates selector property and field selector with settings specified by parameters. - /// If all parameters are omitted the return all public not primitive members. - public static PropertiesAndFieldsSelector All( - bool withNonPublic = true, - bool withPrimitive = true, - bool withFields = true, - bool withBase = true, - IfUnresolved ifUnresolved = IfUnresolved.ReturnDefaultIfNotRegistered, - GetServiceInfo serviceInfo = null) - { - GetServiceInfo info = (m, r) => - serviceInfo != null ? serviceInfo(m, r) : - PropertyOrFieldServiceInfo.Of(m).WithDetails(ServiceDetails.Of(ifUnresolved: ifUnresolved)); - - return req => - { - var properties = req.ImplementationType.GetMembers(x => x.DeclaredProperties, includeBase: withBase) - .Match(p => p.IsInjectable(withNonPublic, withPrimitive), p => info(p, req)); - - if (!withFields) - return properties; - - var fields = req.ImplementationType - .GetMembers(x => x.DeclaredFields, includeBase: withBase) - .Match(f => f.IsInjectable(withNonPublic, withPrimitive), f => info(f, req)); - - return properties.Append(fields); - }; - } - - /// Combines source properties and fields with other. Other will override the source condition. - /// Source selector. Specific other selector to add. - /// Combined result selector. - public static PropertiesAndFieldsSelector OverrideWith( - this PropertiesAndFieldsSelector source, PropertiesAndFieldsSelector other) - { - return source == null || source == Of ? (other ?? Of) - : other == null || other == Of ? source - : r => - { - var sourceMembers = source(r).ToArrayOrSelf(); - var otherMembers = other(r).ToArrayOrSelf(); - return sourceMembers == null || sourceMembers.Length == 0 ? otherMembers - : otherMembers == null || otherMembers.Length == 0 ? sourceMembers - : otherMembers.Append( - sourceMembers.Match(otherMembers, (om, s) => s != null && om.All(o => o == null || !s.Member.Name.Equals(o.Member.Name)))); - }; - } - - /// Obsolete: please use - [Obsolete("Replaced with OverrideWith", false)] - public static PropertiesAndFieldsSelector And( - this PropertiesAndFieldsSelector source, PropertiesAndFieldsSelector other) => - source.OverrideWith(other); - - /// Specifies service details (key, if-unresolved policy, required type) for property/field with the name. - /// Original member selector. Member name. Details. - /// New selector. - public static PropertiesAndFieldsSelector Details(this PropertiesAndFieldsSelector source, - string name, Func getDetails) - { - name.ThrowIfNull(); - getDetails.ThrowIfNull(); - return source.OverrideWith(req => - { - var implType = req.GetKnownImplementationOrServiceType(); - - var property = implType - .GetMembers(x => x.DeclaredProperties, includeBase: true) - .FindFirst(x => x.Name == name); - if (property != null && property.IsInjectable(true, true)) - return getDetails(req)?.To(PropertyOrFieldServiceInfo.Of(property).WithDetails).One(); - - var field = implType - .GetMembers(x => x.DeclaredFields, includeBase: true) - .FindFirst(x => x.Name == name); - if (field != null && field.IsInjectable(true, true)) - return getDetails(req)?.To(PropertyOrFieldServiceInfo.Of(field).WithDetails).One(); - - return Throw.For>( - Error.NotFoundSpecifiedWritablePropertyOrField, name, req); - }); - } - - /// Adds to selector service info for property/field identified by . - /// Source selector. Name to identify member. - /// (optional) (optional) - /// (optional) By default returns default value if unresolved. - /// (optional) Specifies default value to use when unresolved. - /// (optional) Required metadata key Required metadata or value. - /// Combined selector. - public static PropertiesAndFieldsSelector Name(this PropertiesAndFieldsSelector source, string name, - Type requiredServiceType = null, object serviceKey = null, - IfUnresolved ifUnresolved = IfUnresolved.ReturnDefault, object defaultValue = null, - string metadataKey = null, object metadata = null) => - source.Details(name, r => ServiceDetails.Of( - requiredServiceType, serviceKey, ifUnresolved, defaultValue, metadataKey, metadata)); - - /// Specifies custom value for property/field with specific name. - public static PropertiesAndFieldsSelector Name(this PropertiesAndFieldsSelector source, - string name, Func getCustomValue) => - source.Details(name, r => ServiceDetails.Of(getCustomValue(r))); - - /// Returns true if property matches flags provided. - /// Property to match - /// Says to include non public properties. - /// Says to include properties of primitive type. - /// True if property is matched and false otherwise. - public static bool IsInjectable(this PropertyInfo property, - bool withNonPublic = false, bool withPrimitive = false) - { - if (!property.CanWrite || property.IsExplicitlyImplemented()) - return false; - - if (property.IsStatic()) - return false; - - return !property.IsIndexer() && - (withNonPublic || property.GetSetMethodOrNull() != null) && - (withPrimitive || !property.PropertyType.IsPrimitive(orArrayOfPrimitives: true)); - } - - /// Returns true if field matches flags provided. - /// Field to match. - /// Says to include non public fields. - /// Says to include fields of primitive type. - /// True if property is matched and false otherwise. - public static bool IsInjectable(this FieldInfo field, - bool withNonPublic = false, bool withPrimitive = false) => - !field.IsInitOnly && !field.IsBackingField() - && (withNonPublic || field.IsPublic) - && (withPrimitive || !field.FieldType.IsPrimitive(orArrayOfPrimitives: true)); - } - - /// Reflects on constructor parameters and members, - /// creates expression for each reflected dependency, and composes result service expression. - public sealed class ReflectionFactory : Factory - { - /// Non-abstract service implementation type. May be open generic. - public override Type ImplementationType - { - get - { - if (_implementationType == null && _implementationTypeProvider != null) - SetKnownImplementationType(_implementationTypeProvider(), Made); - return _implementationType; - } - } - - /// False for lazy implementation type, to prevent its early materialization. - public override bool CanAccessImplementationType => - _implementationType != null || _implementationTypeProvider == null; - - /// Provides closed-generic factory for registered open-generic variant. - public override IConcreteFactoryGenerator FactoryGenerator => _factoryGenerator; - - /// Injection rules set for Constructor/FactoryMethod, Parameters, Properties and Fields. - public override Made Made => _made; - - /// FactoryID of generator (open-generic) factory. - public int GeneratorFactoryID { get; private set; } - - /// Will contain factory ID of generator's factory for generated factory. - public override int RegistrationOrder => GeneratorFactoryID != 0 ? GeneratorFactoryID : FactoryID; - - /// Creates factory providing implementation type, optional reuse and setup. - /// (optional) Optional if Made.FactoryMethod is present Non-abstract close or open generic type. - /// (optional) (optional) (optional) - public ReflectionFactory(Type implementationType = null, IReuse reuse = null, Made made = null, Setup setup = null) - : base(reuse, setup) - { - _made = made ?? Made.Default; - SetKnownImplementationType(implementationType, _made); - } - - /// Creates factory providing implementation type, optional reuse and setup. - /// Provider of non-abstract closed or open-generic type. - /// (optional) (optional) (optional) - public ReflectionFactory(Func implementationTypeProvider, IReuse reuse = null, Made made = null, Setup setup = null) - : base(reuse, setup) - { - _made = made ?? Made.Default; - _implementationTypeProvider = implementationTypeProvider.ThrowIfNull(); - } - - /// Creates service expression. - public override Expression CreateExpressionOrDefault(Request request) - { - var container = request.Container; - var rules = container.Rules; - - FactoryMethod factoryMethod; - var factoryMethodSelector = Made.FactoryMethod ?? rules.FactoryMethod; - if (factoryMethodSelector == null) - { - factoryMethod = new FactoryMethod(_knownSingleCtor ?? request.ImplementationType.SingleConstructor()); - } - else if ((factoryMethod = factoryMethodSelector(request)) == null) - return Throw.For(request.IfUnresolved != IfUnresolved.ReturnDefault, - Error.UnableToSelectCtor, request.ImplementationType, request); - - // If factory method is the method of some registered service, then resolve factory service first. - var factoryExpr = factoryMethod.FactoryExpression; - if (factoryExpr == null && factoryMethod.FactoryServiceInfo != null) - { - var factoryRequest = request.Push(factoryMethod.FactoryServiceInfo); - factoryExpr = container.ResolveFactory(factoryRequest)?.GetExpressionOrDefault(factoryRequest); - if (factoryExpr == null) - return null; - } - - var ctorOrMember = factoryMethod.ConstructorOrMethodOrMember; - var ctorOrMethod = ctorOrMember as MethodBase; - if (ctorOrMethod == null) // return early when factory is Property or Field - { - var memberExpr = ctorOrMember is PropertyInfo - ? Property(factoryExpr, (PropertyInfo)ctorOrMember) - : (Expression)Field(factoryExpr, (FieldInfo)ctorOrMember); - return ConvertExpressionIfNeeded(memberExpr, request, ctorOrMember); - } - - Expression arg0 = null, arg1 = null, arg2 = null, arg3 = null, arg4 = null; - Expression[] paramExprs = Empty(); - var parameters = ctorOrMethod.GetParameters(); - if (parameters.Length != 0) - { - paramExprs = factoryMethod.ResolvedParameterExpressions; - if (paramExprs == null) - { - if (parameters.Length > 5) - paramExprs = new Expression[parameters.Length]; - - var paramSelector = rules.OverrideRegistrationMade - ? Made.Parameters.OverrideWith(rules.Parameters) - : rules.Parameters.OverrideWith(Made.Parameters); - var paramServiceInfoSelector = paramSelector(request); - - var inputArgs = request.InputArgExprs; - var argsUsedMask = 0; - for (var i = 0; i < parameters.Length; i++) - { - var param = parameters[i]; - if (inputArgs != null) - { - var inputArgExpr = TryGetExpressionFromInputArgs(param.ParameterType, inputArgs, ref argsUsedMask); - if (inputArgExpr != null) - { - if (paramExprs != null) - paramExprs[i] = inputArgExpr; - else if (i == 0) - arg0 = inputArgExpr; - else if (i == 1) - arg1 = inputArgExpr; - else if (i == 2) - arg2 = inputArgExpr; - else if (i == 3) - arg3 = inputArgExpr; - else - arg4 = inputArgExpr; - continue; - } - } - - var paramInfo = paramServiceInfoSelector(param) ?? ParameterServiceInfo.Of(param); - var paramRequest = request.Push(paramInfo); - var paramDetails = paramInfo.Details; - var usedOrCustomValExpr = TryGetUsedInstanceOrCustomValueExpression(request, paramRequest, paramDetails); - if (usedOrCustomValExpr != null) - { - if (paramExprs != null) - paramExprs[i] = usedOrCustomValExpr; - else if (i == 0) - arg0 = usedOrCustomValExpr; - else if (i == 1) - arg1 = usedOrCustomValExpr; - else if (i == 2) - arg2 = usedOrCustomValExpr; - else if (i == 3) - arg3 = usedOrCustomValExpr; - else - arg4 = usedOrCustomValExpr; - continue; - } - - var injectedExpr = container.ResolveFactory(paramRequest)?.GetExpressionOrDefault(paramRequest); - if (injectedExpr == null || - // When param is an empty array / collection, then we may use a default value instead (#581) - paramDetails.DefaultValue != null && - injectedExpr.NodeType == System.Linq.Expressions.ExpressionType.NewArrayInit && - ((NewArrayExpression) injectedExpr).Expressions.Count == 0) - { - // Check if parameter dependency itself (without propagated parent details) - // does not allow default, then stop checking the rest of parameters. - if (paramDetails.IfUnresolved == IfUnresolved.Throw) - return null; - injectedExpr = paramDetails.DefaultValue != null - ? container.GetConstantExpression(paramDetails.DefaultValue) - : paramRequest.ServiceType.GetDefaultValueExpression(); - } - - if (paramExprs != null) - paramExprs[i] = injectedExpr; - else if (i == 0) - arg0 = injectedExpr; - else if (i == 1) - arg1 = injectedExpr; - else if (i == 2) - arg2 = injectedExpr; - else if (i == 3) - arg3 = injectedExpr; - else - arg4 = injectedExpr; - } - } - } - - if (rules.UsedForValidation) - return request.GetActualServiceType().GetDefaultValueExpression(); - - var ctor = ctorOrMethod as ConstructorInfo; - Expression serviceExpr; - if (arg0 == null) - serviceExpr = ctor != null ? New(ctor, paramExprs) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, paramExprs); - else if (arg1 == null) - serviceExpr = ctor != null ? New(ctor, arg0) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0); - else if (arg2 == null) - serviceExpr = ctor != null ? New(ctor, arg0, arg1) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1); - else if (arg3 == null) - serviceExpr = ctor != null ? New(ctor, arg0, arg1, arg2) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1, arg2); - else if (arg4 == null) - serviceExpr = ctor != null ? New(ctor, arg0, arg1, arg2, arg3) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1, arg2, arg3); - else - serviceExpr = ctor != null ? New(ctor, arg0, arg1, arg2, arg3, arg4) : (Expression)Call(factoryExpr, (MethodInfo)ctorOrMethod, arg0, arg1, arg2, arg3, arg4); - - if (ctor == null) - return ConvertExpressionIfNeeded(serviceExpr, request, ctorOrMember); - - if (rules.PropertiesAndFields == null && Made.PropertiesAndFields == null) - return serviceExpr; - - return CreateMemberInitExpression(serviceExpr, request); - } - - private Expression CreateMemberInitExpression(Expression serviceExpr, Request request) - { - var container = request.Container; - var rules = container.Rules; - var propertiesAndFieldsSelector = rules.OverrideRegistrationMade - ? Made.PropertiesAndFields.OverrideWith(rules.PropertiesAndFields) - : rules.PropertiesAndFields.OverrideWith(Made.PropertiesAndFields); - var propertiesAndFields = propertiesAndFieldsSelector.Invoke(request); - if (propertiesAndFields == null) - return serviceExpr; - - var assignments = Empty(); - foreach (var member in propertiesAndFields) - if (member != null) - { - var memberRequest = request.Push(member); - var memberExpr = - TryGetUsedInstanceOrCustomValueExpression(request, memberRequest, member.Details) - ?? container.ResolveFactory(memberRequest)?.GetExpressionOrDefault(memberRequest); - if (memberExpr != null) - assignments = assignments.AppendOrUpdate(Bind(member.Member, memberExpr)); - else if (request.IfUnresolved == IfUnresolved.ReturnDefault) - return null; - } - - return assignments.Length == 0 ? serviceExpr : MemberInit((NewExpression) serviceExpr, assignments); - } - - private static Expression ConvertExpressionIfNeeded(Expression serviceExpr, Request request, MemberInfo ctorOrMember) - { - var actualServiceType = request.GetActualServiceType(); - var serviceExprType = serviceExpr.Type; - if (serviceExprType == typeof(object)) - return Convert(serviceExpr, actualServiceType); - return serviceExprType == actualServiceType || serviceExprType.IsAssignableTo(actualServiceType) ? serviceExpr - : serviceExprType.HasConversionOperatorTo(actualServiceType) ? Convert(serviceExpr, actualServiceType) - : request.IfUnresolved != IfUnresolved.Throw ? null - : Throw.For(Error.ServiceIsNotAssignableFromFactoryMethod, actualServiceType, ctorOrMember, - request); - } - - // Check not yet used arguments provided via `Func` or `Resolve(.., args: new[] { arg })` - internal static Expression TryGetExpressionFromInputArgs(Type paramType, Expression[] inputArgs, ref int argsUsedMask) - { - for (var a = 0; a < inputArgs.Length; ++a) - if ((argsUsedMask & 1 << a) == 0 && inputArgs[a].Type.IsAssignableTo(paramType)) - { - argsUsedMask |= 1 << a; // mark that argument was used - return inputArgs[a]; - } - return null; - } - - internal static Expression TryGetUsedInstanceOrCustomValueExpression(Request request, Request paramRequest, ServiceDetails paramDetails) - { - if (paramDetails == DryIoc.ServiceDetails.Default) - { - // Generate the fast resolve call for used instances - if (request.Container.TryGetUsedInstance(paramRequest.ServiceType, out var instance)) - return Call(ResolverContext.GetRootOrSelfExpr(paramRequest), Resolver.ResolveFastMethod, - Constant(paramRequest.ServiceType, typeof(Type)), Constant(paramRequest.IfUnresolved)); - return null; - } - - if (paramDetails.HasCustomValue) - { - var serviceType = paramRequest.ServiceType; - var hasConversionOperator = false; - var customValue = paramDetails.CustomValue; - if (customValue != null) - { - var customTypeValue = customValue.GetType(); - if (!customTypeValue.IsArray && - !customTypeValue.IsAssignableTo(serviceType) && - !(hasConversionOperator = customTypeValue.HasConversionOperatorTo(serviceType))) - return Throw.For(paramRequest.IfUnresolved != IfUnresolved.ReturnDefault, - Error.InjectedCustomValueIsOfDifferentType, customValue, serviceType, paramRequest); - } - - return hasConversionOperator - ? Convert(request.Container.GetConstantExpression(customValue), serviceType) - : request.Container.GetConstantExpression(customValue, serviceType); - } - - return null; - } - - internal override bool ValidateAndNormalizeRegistration(Type serviceType, object serviceKey, bool isStaticallyChecked, Rules rules) - { - base.ValidateAndNormalizeRegistration(serviceType, serviceKey, isStaticallyChecked, rules); - - if (!CanAccessImplementationType) - return true; - - var implType = ImplementationType; - if (Made.FactoryMethod == null && rules.FactoryMethod == null) - { - var ctors = implType.GetTypeInfo().DeclaredConstructors.ToArrayOrSelf(); - var ctorCount = 0; - for (var i = 0; ctorCount != 2 && i < ctors.Length; i++) - { - var ctor = ctors[i]; - if (ctor.IsPublic && !ctor.IsStatic) - { - ++ctorCount; - _knownSingleCtor = ctor; - } - } - - if (ctorCount == 0) - Throw.It(Error.UnableToSelectSinglePublicConstructorFromNone, implType); - else if (ctorCount > 1) - Throw.It(Error.UnableToSelectSinglePublicConstructorFromMultiple, implType, ctors); - } - - if (isStaticallyChecked || implType == null) - return true; - - var implTypeInfo = implType.GetTypeInfo(); - if (!implTypeInfo.IsGenericTypeDefinition) - { - if (implTypeInfo.IsGenericType && !ReflectionTools.AreAllTypeArgumentsClosed(implTypeInfo.GetGenericParamsAndArgsUnsafe())) - Throw.It(Error.RegisteringNotAGenericTypedefImplType, implType, implType.GetGenericTypeDefinition()); - - else if (implType != serviceType && serviceType != typeof(object)) - { - if (!serviceType.IsGenericDefinition()) - { - if (!implType.IsImplementingServiceType(serviceType)) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - } - else - { - if (implType.GetImplementedTypes().IndexOf(serviceType, (st, t) => t == st || t.GetGenericDefinitionOrNull() == st) == -1) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - } - } - } - else if (implType != serviceType) // implTypeInfo.IsGenericTypeDefinition - { - var serviceTypeInfo = serviceType.GetTypeInfo(); - if (serviceTypeInfo.IsGenericTypeDefinition) - { - var implTypeParams = implTypeInfo.GetGenericParamsAndArgsUnsafe(); - var implementedTypes = implType.GetImplementedTypes(); - - var implementedTypeFound = false; - var containsAllTypeParams = false; - for (var i = 0; !containsAllTypeParams && i < implementedTypes.Length; ++i) - { - var implementedType = implementedTypes[i]; - implementedTypeFound = implementedType.GetGenericDefinitionOrNull() == serviceType; - containsAllTypeParams = implementedTypeFound && implementedType.ContainsAllGenericTypeParameters(implTypeParams); - } - - if (!implementedTypeFound) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - - if (!containsAllTypeParams) - Throw.It(Error.RegisteringOpenGenericServiceWithMissingTypeArgs, - implType, serviceType, implementedTypes.Match(t => t.GetGenericDefinitionOrNull() == serviceType)); - } - - else if (serviceTypeInfo.IsGenericType && !ReflectionTools.AreAllTypeArgumentsClosed(serviceTypeInfo.GetGenericParamsAndArgsUnsafe())) - Throw.It(Error.RegisteringNotAGenericTypedefServiceType, - serviceType, serviceType.GetGenericTypeDefinition()); - - else if (!serviceTypeInfo.IsGenericType) - Throw.It(Error.RegisteringOpenGenericImplWithNonGenericService, implType, serviceType); - - else if (!implType.IsImplementingServiceType(serviceType.GetGenericTypeDefinition())) - Throw.It(Error.RegisteringImplementationNotAssignableToServiceType, implType, serviceType); - } - - return true; - } - -#region Implementation - - private Type _implementationType; // non-readonly to be set by lazy type provider - private readonly Func _implementationTypeProvider; - private readonly Made _made; - private ClosedGenericFactoryGenerator _factoryGenerator; - private ConstructorInfo _knownSingleCtor; - - private sealed class ClosedGenericFactoryGenerator : IConcreteFactoryGenerator - { - public ImHashMap, ReflectionFactory> GeneratedFactories => _generatedFactories.Value; - - public ClosedGenericFactoryGenerator(ReflectionFactory openGenericFactory) => _openGenericFactory = openGenericFactory; - - public Factory GetGeneratedFactory(Request request, bool ifErrorReturnDefault = false) - { - var openFactory = _openGenericFactory; - var implType = openFactory._implementationType; - var serviceType = request.GetActualServiceType(); - var serviceTypeInfo = serviceType.GetTypeInfo(); - - Type[] closedTypeArgs; - if (implType == null || serviceTypeInfo.IsGenericType && serviceType.GetGenericTypeDefinition() == implType) - closedTypeArgs = serviceTypeInfo.GetGenericParamsAndArgsUnsafe(); - else if (implType.IsGenericParameter) - closedTypeArgs = serviceType.One(); - else - { - var implTypeInfo = implType.GetTypeInfo(); - var implTypeParams = implTypeInfo.GetGenericParamsAndArgsUnsafe(); - - closedTypeArgs = new Type[implTypeParams.Length]; - var implementedTypes = implType.GetImplementedTypes(); - - var serviceTypeArgs = serviceTypeInfo.GetGenericParamsAndArgsUnsafe(); - - var matchFound = false; - for (var i = 0; !matchFound && i < implementedTypes.Length; ++i) - { - var implementedType = implementedTypes[i]; - var implementedTypeInfo = implementedType.GetTypeInfo(); - if (implementedTypeInfo.IsGenericType && - implementedType.GetGenericTypeDefinition() == serviceType.GetGenericTypeDefinition()) - { - var implementedTypeGenericParams = implementedTypeInfo.GetGenericParamsAndArgsUnsafe(); - if (!ReflectionTools.AreAllTypeArgumentsClosed(implementedTypeGenericParams)) - matchFound = MatchServiceWithImplementedTypeParams( - closedTypeArgs, implTypeParams, implementedTypeGenericParams, serviceTypeArgs); - } - } - - if (!matchFound) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null : - Throw.For(Error.NoMatchedImplementedTypesWithServiceType, implType, implementedTypes, request); - - MatchOpenGenericConstraints(implTypeParams, closedTypeArgs); - - for (var i = 0; i < closedTypeArgs.Length; i++) - if (closedTypeArgs[i] == null) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null : - Throw.For(Error.NotFoundOpenGenericImplTypeArgInService, - implType, implTypeParams[i], request); - } - - var made = openFactory.Made; - if (made.FactoryMethod != null) - { - // resolve request with factory to specify the implementation type may be required by FactoryMethod or GetClosed... - request = request.WithResolvedFactory(openFactory, ifErrorReturnDefault, ifErrorReturnDefault, copyRequest: true); - var factoryMethod = made.FactoryMethod(request); - if (factoryMethod == null) - return ifErrorReturnDefault ? null : // todo: should we check ifUnresolved here, - Throw.For(Error.GotNullFactoryWhenResolvingService, request); - - var checkMatchingType = implType != null && implType.IsGenericParameter; - var closedFactoryMethod = GetClosedFactoryMethodOrDefault(factoryMethod, closedTypeArgs, request, checkMatchingType); - if (closedFactoryMethod == null) // may be null only for `IfUnresolved.ReturnDefault` or if the check for matching type is failed - return null; - - made = Made.Of(closedFactoryMethod, made.Parameters, made.PropertiesAndFields); - } - - if (implType != null) - { - implType = implType.IsGenericParameter ? closedTypeArgs[0] : - Throw.IfThrows(() => implType.MakeGenericType(closedTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, implType, request); - if (implType == null) - return null; - } - - var knownImplOrServiceType = implType ?? made.FactoryMethodKnownResultType ?? serviceType; - var serviceKey = request.ServiceKey; - serviceKey = (serviceKey as OpenGenericTypeKey)?.ServiceKey ?? serviceKey; - var generatedFactoryKey = KV.Of(knownImplOrServiceType, serviceKey); - - var generatedFactories = _generatedFactories.Value; - if (!generatedFactories.IsEmpty) - { - var generatedFactory = generatedFactories.GetValueOrDefault(generatedFactoryKey); - if (generatedFactory != null) - return generatedFactory; - } - - var closedGenericFactory = new ReflectionFactory(implType, openFactory.Reuse, made, openFactory.Setup) - { - GeneratorFactoryID = openFactory.FactoryID, - Caching = openFactory.Caching - }; - - // we should use whatever the first factory is registered because it can be used already in decorators and recursive factories check - _generatedFactories.Swap(generatedFactoryKey, closedGenericFactory, - (x, genFacKey, fac) => x.AddOrUpdate(genFacKey, fac, (oldFac, _) => closedGenericFactory = oldFac)); - - return closedGenericFactory; - } - - private readonly ReflectionFactory _openGenericFactory; - private readonly Ref, ReflectionFactory>> - _generatedFactories = Ref.Of(ImHashMap, ReflectionFactory>.Empty); - } - - private void SetKnownImplementationType(Type implType, Made made) - { - var knownImplType = implType; - - var factoryMethodResultType = Made.FactoryMethodKnownResultType; - if (implType == null || - implType == typeof(object) || // required as currently object represents the open-generic type argument T registrations - implType.IsAbstract()) - { - if (made.FactoryMethod == null) - { - if (implType == null) - Throw.It(Error.RegisteringNullImplementationTypeAndNoFactoryMethod); - if (implType == typeof(object)) - Throw.It(Error.RegisteringObjectTypeAsImplementationIsNotSupported); - if (implType.IsAbstract()) - Throw.It(Error.RegisteringAbstractImplementationTypeAndNoFactoryMethod, implType); - } - - knownImplType = null; // Ensure that we do not have abstract implementation type - - // Using non-abstract factory method result type is safe for conditions and diagnostics - if (factoryMethodResultType != null && - factoryMethodResultType != typeof(object) && - !factoryMethodResultType.IsAbstract()) - knownImplType = factoryMethodResultType; - } - else if (factoryMethodResultType != null - && factoryMethodResultType != implType) - { - if (!factoryMethodResultType.IsAssignableTo(implType) && - !factoryMethodResultType.HasConversionOperatorTo(implType)) - Throw.It(Error.RegisteredFactoryMethodResultTypesIsNotAssignableToImplementationType, - implType, factoryMethodResultType); - } - - var openGenericImplType = knownImplType ?? implType; - if (openGenericImplType == typeof(object) || // for open-generic T implementation - openGenericImplType != null && // for open-generic X implementation - (openGenericImplType.IsGenericDefinition() || openGenericImplType.IsGenericParameter) || - made.IsConditionalImplementation) - { - _factoryGenerator = new ClosedGenericFactoryGenerator(this); - } - - _implementationType = knownImplType; - } - - private static void MatchOpenGenericConstraints(Type[] implTypeParams, Type[] implTypeArgs) - { - for (var i = 0; i < implTypeParams.Length; i++) - { - var implTypeArg = implTypeArgs[i]; - if (implTypeArg == null) - continue; // skip yet unknown type arg - - var implTypeParamConstraints = implTypeParams[i].GetGenericParamConstraints(); - if (implTypeParamConstraints.IsNullOrEmpty()) - continue; // skip case with no constraints - - // match type parameters inside constraint - var constraintMatchFound = false; - for (var j = 0; !constraintMatchFound && j < implTypeParamConstraints.Length; ++j) - { - var implTypeParamConstraint = implTypeParamConstraints[j]; - if (implTypeParamConstraint != implTypeArg && implTypeParamConstraint.IsOpenGeneric()) - { - var implTypeArgArgs = implTypeArg.IsGeneric() ? implTypeArg.GetGenericParamsAndArgs() : implTypeArg.One(); - var implTypeParamConstraintParams = implTypeParamConstraint.GetGenericParamsAndArgs(); - - constraintMatchFound = MatchServiceWithImplementedTypeParams( - implTypeArgs, implTypeParams, implTypeParamConstraintParams, implTypeArgArgs); - } - } - } - } - - private static bool MatchServiceWithImplementedTypeParams( - Type[] resultImplArgs, Type[] implParams, Type[] serviceParams, Type[] serviceArgs, - int resultCount = 0) - { - if (serviceArgs.Length != serviceParams.Length) - return false; - - for (var i = 0; i < serviceParams.Length; i++) - { - var serviceArg = serviceArgs[i]; - var implementedParam = serviceParams[i]; - if (implementedParam.IsGenericParameter) - { - var paramIndex = implParams.Length - 1; - while (paramIndex != -1 && !ReferenceEquals(implParams[paramIndex], implementedParam)) - --paramIndex; - if (paramIndex != -1) - { - if (resultImplArgs[paramIndex] == null) - resultImplArgs[paramIndex] = serviceArg; - else if (resultImplArgs[paramIndex] != serviceArg) - return false; // more than one service type arg is matching with single implementation type parameter - } - } - else if (implementedParam != serviceArg) - { - if (!implementedParam.IsOpenGeneric() || - implementedParam.GetGenericDefinitionOrNull() != serviceArg.GetGenericDefinitionOrNull()) - return false; // type parameter and argument are of different types - - if (!MatchServiceWithImplementedTypeParams(resultImplArgs, implParams, - implementedParam.GetGenericParamsAndArgs(), serviceArg.GetGenericParamsAndArgs())) - return false; // nested match failed due one of above reasons. - } - } - - return true; - } - - private static FactoryMethod GetClosedFactoryMethodOrDefault( - FactoryMethod factoryMethod, Type[] serviceTypeArgs, Request request, bool ifErrorReturnDefault = false) - { - var factoryMember = factoryMethod.ConstructorOrMethodOrMember; - var factoryInfo = factoryMethod.FactoryServiceInfo; - - var resultType = factoryMember.GetReturnTypeOrDefault(); - var implTypeParams = resultType.IsGenericParameter ? resultType.One() : resultType.GetGenericParamsAndArgs(); - - // Get method declaring type, and if its open-generic, - // then close it first. It is required to get actual method. - var factoryImplType = factoryMember.DeclaringType.ThrowIfNull(); - if (factoryImplType.IsOpenGeneric()) - { - var factoryImplTypeParams = factoryImplType.GetGenericParamsAndArgs(); - var resultFactoryImplTypeArgs = new Type[factoryImplTypeParams.Length]; - - var isFactoryImplTypeClosed = MatchServiceWithImplementedTypeParams( - resultFactoryImplTypeArgs, factoryImplTypeParams, implTypeParams, serviceTypeArgs); - - if (!isFactoryImplTypeClosed) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null - : Throw.For(Error.NoMatchedFactoryMethodDeclaringTypeWithServiceTypeArgs, - factoryImplType, new StringBuilder().Print(serviceTypeArgs, itemSeparator: ", "), request); - - // For instance factory match its service type from the implementation factory type. - if (factoryInfo != null) - { - // Look for service type equivalent within factory implementation type base classes and interfaces, - // because we need identical type arguments to match. - var factoryServiceType = factoryInfo.ServiceType; - if (factoryServiceType != factoryImplType) - factoryServiceType = factoryImplType.GetImplementedTypes() - .FindFirst(factoryServiceType, (fServiceType, t) => t.IsGeneric() && t.GetGenericTypeDefinition() == fServiceType) - .ThrowIfNull(); - - var factoryServiceTypeParams = factoryServiceType.GetGenericParamsAndArgs(); - var resultFactoryServiceTypeArgs = new Type[factoryServiceTypeParams.Length]; - - var isFactoryServiceTypeClosed = MatchServiceWithImplementedTypeParams( - resultFactoryServiceTypeArgs, factoryServiceTypeParams, factoryImplTypeParams, resultFactoryImplTypeArgs); - - // Replace factory info with close factory service type - if (isFactoryServiceTypeClosed) - { - factoryServiceType = factoryServiceType.GetGenericTypeDefinition().ThrowIfNull(); - var closedFactoryServiceType = Throw.IfThrows( - () => factoryServiceType.MakeGenericType(resultFactoryServiceTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, factoryServiceType, request); - - if (closedFactoryServiceType == null) - return null; - - // Copy factory info with closed factory type - factoryInfo = ServiceInfo.Of(closedFactoryServiceType).WithDetails(factoryInfo.Details); - } - } - - MatchOpenGenericConstraints(factoryImplTypeParams, resultFactoryImplTypeArgs); - - // Close the factory type implementation - // and get factory member to use from it. - var closedFactoryImplType = Throw.IfThrows( - () => factoryImplType.MakeGenericType(resultFactoryImplTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, factoryImplType, request); - - if (closedFactoryImplType == null) - return null; - - // Find corresponding member again, now from closed type - var factoryMethodBase = factoryMember as MethodBase; - if (factoryMethodBase != null) - { - var factoryMethodParameters = factoryMethodBase.GetParameters(); - var targetMethods = closedFactoryImplType.GetMembers(t => t.DeclaredMethods, includeBase: true) - .Match(m => m.Name == factoryMember.Name && m.GetParameters().Length == factoryMethodParameters.Length) - .ToArrayOrSelf(); - - if (targetMethods.Length == 1) - factoryMember = targetMethods[0]; - else // Fallback to MethodHandle only if methods have similar signatures - { - var methodHandleProperty = typeof(MethodBase).GetTypeInfo() - .DeclaredProperties - .FindFirst(it => it.Name == "MethodHandle") - .ThrowIfNull(Error.OpenGenericFactoryMethodDeclaringTypeIsNotSupportedOnThisPlatform, - factoryImplType, closedFactoryImplType, factoryMethodBase.Name); - factoryMember = MethodBase.GetMethodFromHandle( - (RuntimeMethodHandle)methodHandleProperty.GetValue(factoryMethodBase, Empty()), - closedFactoryImplType.TypeHandle); - } - } - else if (factoryMember is FieldInfo) - { - factoryMember = closedFactoryImplType.GetMembers(t => t.DeclaredFields, includeBase: true) - .Single(f => f.Name == factoryMember.Name); - } - else if (factoryMember is PropertyInfo) - { - factoryMember = closedFactoryImplType.GetMembers(t => t.DeclaredProperties, includeBase: true) - .Single(f => f.Name == factoryMember.Name); - } - } - - // If factory method is actual method and still open-generic after closing its declaring type, - // then match remaining method type parameters and make closed method - var openFactoryMethod = factoryMember as MethodInfo; - if (openFactoryMethod != null && openFactoryMethod.ContainsGenericParameters) - { - var methodTypeParams = openFactoryMethod.GetGenericArguments(); - var resultMethodTypeArgs = new Type[methodTypeParams.Length]; - - var isMethodClosed = MatchServiceWithImplementedTypeParams( - resultMethodTypeArgs, methodTypeParams, implTypeParams, serviceTypeArgs); - - if (!isMethodClosed) - return ifErrorReturnDefault || request.IfUnresolved != IfUnresolved.Throw ? null - : Throw.For(Error.NoMatchedFactoryMethodWithServiceTypeArgs, - openFactoryMethod, new StringBuilder().Print(serviceTypeArgs, itemSeparator: ", "), - request); - - MatchOpenGenericConstraints(methodTypeParams, resultMethodTypeArgs); - - factoryMember = Throw.IfThrows( - () => openFactoryMethod.MakeGenericMethod(resultMethodTypeArgs), - !ifErrorReturnDefault && request.IfUnresolved == IfUnresolved.Throw, - Error.NoMatchedGenericParamConstraints, factoryImplType, request); - - if (factoryMember == null) - return null; - } - - return FactoryMethod.Of(factoryMember, factoryInfo); - } - -#endregion - } - - /// Creates service expression using client provided expression factory delegate. - public sealed class ExpressionFactory : Factory - { - /// Wraps provided delegate into factory. - /// Delegate that will be used internally to create service expression. - /// (optional) Reuse. (optional) Setup. - public ExpressionFactory(Func getServiceExpression, IReuse reuse = null, Setup setup = null) - : base(reuse, setup) - { - _getServiceExpression = getServiceExpression.ThrowIfNull(); - } - - /// Creates service expression using wrapped delegate. - /// Request to resolve. Expression returned by stored delegate. - public override Expression CreateExpressionOrDefault(Request request) => - _getServiceExpression(request); - - private readonly Func _getServiceExpression; - } - - /// Wraps the instance in registry - public sealed class RegisteredInstanceFactory : Factory - { - /// The registered pre-created object instance - public readonly object Instance; - - /// Non-abstract closed implementation type. - public override Type ImplementationType { get; } - - /// - public override bool HasRuntimeState => true; - - /// Simplified specially for register instance - internal override bool ValidateAndNormalizeRegistration(Type serviceType, object serviceKey, bool isStaticallyChecked, Rules rules) - { - if (!isStaticallyChecked && (ImplementationType != null && !ImplementationType.IsAssignableTo(serviceType.ThrowIfNull()))) - Throw.It(Error.RegisteringInstanceNotAssignableToServiceType, ImplementationType, serviceType); - return true; - } - - /// Creates factory. - public RegisteredInstanceFactory(object instance, IReuse reuse = null, Setup setup = null) - : base(reuse ?? DryIoc.Reuse.Singleton, - (setup ?? DryIoc.Setup.Default).WithAsResolutionCallForGeneratedExpression()) - { - if (instance != null) // it may be `null` as well - { - ImplementationType = instance.GetType(); - if (Setup.WeaklyReferenced) - Instance = new WeakReference(instance); - else - Instance = instance; - } - } - - /// Wraps the instance in expression constant - public override Expression CreateExpressionOrDefault(Request request) - { - // unpacks the weak-reference - if (Setup.WeaklyReferenced) - return Call( - typeof(ThrowInGeneratedCode).GetTypeInfo() - .GetDeclaredMethod(nameof(ThrowInGeneratedCode.WeakRefReuseWrapperGCed)), - Property( - Constant(Instance, typeof(WeakReference)), - typeof(WeakReference).Property(nameof(WeakReference.Target)))); - - // otherwise just return a constant - var instanceExpr = request.Container.GetConstantExpression(Instance); - var serviceType = request.GetActualServiceType(); - if (ImplementationType.IsAssignableTo(serviceType)) - return instanceExpr; - return Convert(instanceExpr, serviceType); - } - - /// Simplified path for the registered instance - public override Expression GetExpressionOrDefault(Request request) - { - if (// preventing recursion - (request.Flags & RequestFlags.IsGeneratedResolutionDependencyExpression) == 0 && !request.IsResolutionCall && - (Setup.AsResolutionCall || Setup.AsResolutionCallForExpressionGeneration && request.Rules.UsedForExpressionGeneration)) - return Resolver.CreateResolutionExpression(request.WithResolvedFactory(this), Setup.OpenResolutionScope); - - // First look for decorators if it is not already a decorator - var serviceType = request.ServiceType; - var elementType = serviceType.GetArrayElementTypeOrNull(); - if (elementType != null) - serviceType = typeof(IEnumerable<>).MakeGenericType(elementType); - if (!request.Container.GetDecoratorFactoriesOrDefault(serviceType).IsNullOrEmpty()) - { - var decoratorExpr = request.Container.GetDecoratorExpressionOrDefault(request.WithResolvedFactory(this)); - if (decoratorExpr != null) - return decoratorExpr; - } - - return CreateExpressionOrDefault(request); - } - - /// Used at resolution root too simplify getting the actual instance - public override FactoryDelegate GetDelegateOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - - if (request.Container.GetDecoratorExpressionOrDefault(request) != null) - return base.GetDelegateOrDefault(request); - - return Setup.WeaklyReferenced - ? (FactoryDelegate)UnpackWeakRefFactory - : InstanceFactory; - } - - private object InstanceFactory(IResolverContext _) => Instance; - private object UnpackWeakRefFactory(IResolverContext _) => (Instance as WeakReference)?.Target.WeakRefReuseWrapperGCed(); - } - - /// This factory is the thin wrapper for user provided delegate - /// and where possible it uses delegate directly: without converting it to expression. - public sealed class DelegateFactory : Factory - { - /// Non-abstract closed implementation type. - public override Type ImplementationType { get; } - - /// - public override bool HasRuntimeState => true; - - /// Creates factory. - public DelegateFactory(FactoryDelegate factoryDelegate, - IReuse reuse = null, Setup setup = null, Type knownImplementationType = null) - : base(reuse, (setup ?? Setup.Default).WithAsResolutionCallForGeneratedExpression()) - { - _factoryDelegate = factoryDelegate.ThrowIfNull(); - ImplementationType = knownImplementationType; - } - - /// Create expression by wrapping call to stored delegate with provided request. - public override Expression CreateExpressionOrDefault(Request request) - { - // GetConstant here is needed to check the state - var delegateExpr = request.Container.GetConstantExpression(_factoryDelegate); - var resolverExpr = ResolverContext.GetRootOrSelfExpr(request); - return Convert(Invoke(delegateExpr, resolverExpr), request.GetActualServiceType()); - } - - /// If possible returns delegate directly, without creating expression trees, just wrapped in . - /// If decorator found for request then factory fall-backs to expression creation. - /// Request to resolve. - /// Factory delegate directly calling wrapped delegate, or invoking expression if decorated. - public override FactoryDelegate GetDelegateOrDefault(Request request) - { - request = request.WithResolvedFactory(this); - - // Wrap the delegate in respective expression for non-simple use - if (request.Reuse != DryIoc.Reuse.Transient || - FactoryType == FactoryType.Service && - request.Container.GetDecoratorExpressionOrDefault(request) != null) - return base.GetDelegateOrDefault(request); - - // Otherwise just use delegate as-is - return _factoryDelegate; - } - - private readonly FactoryDelegate _factoryDelegate; - } - - internal sealed class FactoryPlaceholder : Factory - { - public static readonly Factory Default = new FactoryPlaceholder(); - - // Always resolved asResolutionCall, to create a hole in object graph to be filled in later - public override Setup Setup => _setup; - private static readonly Setup _setup = Setup.With(asResolutionCall: true); - - public override Expression CreateExpressionOrDefault(Request request) => - Throw.For(Error.NoImplementationForPlaceholder, request); - } - - /// Should return value stored in scope - public delegate object CreateScopedValue(); - - /// Lazy object storage that will create object with provided factory on first access, - /// then will be returning the same object for subsequent access. - public interface IScope : IEnumerable, IDisposable - { - /// Parent scope in scope stack. Null for root scope. - IScope Parent { get; } - - /// Optional name object associated with scope. - object Name { get; } - - /// True if scope is disposed. - bool IsDisposed { get; } - - /// Looks up for stored item by id. - bool TryGet(out object item, int id); - - /// Creates, stores, and returns created item - object GetOrAdd(int id, CreateScopedValue createValue, int disposalOrder = 0); - - /// Create the value via `FactoryDelegate` passing the `IResolverContext` - object GetOrAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder = 0); - - /// Creates, stores, and returns created item - object TryGetOrAddWithoutClosure(int id, - IResolverContext resolveContext, Expression expr, bool useFec, - Func createValue, int disposalOrder = 0); - - /// Tracked item will be disposed with the scope. - /// Smaller will be disposed first. - object TrackDisposable(object item, int disposalOrder = 0); - - ///[Obsolete("Removing because it is used only by obsolete `UseInstance` feature")] - void SetOrAdd(int id, object item); - - ///[Obsolete("Removing because it is not used")] - object GetOrTryAdd(int id, object item, int disposalOrder); - - /// Sets (replaces) the factory for specified type. - void SetUsedInstance(Type type, FactoryDelegate factory); - - /// Looks up for stored item by type. - bool TryGetUsedInstance(IResolverContext r, Type type, out object instance); - - /// Clones the scope. - IScope Clone(); - } - - /// Scope is container to hold the shared per scope items and dispose items. - /// Scope uses Locking to ensure that the object factory called only once. - public sealed class Scope : IScope - { - /// Parent scope in scope stack. Null for the root scope. - public IScope Parent { get; } - - /// Optional name associated with scope. - public object Name { get; } - - /// True if scope is disposed. - public bool IsDisposed => _disposed == 1; - private int _disposed; - - private ImHashMap _factories; - private ImList _unorderedDisposables; - private ImMap _disposables; - - internal const int MAP_COUNT = 16; - internal const int MAP_COUNT_SUFFIX_MASK = MAP_COUNT - 1; - internal ImMap[] _maps; - - internal static readonly object NoItem = new object(); - - private static StackPool[]> _mapsPool = new StackPool[]>(); - - private static ImMap[] _emptySlots = CreateEmptyMaps(); - - private static ImMap[] CreateEmptyMaps() - { - var empty = ImMap.Empty; - var slots = new ImMap[MAP_COUNT]; - for (var i = 0; i < MAP_COUNT; ++i) - slots[i] = empty; - return slots; - } - - /// Creates scope with optional parent and name. - public Scope(IScope parent = null, object name = null) - : this(parent, name, /*_mapsPool.RentOrDefault() ??*/ CreateEmptyMaps(), ImHashMap.Empty, - ImList.Empty, ImMap.Empty) - { } - - private Scope(IScope parent, object name, ImMap[] maps, ImHashMap instances, - ImList unorderedDisposables, ImMap disposables) - { - Parent = parent; - Name = name; - _unorderedDisposables = unorderedDisposables; - _disposables = disposables; - _factories = ImHashMap.Empty; - _maps = maps; - } - - /// - public IScope Clone() - { - var slotsCopy = new ImMap[MAP_COUNT]; - - // todo: we need a way to copy all Refs in the slot - do we? - for (var i = 0; i < MAP_COUNT; i++) - slotsCopy[i] = _maps[i]; - - return new Scope(Parent, Name, slotsCopy, _factories, _unorderedDisposables, _disposables); - } - - /// - [MethodImpl((MethodImplOptions)256)] - public object GetOrAdd(int id, CreateScopedValue createValue, int disposalOrder = 0) - { - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != NoItem) - return itemRef.Value; - return TryGetOrAdd(ref map, id, createValue, disposalOrder); - } - - private object TryGetOrAdd(ref ImMap map, int id, CreateScopedValue createValue, int disposalOrder = 0) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef.Value; - - // lock on the ref itself to set its `Item` field - lock (itemRef) - { - // double-check if the item was changed in between (double check locking) - if (itemRef.Value != NoItem) - return itemRef.Value; - itemRef.Value = createValue(); - } - - if (itemRef.Value is IDisposable disp && disp != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disp); - else - AddDisposable(disp, disposalOrder); - - return itemRef.Value; - } - - /// - [MethodImpl((MethodImplOptions)256)] - public object GetOrAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder = 0) - { - var itemRef = _maps[id & MAP_COUNT_SUFFIX_MASK].GetEntryOrDefault(id); - return itemRef != null && itemRef.Value != NoItem - ? itemRef.Value - : TryGetOrAddViaFactoryDelegate(id, createValue, r, disposalOrder); - } - - internal static readonly MethodInfo GetOrAddViaFactoryDelegateMethod = - typeof(IScope).GetTypeInfo().GetDeclaredMethod(nameof(IScope.GetOrAddViaFactoryDelegate)); - - internal ImMapEntry TryAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef; - - lock (itemRef) - { - if (itemRef.Value != NoItem) - return itemRef; - itemRef.Value = createValue(r); - } - - if (itemRef.Value is IDisposable disp && disp != this) - AddDisposable(disp, disposalOrder); - - return itemRef; - } - - internal object TryGetOrAddViaFactoryDelegate(int id, FactoryDelegate createValue, IResolverContext r, int disposalOrder = 0) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef.Value; - - lock (itemRef) - { - if (itemRef.Value != NoItem) - return itemRef.Value; - itemRef.Value = createValue(r); - } - - if (itemRef.Value is IDisposable disp && disp != this) - { - if (disposalOrder == 0) - AddUnorderedDisposable(disp); - else - AddDisposable(disp, disposalOrder); - } - - return itemRef.Value; - } - - /// - public object TryGetOrAddWithoutClosure(int id, - IResolverContext resolveContext, Expression expr, bool useFec, - Func createValue, int disposalOrder = 0) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, id, (x, i) => x.AddOrKeep(i, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return itemRef.Value; - - lock (itemRef) - { - if (itemRef.Value != NoItem) - return itemRef.Value; - itemRef.Value = createValue(resolveContext, expr, useFec); - } - - if (itemRef.Value is IDisposable disposable && disposable != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disposable); - else - AddDisposable(disposable, disposalOrder); - - return itemRef.Value; - } - - ///[Obsolete("Removing because it is used only by obsolete `UseInstance` feature")] - public void SetOrAdd(int id, object item) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, x => x.AddOrKeep(id, NoItem)); - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef.Value != NoItem) - return; - - // lock on the ref itself to set its `Item` field - lock (itemRef) - { - // double-check if the item was changed in between (double check locking) - if (itemRef.Value != NoItem) - return; - // we can simple assign because we are under the lock - itemRef.Value = item; - } - - if (item is IDisposable disp && disp != this) - AddUnorderedDisposable(disp); - } - - ///[Obsolete("Removing because it is not used")] - public object GetOrTryAdd(int id, object newItem, int disposalOrder) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - - ref var map = ref _maps[id & MAP_COUNT_SUFFIX_MASK]; - - var itemRef = map.GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != NoItem) - return itemRef.Value; - - var m = map; - if (Interlocked.CompareExchange(ref map, m.AddOrKeep(id, NoItem), m) != m) - Ref.Swap(ref map, x => x.AddOrUpdate(id, NoItem)); - - itemRef = map.GetEntryOrDefault(id); - - // lock on the ref itself to set its `Item` field - lock (itemRef) - { - // double-check if the item was changed in between (double check locking) - if (itemRef.Value != NoItem) - return itemRef.Value; - - // we can simple assign because we are under the lock - itemRef.Value = newItem; - } - - if (itemRef.Value is IDisposable disp && disp != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disp); - else - AddDisposable(disp, disposalOrder); - - return itemRef.Value; - } - - internal void AddDisposable(IDisposable disposable, int disposalOrder) - { - var d = _disposables; - if (Interlocked.CompareExchange(ref _disposables, d.AddOrUpdate(disposalOrder, disposable), d) != d) - Ref.Swap(ref _disposables, disposalOrder, disposable, (x, dispOrder, disp) => x.AddOrUpdate(dispOrder, disp)); - } - - [MethodImpl((MethodImplOptions)256)] - internal void AddUnorderedDisposable(IDisposable disposable) - { - var copy = _unorderedDisposables; - if (Interlocked.CompareExchange(ref _unorderedDisposables, copy.Push(disposable), copy) != copy) - Ref.Swap(ref _unorderedDisposables, disposable, (x, d) => x.Push(d)); - } - - /// - [MethodImpl((MethodImplOptions)256)] - public bool TryGet(out object item, int id) - { - var itemRef = _maps[id & MAP_COUNT_SUFFIX_MASK].GetEntryOrDefault(id); - if (itemRef != null && itemRef.Value != NoItem) - { - item = itemRef.Value; - return true; - } - - item = null; - return false; - } - - // todo: consider adding the overload without `disposalOrder` - /// Can be used to manually add service for disposal - public object TrackDisposable(object item, int disposalOrder = 0) - { - if (item is IDisposable disposable && disposable != this) - if (disposalOrder == 0) - AddUnorderedDisposable(disposable); - else - AddDisposable(disposable, disposalOrder); - return item; - } - - internal static readonly MethodInfo TrackDisposableMethod = - typeof(IScope).GetTypeInfo().GetDeclaredMethod(nameof(IScope.TrackDisposable)); - - /// Add instance to the small registry via factory - public void SetUsedInstance(Type type, FactoryDelegate factory) - { - if (_disposed == 1) - Throw.It(Error.ScopeIsDisposed, ToString()); - var f = _factories; - var hash = RuntimeHelpers.GetHashCode(type); - if (Interlocked.CompareExchange(ref _factories, f.AddOrUpdate(hash, type, factory), f) != f) - Ref.Swap(ref _factories, hash, type, factory, (x, h, t, fac) => x.AddOrUpdate(h, t, fac)); - } - - /// Try retrieve instance from the small registry. - public bool TryGetUsedInstance(IResolverContext r, Type type, out object instance) - { - instance = null; - if (_disposed == 1) - return false; - - if (!_factories.IsEmpty) - { - var factory = _factories.GetValueOrDefault(RuntimeHelpers.GetHashCode(type), type); - if (factory != null) - { - instance = factory(r); - return true; - } - } - - return Parent?.TryGetUsedInstance(r, type, out instance) ?? false; - } - - /// Enumerates all the parent scopes upwards starting from this one. - public IEnumerator GetEnumerator() - { - for (IScope scope = this; scope != null; scope = scope.Parent) - yield return scope; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// Disposes all stored objects and empties item storage. - /// The disposal happens in REVERSE resolution / injection order, consumer first, dependency next. - /// It will allow consumer to do something with its dependency before it is disposed. - /// All disposal exceptions are swallowed except the ContainerException, - /// which may indicate container misconfiguration. - public void Dispose() - { - if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1) - return; - - if (!_disposables.IsEmpty) - SafelyDisposeOrderedDisposables(_disposables); - - for (var unordDisp = _unorderedDisposables; !unordDisp.IsEmpty; unordDisp = unordDisp.Tail) - unordDisp.Head.Dispose(); - - _unorderedDisposables = ImList.Empty; - _disposables = ImMap.Empty; - _factories = ImHashMap.Empty; - - var maps = Interlocked.Exchange(ref _maps, _emptySlots); - var empty = ImMap.Empty; - //for (int i = 0; i < MAP_COUNT; i++) maps[i] = empty; - //_mapsPool.Return(maps); - } - - private static void SafelyDisposeOrderedDisposables(ImMap disposables) - { - disposables.Visit(d => { - try - { - // Ignoring disposing exception, as it is not important to proceed the disposal of other items - d.Value.Dispose(); - } - catch (ContainerException) - { - throw; - } - catch (Exception) - { - } - }); - } - - /// Prints scope info (name and parent) to string for debug purposes. - public override string ToString() => - "{" + (IsDisposed ? "IsDisposed=true, " : "") - + (Name != null ? "Name=" + Name : "Name=null") - + (Parent != null ? ", Parent=" + Parent : "") - + "}"; - } - - /// Delegate to get new scope from old/existing current scope. - /// Old/existing scope to change. - /// New scope or old if do not want to change current scope. - public delegate IScope SetCurrentScopeHandler(IScope oldScope); - - /// Provides ambient current scope and optionally scope storage for container, - /// examples are HttpContext storage, Execution context, Thread local. - public interface IScopeContext : IDisposable - { - /// Returns current scope or null if no ambient scope available at the moment. - /// Current scope or null. - IScope GetCurrentOrDefault(); - - /// Changes current scope using provided delegate. Delegate receives current scope as input and - /// should return new current scope. - /// Delegate to change the scope. - /// Important: may be called multiple times in concurrent environment. - /// Make it predictable by removing any side effects. - /// New current scope. So it is convenient to use method in "using (var newScope = ctx.SetCurrent(...))". - IScope SetCurrent(SetCurrentScopeHandler setCurrentScope); - } - -#if NET35 || NET40 || NET403 || PCL || PCL328 || PCL259 - - /// Tracks one current scope per thread, so the current scope in different tread would be different or null, - /// if not yet tracked. Context actually stores scope references internally, so it should be disposed to free them. - public sealed class ThreadScopeContext : IScopeContext - { - /// Provides static name for context. It is OK because its constant. - public static readonly string ScopeContextName = "ThreadScopeContext"; - - /// Returns current scope in calling Thread or null, if no scope tracked. - public IScope GetCurrentOrDefault() => - _scopes.GetValueOrDefault(Portable.GetCurrentManagedThreadID()) as IScope; - - /// Change current scope for the calling Thread. - public IScope SetCurrent(SetCurrentScopeHandler setCurrentScope) - { - var threadId = Portable.GetCurrentManagedThreadID(); - IScope newScope = null; - Ref.Swap(ref _scopes, s => s.AddOrUpdate(threadId, - newScope = setCurrentScope(s.GetValueOrDefault(threadId) as IScope))); - return newScope; - } - - /// Disposes the scopes and empties internal scope storage. - public void Dispose() - { - if (!_scopes.IsEmpty) - _scopes.Visit(d => d.Value?.Dispose()); - _scopes = ImMap.Empty; - } - - /// Collection of scoped by their managed thread id - private ImMap _scopes = ImMap.Empty; - } - -#else - /// Tracks one current scope per thread, so the current scope in different tread would be different or null, - /// if not yet tracked. Context actually stores scope references internally, so it should be disposed to free them. - public sealed class ThreadScopeContext : IScopeContext - { - /// Provides static name for context. It is OK because its constant. - public static readonly string ScopeContextName = "ThreadScopeContext"; - - private ThreadLocal _scope = new ThreadLocal(true); - - /// Returns current scope in calling Thread or null, if no scope tracked. - public IScope GetCurrentOrDefault() => - _scope.Value; - - /// Change current scope for the calling Thread. - public IScope SetCurrent(SetCurrentScopeHandler setCurrentScope) => - _scope.Value = setCurrentScope(GetCurrentOrDefault()); - - /// Disposes the scopes and empties internal scope storage. - public void Dispose() - { - var scopes = _scope.Values; - foreach (var scope in scopes) - { - var s = scope; - while (s != null) - { - var x = s; - s = s.Parent; - x.Dispose(); - } - } - } - } - -#endif - - /// Simplified scope agnostic reuse abstraction. More easy to implement, - /// and more powerful as can be based on other storage beside reuse. - public interface IReuse : IConvertibleToExpression - { - /// Relative to other reuses lifespan value. - int Lifespan { get; } - - /// Optional name. Use to find matching scope by the name. - /// It also may be interpreted as object[] Names for matching with multiple scopes - object Name { get; } - - /// Returns true if reuse can be applied: may check if scope or other reused item storage is present. - bool CanApply(Request request); - - /// Returns composed expression. - Expression Apply(Request request, Expression serviceFactoryExpr); - } - - /// Returns container bound scope for storing singleton objects. - public sealed class SingletonReuse : IReuse - { - /// Big lifespan. - public const int DefaultLifespan = 1000; - - /// Relative to other reuses lifespan value. - public int Lifespan => DefaultLifespan; - - /// - public object Name => null; - - /// Returns true because singleton is always available. - public bool CanApply(Request request) => true; - - /// Returns expression call to GetOrAddItem. - public Expression Apply(Request request, Expression serviceFactoryExpr) - { - // this is required because we cannot use ValueType for the object - if (serviceFactoryExpr.Type.IsValueType()) - serviceFactoryExpr = Convert(serviceFactoryExpr, typeof(object)); - - if (request.TracksTransientDisposable) - return Call(ResolverContext.SingletonScopeExpr, Scope.TrackDisposableMethod, - serviceFactoryExpr, Constant(request.Factory.Setup.DisposalOrder)); - - return Call(ResolverContext.SingletonScopeExpr, Scope.GetOrAddViaFactoryDelegateMethod, - Constant(request.FactoryID), Lambda(serviceFactoryExpr, - FactoryDelegateCompiler.FactoryDelegateParamExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , typeof(object) -#endif - ), - FactoryDelegateCompiler.ResolverContextParamExpr, - Constant(request.Factory.Setup.DisposalOrder)); - } - - private static readonly Lazy _singletonReuseExpr = Lazy.Of(() => - Field(null, typeof(Reuse).Field(nameof(Reuse.Singleton)))); - - /// - public Expression ToExpression(Func fallbackConverter) => _singletonReuseExpr.Value; - - /// Pretty prints reuse name and lifespan - public override string ToString() => "Singleton {Lifespan=" + Lifespan + "}"; - } - - /// Specifies that instances are created, stored and disposed together with some scope. - public sealed class CurrentScopeReuse : IReuse - { - /// Less than Singleton's - public const int DefaultLifespan = 100; - - /// Relative to other reuses lifespan value. - public int Lifespan { get; } - - /// - public object Name { get; } - - /// Returns true if scope is open and the name is matching with reuse . - public bool CanApply(Request request) => - ScopedOrSingleton || - (Name == null ? request.Container.CurrentScope != null : request.Container.GetNamedScope(Name, false) != null); - - /// Creates scoped item creation and access expression. - public Expression Apply(Request request, Expression serviceFactoryExpr) - { - // strip the conversion as we are operating with object anyway - if (serviceFactoryExpr.NodeType == ExprType.Convert) - serviceFactoryExpr = ((UnaryExpression)serviceFactoryExpr).Operand; - - // this is required because we cannot use ValueType for the object - if (serviceFactoryExpr.Type.IsValueType()) - serviceFactoryExpr = Convert(serviceFactoryExpr, typeof(object)); - - var resolverContextParamExpr = FactoryDelegateCompiler.ResolverContextParamExpr; - - if (request.TracksTransientDisposable) - { - if (ScopedOrSingleton) - return Call(TrackScopedOrSingletonMethod, new[] { resolverContextParamExpr, serviceFactoryExpr }); - - var ifNoScopeThrowExpr = Constant(request.IfUnresolved == IfUnresolved.Throw); - if (Name == null) - return Call(TrackScopedMethod, new[] { resolverContextParamExpr, ifNoScopeThrowExpr, serviceFactoryExpr }); - - var nameExpr = request.Container.GetConstantExpression(Name, typeof(object)); - return Call(TrackNameScopedMethod, new[] { resolverContextParamExpr, nameExpr, ifNoScopeThrowExpr, serviceFactoryExpr }); - } - else - { - var idExpr = Constant(request.FactoryID); - - Expression factoryDelegateExpr; - if (serviceFactoryExpr is InvocationExpression ie && - ie.Expression is ConstantExpression registeredDelegateExpr && - registeredDelegateExpr.Type == typeof(FactoryDelegate)) - { - // optimization for the registered delegate - factoryDelegateExpr = registeredDelegateExpr; - } - else - { - factoryDelegateExpr = Lambda(serviceFactoryExpr, - FactoryDelegateCompiler.FactoryDelegateParamExprs -#if SUPPORTS_FAST_EXPRESSION_COMPILER - , typeof(object) -#endif - ); - } - - var disposalIndex = request.Factory.Setup.DisposalOrder; - - if (ScopedOrSingleton) - return Call(GetScopedOrSingletonViaFactoryDelegateMethod, - resolverContextParamExpr, idExpr, factoryDelegateExpr, Constant(disposalIndex)); - - var ifNoScopeThrowExpr = Constant(request.IfUnresolved == IfUnresolved.Throw); - - if (Name == null) - { - if (disposalIndex == 0) - return Call(GetScopedViaFactoryDelegateNoDisposalIndexMethod, - resolverContextParamExpr, ifNoScopeThrowExpr, idExpr, factoryDelegateExpr); - - return Call(GetScopedViaFactoryDelegateMethod, - resolverContextParamExpr, ifNoScopeThrowExpr, idExpr, factoryDelegateExpr, Constant(disposalIndex)); - } - - return Call(GetNameScopedViaFactoryDelegateMethod, resolverContextParamExpr, - request.Container.GetConstantExpression(Name, typeof(object)), - ifNoScopeThrowExpr, idExpr, factoryDelegateExpr, Constant(disposalIndex)); - } - } - - /// - public Expression ToExpression(Func fallbackConverter) => - Name == null && !ScopedOrSingleton - ? Field(null, typeof(Reuse).GetTypeInfo().GetDeclaredField(nameof(Reuse.Scoped))) - : ScopedOrSingleton - ? (Expression)Field(null, typeof(Reuse).GetTypeInfo().GetDeclaredField(nameof(Reuse.ScopedOrSingleton))) - : Call(typeof(Reuse).Method(nameof(Reuse.ScopedTo), typeof(object)), fallbackConverter(Name)); - - /// Pretty prints reuse to string. Reuse string. - public override string ToString() - { - var s = new StringBuilder(ScopedOrSingleton ? "ScopedOrSingleton {" : "Scoped {"); - if (Name != null) - s.Append("Name=").Print(Name).Append(", "); - if (Lifespan != DefaultLifespan) - s.Append("NON DEFAULT LIFESPAN=").Append(Lifespan); - else - s.Append("Lifespan=").Append(Lifespan); - - return s.Append("}").ToString(); - } - - /// Creates the reuse. - public CurrentScopeReuse(object name, bool scopedOrSingleton, int lifespan) - { - Name = name; - ScopedOrSingleton = scopedOrSingleton; - Lifespan = lifespan; - } - - /// Creates the reuse optionally specifying its name. - public CurrentScopeReuse(object name = null, bool scopedOrSingleton = false) - : this(name, scopedOrSingleton, DefaultLifespan) - { - } - - /// Flag indicating that it is a scope or singleton. - public readonly bool ScopedOrSingleton; - - /// [Obsolete("Replaced by `GetScopedOrSingletonViaFactoryDelegate`")] - public static object GetScopedOrSingleton(IResolverContext r, - int id, CreateScopedValue createValue, int disposalIndex) => - (r.CurrentScope ?? r.SingletonScope).GetOrAdd(id, createValue, disposalIndex); - - /// Subject - public static object GetScopedOrSingletonViaFactoryDelegate(IResolverContext r, - int id, FactoryDelegate createValue, int disposalIndex) => - (r.CurrentScope ?? r.SingletonScope).GetOrAddViaFactoryDelegate(id, createValue, r, disposalIndex); - - internal static readonly MethodInfo GetScopedOrSingletonViaFactoryDelegateMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetScopedOrSingletonViaFactoryDelegate)); - - /// Subject - public static object TrackScopedOrSingleton(IResolverContext r, object item) => - (r.CurrentScope ?? r.SingletonScope).TrackDisposable(item); - - internal static readonly MethodInfo TrackScopedOrSingletonMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(TrackScopedOrSingleton)); - - /// [Obsolete("Replaced by `GetScopedViaFactoryDelegate`")] - public static object GetScoped(IResolverContext r, - bool throwIfNoScope, int id, CreateScopedValue createValue, int disposalIndex) => - r.GetCurrentScope(throwIfNoScope)?.GetOrAdd(id, createValue, disposalIndex); - - /// Subject - public static object GetScopedViaFactoryDelegateNoDisposalIndex(IResolverContext r, - bool throwIfNoScope, int id, FactoryDelegate createValue) => - r.GetCurrentScope(throwIfNoScope)?.GetOrAddViaFactoryDelegate(id, createValue, r); - - internal static readonly MethodInfo GetScopedViaFactoryDelegateNoDisposalIndexMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetScopedViaFactoryDelegateNoDisposalIndex)); - - /// Subject - public static object GetScopedViaFactoryDelegate(IResolverContext r, - bool throwIfNoScope, int id, FactoryDelegate createValue, int disposalIndex) => - r.GetCurrentScope(throwIfNoScope)?.GetOrAddViaFactoryDelegate(id, createValue, r, disposalIndex); - - internal static readonly MethodInfo GetScopedViaFactoryDelegateMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetScopedViaFactoryDelegate)); - - /// [Obsolete("Replaced by `GetNameScopedViaFactoryDelegate`")] - public static object GetNameScoped(IResolverContext r, - object scopeName, bool throwIfNoScope, int id, CreateScopedValue createValue, int disposalIndex) => - r.GetNamedScope(scopeName, throwIfNoScope)?.GetOrAdd(id, createValue, disposalIndex); - - /// Subject - public static object GetNameScopedViaFactoryDelegate(IResolverContext r, - object scopeName, bool throwIfNoScope, int id, FactoryDelegate createValue, int disposalIndex) => - r.GetNamedScope(scopeName, throwIfNoScope)?.GetOrAddViaFactoryDelegate(id, createValue, r, disposalIndex); - - internal static readonly MethodInfo GetNameScopedViaFactoryDelegateMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(GetNameScopedViaFactoryDelegate)); - - /// Subject - public static object TrackScoped(IResolverContext r, bool throwIfNoScope, object item) => - r.GetCurrentScope(throwIfNoScope)?.TrackDisposable(item); - - internal static readonly MethodInfo TrackScopedMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(TrackScoped)); - - /// Subject - public static object TrackNameScoped(IResolverContext r, object scopeName, bool throwIfNoScope, object item) => - r.GetNamedScope(scopeName, throwIfNoScope)?.TrackDisposable(item); - - internal static readonly MethodInfo TrackNameScopedMethod = - typeof(CurrentScopeReuse).GetTypeInfo().GetDeclaredMethod(nameof(TrackNameScoped)); - } - - /// Abstracts way to match reuse and scope names - public interface IScopeName - { - /// Does the job. - bool Match(object scopeName); - } - - /// Represents multiple names - public sealed class CompositeScopeName : IScopeName - { - /// Wraps multiple names - public static CompositeScopeName Of(object[] names) => new CompositeScopeName(names); - - /// Matches all the name in a loop until first match is found, otherwise returns false. - public bool Match(object scopeName) - { - var names = _names; - for (var i = 0; i < names.Length; i++) - { - var name = names[i]; - if (name == scopeName) - return true; - var aScopeName = name as IScopeName; - if (aScopeName != null && aScopeName.Match(scopeName)) - return true; - if (scopeName != null && scopeName.Equals(name)) - return true; - } - - return false; - } - - private CompositeScopeName(object[] names) { _names = names; } - private readonly object[] _names; - } - - /// Holds the name for the resolution scope. - public sealed class ResolutionScopeName : IScopeName - { - /// Creates scope with specified service type and key - public static ResolutionScopeName Of(Type serviceType = null, object serviceKey = null) => - new ResolutionScopeName(serviceType, serviceKey); - - /// Creates scope with specified service type and key. - public static ResolutionScopeName Of(object serviceKey = null) => - new ResolutionScopeName(typeof(TService), serviceKey); - - /// Type of service opening the scope. - public readonly Type ServiceType; - - /// Optional service key of service opening the scope. - public readonly object ServiceKey; - - private ResolutionScopeName(Type serviceType, object serviceKey) - { - ServiceType = serviceType; - ServiceKey = serviceKey; - } - - /// - public bool Match(object scopeName) - { - var name = scopeName as ResolutionScopeName; - return name != null && - (ServiceType == null || - name.ServiceType.IsAssignableTo(ServiceType) || - ServiceType.IsOpenGeneric() && - name.ServiceType.GetGenericDefinitionOrNull().IsAssignableTo(ServiceType)) && - (ServiceKey == null || ServiceKey.Equals(name.ServiceKey)); - } - - /// String representation for easy debugging and understood error messages. - public override string ToString() - { - var s = new StringBuilder().Append("{ServiceType=").Print(ServiceType); - if (ServiceKey != null) - s.Append(", ServiceKey=").Print(ServiceKey); - return s.Append('}').ToString(); - } - } - - /// Specifies pre-defined reuse behaviors supported by container: - /// used when registering services into container with methods. - public static class Reuse - { - /// Synonym for absence of reuse. - public static readonly IReuse Transient = new TransientReuse(); - - /// Specifies to store single service instance per . - public static readonly IReuse Singleton = new SingletonReuse(); - - /// Same as InCurrentScope. From now on will be the default name. - public static readonly IReuse Scoped = new CurrentScopeReuse(); - - /// Same as InCurrentNamedScope. From now on will be the default name. - public static IReuse ScopedTo(object name) => new CurrentScopeReuse(name); - - /// Specifies all the scope details - public static IReuse ScopedTo(object name, bool scopedOrSingleton, int lifespan) => - new CurrentScopeReuse(name, scopedOrSingleton, lifespan); - - // todo: Should be renamed to `ScopedToMany` to prevent overload ambiguity - /// Scoped to multiple names. - public static IReuse ScopedTo(params object[] names) => - names.IsNullOrEmpty() ? Scoped - : names.Length == 1 ? ScopedTo(names[0]) - : new CurrentScopeReuse(CompositeScopeName.Of(names)); - - // todo: Consider changing the name to remove the ambiguity - /// Same as InResolutionScopeOf. From now on will be the default name. - public static IReuse ScopedTo(Type serviceType = null, object serviceKey = null) => - serviceType == null && serviceKey == null ? Scoped - : new CurrentScopeReuse(ResolutionScopeName.Of(serviceType, serviceKey)); - - /// Same as InResolutionScopeOf. From now on will be the default name. - public static IReuse ScopedTo(object serviceKey = null) => - ScopedTo(typeof(TService), serviceKey); - - /// The same as but if no open scope available will fallback to - /// The is applied the same way as for reuse. - public static readonly IReuse ScopedOrSingleton = new CurrentScopeReuse(scopedOrSingleton: true); - - /// Obsolete: same as . - [Obsolete("The same as Reuse.Scoped, please prefer to use Reuse.Scoped or the Reuse.ScopedTo to specify a bound service.")] - public static readonly IReuse InResolutionScope = Scoped; - - /// Obsolete: same as . - public static readonly IReuse InCurrentScope = Scoped; - - /// Returns current scope reuse with specific name to match with scope. - /// If name is not specified then function returns . - /// (optional) Name to match with scope. - /// Created current scope reuse. - public static IReuse InCurrentNamedScope(object name = null) => ScopedTo(name); - - /// Obsolete: please use ScopedTo instead. - public static IReuse InResolutionScopeOf(Type assignableFromServiceType = null, object serviceKey = null) => - ScopedTo(assignableFromServiceType, serviceKey); - - /// Obsolete: please use ScopedTo instead. - public static IReuse InResolutionScopeOf(object serviceKey = null) => - ScopedTo(serviceKey); - - /// Same as Scoped but requires . - public static readonly IReuse InThread = Scoped; - - // todo: Minimize usage of name for scopes, it will be more performant. e.g. ASP.NET Core does not use one. - /// Special name that by convention recognized by . - public static string WebRequestScopeName = "~WebRequestScopeName"; - - /// Obsolete: please prefer using instead. - /// The named scope has performance drawback comparing to just a scope. - /// If you need to distinguish nested scope, give names to them instead of naming the top web request scope. - public static readonly IReuse InWebRequest = ScopedTo(WebRequestScopeName); - -#region Implementation - - private sealed class TransientReuse : IReuse - { - public int Lifespan => 0; - - public object Name => null; - - public Expression Apply(Request _, Expression serviceFactoryExpr) => serviceFactoryExpr; - - public bool CanApply(Request request) => true; - - private readonly Lazy _transientReuseExpr = Lazy.Of(() => - Field(null, typeof(Reuse).Field(nameof(Transient)))); - - public Expression ToExpression(Func fallbackConverter) => - _transientReuseExpr.Value; - - public override string ToString() => "TransientReuse"; - } - -#endregion - } - - /// Policy to handle unresolved service. - public enum IfUnresolved - { - /// If service is unresolved for whatever means, it will throw the respective exception. - Throw, - /// If service is unresolved for whatever means, it will return default(serviceType) value. - ReturnDefault, - /// If service is not registered, then it will return default, for other errors it will throw. - ReturnDefaultIfNotRegistered, - } - - /// Declares minimal API for service resolution. - /// Resolve default and keyed is separated because of optimization for faster resolution of the former. - public interface IResolver -#if SUPPORTS_ISERVICE_PROVIDER - : IServiceProvider -#endif - - { - /// Resolves default (non-keyed) service from container and returns created service object. - /// Service type to search and to return. - /// Says what to do if service is unresolved. - /// Created service object or default based on provided. - object Resolve(Type serviceType, IfUnresolved ifUnresolved); - - /// Resolves service instance from container. - /// Service type to search and to return. - /// (optional) service key used for registering service. - /// (optional) Says what to do if service is unresolved. - /// (optional) Registered or wrapped service type to use instead of , - /// or wrapped type for generic wrappers. The type should be assignable to return . - /// (optional) Dependency chain info. - /// (optional) To specify the dependency objects to use instead of resolving them from container. - /// Created service object or default based on parameter. - object Resolve(Type serviceType, object serviceKey, - IfUnresolved ifUnresolved, Type requiredServiceType, Request preResolveParent, object[] args); - - /// Resolves all services registered for specified , or if not found returns - /// empty enumerable. If specified then returns only (single) service registered with this type. - /// Return type of an service item. - /// (optional) Resolve only single service registered with the key. - /// (optional) Actual registered service to search for. - /// Dependency resolution path info. - /// (optional) To specify the dependency objects to use instead of resolving them from container. - /// Enumerable of found services or empty. Does Not throw if no service found. - IEnumerable ResolveMany(Type serviceType, object serviceKey, - Type requiredServiceType, Request preResolveParent, object[] args); - } - - /// Specifies options to handle situation when registered service is already present in the registry. - public enum IfAlreadyRegistered - { - /// Appends new default registration or throws registration with the same key. - AppendNotKeyed, - /// Throws if default or registration with the same key is already exist. - Throw, - /// Keeps old default or keyed registration ignoring new registration: ensures Register-Once semantics. - Keep, - /// Replaces old registration with new one. - Replace, - /// Adds the new implementation or null (Made.Of), - /// otherwise keeps the previous registration of the same implementation type. - AppendNewImplementation - } - - /// Existing registration info. - public struct ServiceRegistrationInfo : IComparable - { - /// Registered factory. - public Factory Factory; - - /// Required service type. - public Type ServiceType; - - /// May be null for single default service, or for multiple default services. - public object OptionalServiceKey; - - /// Provides registration order across all factory registrations in container. - /// May be the same for factory registered with multiple services - /// OR for closed-generic factories produced from the single open-generic registration. - public int FactoryRegistrationOrder => Factory.RegistrationOrder; - - /// Implementation type if available. - public Type ImplementationType => Factory.CanAccessImplementationType ? Factory.ImplementationType : null; - - /// Shortcut to property, useful to find all roots - public bool AsResolutionRoot => Factory.Setup.AsResolutionRoot; - - /// Shortcut to service info. - public ServiceInfo ToServiceInfo() => ServiceInfo.Of(ServiceType, serviceKey: OptionalServiceKey); - - /// Overrides the service type and pushes the original service type to required service type - public ServiceInfo ToServiceInfo(Type serviceType) => - ServiceInfo.Of(serviceType, ServiceType, IfUnresolved.Throw, OptionalServiceKey); - - /// Overrides the service type and pushes the original service type to required service type - public ServiceInfo ToServiceInfo() => ToServiceInfo(typeof(TService)); - - /// Creates info. Registration order is figured out automatically based on Factory. - public ServiceRegistrationInfo(Factory factory, Type serviceType, object optionalServiceKey) - { - Factory = factory; - ServiceType = serviceType; - OptionalServiceKey = optionalServiceKey; - } - - /// Orders by registration - public int CompareTo(ServiceRegistrationInfo other) => Factory.FactoryID - other.Factory.FactoryID; - - /// Pretty-prints info to string. - public override string ToString() - { - var s = new StringBuilder().Print(ServiceType); - if (OptionalServiceKey != null) - s.Append(" with ServiceKey=").Print(OptionalServiceKey); - return s.Append(" with factory ").Append(Factory).ToString(); - } - } - - /// Defines operations that for changing registry, and checking if something exist in registry. - public interface IRegistrator - { - /// Registers factory in registry with specified service type and key for lookup. - /// Returns true if factory was added to registry, false otherwise. False may be in case of - /// setting and already existing factory - /// To register. - /// Service type as unique key in registry for lookup. - /// Service key as complementary lookup for the same service type. - /// Policy how to deal with already registered factory with same service type and key. - /// [performance] Confirms that service and implementation types are statically checked by compiler. - /// True if factory was added to registry, false otherwise. - /// False may be in case of setting and already existing factory. - void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked); - - /// Returns true if expected factory is registered with specified service key and type. - /// Not provided or null means to check the - /// alone with any service key. - bool IsRegistered(Type serviceType, object serviceKey, FactoryType factoryType, Func condition); - - /// Removes factory with specified service type and key from registry and cache. - /// BUT consuming services may still hold on the resolved service instance. - /// The cache of consuming services may also hold on the unregistered service. Use `IContainer.ClearCache` to clear all cache. - void Unregister(Type serviceType, object serviceKey, FactoryType factoryType, Func condition); - - /// Returns all registered service factories with their Type and optional Key. - /// Decorator and Wrapper types are not included. - IEnumerable GetServiceRegistrations(); - - /// Searches for registered factories by type, and key (if specified), - /// and by factory type (by default uses ). - /// May return empty, 1 or multiple factories. - Factory[] GetRegisteredFactories(Type serviceType, object serviceKey, FactoryType factoryType); - - /// Puts instance into the current scope or singletons. - void UseInstance(Type serviceType, object instance, IfAlreadyRegistered IfAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey); - - /// Puts instance created via the passed factory on demand into the current or singleton scope - void Use(Type serviceType, FactoryDelegate factory); - } - - /// What to do with registrations when creating the new container from the existent one. - public enum RegistrySharing - { - /// Shares both registrations and resolution cache if any - Share = 0, - /// Clones the registrations but preserves the resolution cache - CloneButKeepCache, - /// Clones the registrations and drops the cache -- full reset! - CloneAndDropCache - } - - /// Combines registrator and resolver roles, plus rules and scope management. - public interface IContainer : IRegistrator, IResolverContext - { - /// Rules for defining resolution/registration behavior throughout container. - Rules Rules { get; } - - /// Represents scope bound to container itself, and not an ambient (context) thingy. - IScope OwnCurrentScope { get; } - - // todo: replace with the below overload with more parameters - /// Creates new container from the current one by specifying the listed parameters. - /// If the null or default values are provided then the default or new values will be applied. - /// Nothing will be inherited from the current container. - /// If you want to inherit something you need to provide it as parameter. - IContainer With(Rules rules, IScopeContext scopeContext, RegistrySharing registrySharing, IScope singletonScope); - - /// Creates new container from the current one by specifying the listed parameters. - /// If the null or default values are provided then the default or new values will be applied. - /// Nothing will be inherited from the current container. - /// If you want to inherit something you need to provide it as parameter. - IContainer With(IResolverContext parent, Rules rules, IScopeContext scopeContext, - RegistrySharing registrySharing, IScope singletonScope, IScope currentScope); - - /// Produces new container which prevents any further registrations. - /// (optional)Controls what to do with registrations: ignore or throw exception. - /// Throws exception by default. - /// New container preserving all current container state but disallowing registrations. - IContainer WithNoMoreRegistrationAllowed(bool ignoreInsteadOfThrow = false); - - /// Searches for requested factory in registry, and then using . - /// Factory request. - /// Found factory, otherwise null if is set to . - Factory ResolveFactory(Request request); - - /// Searches for registered service factory and returns it, or null if not found. - /// Will use if specified. - /// Factory request. - /// Found factory or null. - Factory GetServiceFactoryOrDefault(Request request); - - /// Finds all registered default and keyed service factories and returns them. - /// It skips decorators and wrappers. - /// Service type to look for, may be open-generic type too. - /// (optional) For generic serviceType instructs to look for - /// both closed and open-generic registrations. - /// Enumerable of found pairs. - /// Returned Key item should not be null - it should be . - IEnumerable> GetAllServiceFactories(Type serviceType, bool bothClosedAndOpenGenerics = false); - - /// Searches for registered wrapper factory and returns it, or null if not found. - /// Service type to look for. Found wrapper factory or null. - Factory GetWrapperFactoryOrDefault(Type serviceType); - - /// Returns all decorators registered for the service type. Decorator factories. - Factory[] GetDecoratorFactoriesOrDefault(Type serviceType); - - /// Creates decorator expression: it could be either Func{TService,TService}, - /// or service expression for replacing decorators. - /// Decorated service request. - /// Decorator expression. - Expression GetDecoratorExpressionOrDefault(Request request); - - /// If is generic type then this method checks if the type registered as generic wrapper, - /// and recursively unwraps and returns its type argument. This type argument is the actual service type we want to find. - /// Otherwise, method returns the input . - /// Type to unwrap. Method will return early if type is not generic. - /// Required service type or null if don't care. - /// Unwrapped service type in case it corresponds to registered generic wrapper, or input type in all other cases. - Type GetWrappedType(Type serviceType, Type requiredServiceType); - - /// Converts known items into custom expression or wraps in a constant expression. - /// Item to convert. - /// (optional) Type of item, otherwise item . - /// (optional) Throws for non-primitive and not-recognized items, - /// identifying that result expression require run-time state. For compiled expression it means closure in lambda delegate. - /// Returns constant or state access expression for added items. - Expression GetConstantExpression(object item, Type itemType = null, bool throwIfStateRequired = false); - - /// Clears cache for specified service(s). But does not clear instances of already resolved/created singletons and scoped services! - /// Target service type. - /// (optional) If not specified, clears cache for all . - /// (optional) If omitted, the cache will be cleared for all registrations of . - /// True if target service was found, false - otherwise. - bool ClearCache(Type serviceType, FactoryType? factoryType, object serviceKey); - - /// Puts instance created via the passed factory on demand into the current or singleton scope - new void Use(Type serviceType, FactoryDelegate factory); - - /// [Obsolete("Replaced by `Use` to put runtime data into container scopes and with `RegisterInstance` as a sugar for `RegisterDelegate(_ => instance)`")] - new void UseInstance(Type serviceType, object instance, IfAlreadyRegistered IfAlreadyRegistered, - bool preventDisposal, bool weaklyReferenced, object serviceKey); - } - - /// Resolves all registered services of type on demand, - /// when enumerator called. If service type is not found, empty returned. - /// Service type to resolve. - public sealed class LazyEnumerable : IEnumerable - { - /// Exposes internal items enumerable. - public readonly IEnumerable Items; - - /// Wraps lazy resolved items. Lazy resolved items. - public LazyEnumerable(IEnumerable items) - { - Items = items.ThrowIfNull(); - } - - /// Return items enumerator. - public IEnumerator GetEnumerator() => Items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - /// Wrapper type to box service with associated arbitrary metadata object. - /// Service type. - /// Arbitrary metadata object type. - public sealed class Meta - { - /// Value or object with associated metadata. - public readonly T Value; - - /// Associated metadata object. Could be anything. - public readonly TMetadata Metadata; - - /// Boxes value and its associated metadata together. - public Meta(T value, TMetadata metadata) - { - Value = value; - Metadata = metadata; - } - } - - /// Exception that container throws in case of error. Dedicated exception type simplifies - /// filtering or catching container relevant exceptions from client code. -#if SUPPORTS_SERIALIZABLE - [Serializable] -#endif - [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "Not available in PCL.")] - public class ContainerException : InvalidOperationException - { - /// Error code of exception, possible values are listed in class. - public readonly int Error; - - /// Simplifies the access to the error name. - public string ErrorName => DryIoc.Error.NameOf(Error); - - /// Creates exception by wrapping and its message, - /// optionally with exception. - public static ContainerException Of(ErrorCheck errorCheck, int errorCode, - object arg0, object arg1 = null, object arg2 = null, object arg3 = null, Exception innerException = null) => - new ContainerException(errorCode, - string.Format(GetMessage(errorCheck, errorCode), Print(arg0), Print(arg1), Print(arg2), Print(arg3)), - innerException); - - /// Gets error message based on provided args. - protected static string GetMessage(ErrorCheck errorCheck, int errorCode) => - errorCode == -1 ? Throw.GetDefaultMessage(errorCheck) : DryIoc.Error.Messages[errorCode]; - - /// Prints argument for formatted message. To print. Printed string. - protected static string Print(object arg) => - arg == null ? string.Empty : new StringBuilder().Print(arg).ToString(); - - /// Creates exception with message describing cause and context of error, - /// and leading/system exception causing it. - public ContainerException(int errorCode, string message, Exception innerException) - : base($"code: {DryIoc.Error.NameOf(errorCode)}; message: {message}", innerException) - { - Error = errorCode; - } - - /// Creates exception with message describing cause and context of error. - public ContainerException(int error, string message) - : this(error, message, null) { } - -#if SUPPORTS_SERIALIZABLE - /// - protected ContainerException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - : base(info, context) {} -#endif - } - - /// Defines error codes and error messages for all DryIoc exceptions (DryIoc extensions may define their own.) - public static class Error - { - private static int _errorIndex = -1; - - /// List of error messages indexed with code. - public static readonly string[] Messages = new string[100]; - -#pragma warning disable 1591 // "Missing XML-comment" - public static readonly int - UnableToResolveUnknownService = Of( - "Unable to resolve {0}" + NewLine + - "Where no service registrations found" + NewLine + - " and no dynamic registrations found in {1} of Rules.DynamicServiceProviders" + NewLine + - " and nothing found in {2} of Rules.UnknownServiceResolvers"), - - UnableToResolveFromRegisteredServices = Of( - "Unable to resolve {0}" + NewLine + - " with normal and dynamic registrations:" + NewLine + "{1}"), - - ExpectedSingleDefaultFactory = Of( - "Expecting single default registration but found many:" + NewLine + "{0}" + NewLine + - "when resolving {1}." + NewLine + - "Please identify service with key, or metadata, or use Rules.WithFactorySelector to specify single registered factory."), - - RegisteringImplementationNotAssignableToServiceType = Of( - "Registering implementation type {0} is not assignable to service type {1}."), - RegisteredFactoryMethodResultTypesIsNotAssignableToImplementationType = Of( - "Registered factory method return type {1} should be assignable Or castable to implementation type {0} but it is not."), - RegisteringOpenGenericRequiresFactoryProvider = Of( - "Unable to register delegate factory for open-generic service {0}." + NewLine + - "You need to specify concrete (closed) service type returned by delegate."), - RegisteringOpenGenericImplWithNonGenericService = Of( - "Unable to register open-generic implementation {0} with non-generic service {1}."), - RegisteringOpenGenericServiceWithMissingTypeArgs = Of( - "Unable to register open-generic implementation {0} because service {1} should specify all type arguments, but specifies only {2}."), - RegisteringNotAGenericTypedefImplType = Of( - "Unsupported registration of implementation {0} which is not a generic type definition but contains generic parameters." + NewLine + - "Consider to register generic type definition {1} instead."), - RegisteringNotAGenericTypedefServiceType = Of( - "Unsupported registration of service {0} which is not a generic type definition but contains generic parameters." + NewLine + - "Consider to register generic type definition {1} instead."), - RegisteringNullImplementationTypeAndNoFactoryMethod = Of( - "Registering without implementation type and without FactoryMethod to use instead."), - RegisteringObjectTypeAsImplementationIsNotSupported = Of( - "Registering `System.Object` type as implementation without a factory method is not supported."), - RegisteringAbstractImplementationTypeAndNoFactoryMethod = Of( - "Registering abstract implementation type {0} when it is should be concrete. Also there is not FactoryMethod to use instead."), - UnableToSelectSinglePublicConstructorFromMultiple = Of( - "Unable to select single public constructor from implementation type {0}:" + NewLine + - "{1}"), - UnableToSelectSinglePublicConstructorFromNone = Of( - "Unable to select single public constructor from implementation type {0} because it does not have one."), - NoMatchedImplementedTypesWithServiceType = Of( - "Unable to match service with open-generic {0} implementing {1} when resolving {2}."), - NoMatchedFactoryMethodDeclaringTypeWithServiceTypeArgs = Of( - "Unable to match open-generic factory method Declaring type {0} with requested service type arguments <{1}> when resolving {2}."), - NoMatchedFactoryMethodWithServiceTypeArgs = Of( - "Unable to match open-generic factory method {0} with requested service type arguments <{1}> when resolving {2}."), - OpenGenericFactoryMethodDeclaringTypeIsNotSupportedOnThisPlatform = Of( - "[Specific to this .NET version] Unable to match method or constructor {0} from open-generic declaring type {1} to closed-generic type {2}, " + - NewLine + - "Please give the method an unique name to distinguish it from other overloads."), - ResolvingOpenGenericServiceTypeIsNotPossible = Of( - "Resolving open-generic service type is not possible for type: {0}."), - RecursiveDependencyDetected = Of( - "Recursive dependency is detected when resolving" + NewLine + "{0}."), - ScopeIsDisposed = Of( - "Scope {0} is disposed and scoped instances are disposed and no longer available."), - NotFoundOpenGenericImplTypeArgInService = Of( - "Unable to find for open-generic implementation {0} the type argument {1} when resolving {2}."), - UnableToSelectCtor = Of( - "Unable to get constructor of {0} using provided constructor selector when resolving {1}."), - UnableToFindCtorWithAllResolvableArgs = Of( - "Unable to find most resolvable constructor also including passed input arguments `{0}` " + - NewLine + " when resolving: {1}."), - RegisteredDelegateResultIsNotOfServiceType = Of( - "Registered factory delegate returns service {0} is not assignable to desired service {1}."), - NotFoundSpecifiedWritablePropertyOrField = Of( - "Unable to find writable property or field {0} when resolving: {1}."), - PushingToRequestWithoutFactory = Of( - "Pushing the next request {0} into parent request not yet resolved to factory: {1}"), - NoMatchedGenericParamConstraints = Of( - "Open-generic service does not match with registered open-generic implementation constraints {0} when resolving: {1}."), - GenericWrapperWithMultipleTypeArgsShouldSpecifyArgIndex = Of( - "Generic wrapper type {0} should specify what type argument is wrapped, but it does not."), - GenericWrapperTypeArgIndexOutOfBounds = Of( - "Registered generic wrapper {0} specified type argument index {1} is out of type argument list."), - DependencyHasShorterReuseLifespan = Of( - "Dependency {0} with reuse {1} has shorter lifespan than its parent's {2}" + NewLine + - "If you know what you're doing you may disable this error with the rule `new Container(rules => rules.WithoutThrowIfDependencyHasShorterReuseLifespan())`."), - WeakRefReuseWrapperGCed = Of( - "Reused service wrapped in WeakReference is Garbage Collected and no longer available."), - ServiceIsNotAssignableFromFactoryMethod = Of( - "Service of {0} is not assignable from factory method {1} when resolving: {2}."), - GotNullConstructorFromFactoryMethod = Of( - "Got null constructor when resolving {0}"), - UnableToRegisterDuplicateDefault = Of( - "The default service {0} without key {1} is already registered as {2}."), - UnableToRegisterDuplicateKey = Of( - "Unable to register service with duplicate key '{0}': {1}" + NewLine + - " There is already registered service with the same key {2}."), - NoCurrentScope = Of( - "No current scope is available: probably you are registering to, or resolving from outside of the scope. " + NewLine + - "Current resolver context is: {0}."), - ContainerIsDisposed = Of( - "Container is disposed and should not be used: {0}"), - NoMatchedScopeFound = Of( - "Unable to find matching scope with name {0} starting from the current scope {1}."), - NotSupportedMadeOfExpression = Of( - "Expected expression of method call, property getter, or new statement (with optional property initializer), " + - "but found this Made.Of expression: {0}"), - UnexpectedFactoryMemberExpressionInMadeOf = Of( - "Expected property getter, but found not supported `{0}` " + NewLine + - "in Made.Of expression: `{1}`"), - UnexpectedExpressionInsteadOfArgMethodInMadeOf = Of( - "Expected `Arg.Of` method call to specify parameter, property or field, but found `{0}` " + NewLine + - "in Made.Of expression: `{1}`"), - UnexpectedExpressionInsteadOfConstantInMadeOf = Of( - "Expected `ConstantExpression` for value of parameter, property, or field, but found `{0}` " + NewLine + - "in Made.Of expression: `{1}`"), - InjectedCustomValueIsOfDifferentType = Of( - "Injected value {0} is not assignable to {1} when resolving: {2}"), - NoConversionOperatorFoundWhenInterpretingTheConvertExpression = Of( - "There is no explicit or implicit conversion operator found when interpreting {0} to {1} in expression: {2}"), - StateIsRequiredToUseItem = Of( - "Runtime state is required to inject (or use) the: {0}. " + NewLine + - "The reason is using RegisterDelegate, Use (or UseInstance), RegisterInitializer/Disposer, or registering with non-primitive service key, or metadata." + NewLine + - "You can convert run-time value to expression via container.With(rules => rules.WithItemToExpressionConverter(YOUR_ITEM_TO_EXPRESSION_CONVERTER))."), - ArgValueIndexIsProvidedButNoArgValues = Of( - "`Arg.Index` is provided but no values are passed in Made.Of expression: " + NewLine + - "{0}"), - ArgValueIndexIsOutOfProvidedArgValues = Of( - "`Arg.Index` {0} is outside of provided values [{1}] in Made.Of expression: " + NewLine + - "{2}"), - ResolutionNeedsRequiredServiceType = Of( - "Expecting required service type but it is not specified when resolving: {0}"), - RegisterMappingNotFoundRegisteredService = Of( - "When registering mapping, Container is unable to find factory of registered service type {0} and key {1}."), - RegisterMappingUnableToSelectFromMultipleFactories = Of( - "RegisterMapping selected more than 1 factory with provided type {0} and key {1}: {2}"), - RegisteringInstanceNotAssignableToServiceType = Of( - "Registered instance of type {0} is not assignable to serviceType {1}."), - NoMoreRegistrationsAllowed = Of( - "Container does not allow further registrations." + NewLine + - "Attempting to register {0}{1} with implementation factory {2}."), - NoMoreUnregistrationsAllowed = Of( - "Container does not allow further registry modification." + NewLine + - "Attempting to Unregister {0}{1} with factory type {2}."), - GotNullFactoryWhenResolvingService = Of( - "Got null factory method when resolving {0}"), - RegisteredDisposableTransientWontBeDisposedByContainer = Of( - "Registered Disposable Transient service {0} with key {1} registered as {2} won't be disposed by container." + - " DryIoc does not hold reference to resolved transients, and therefore does not control their dispose." + - " To silence this exception Register(setup: Setup.With(allowDisposableTransient: true)) " + - " or set the rule Container(rules => rules.WithoutThrowOnRegisteringDisposableTransient())." + - " To enable tracking use Register(setup: Setup.With(trackDisposableTransient: true)) " + - " or set the rule Container(rules => rules.WithTrackingDisposableTransients())"), - NotFoundMetaCtorWithTwoArgs = Of( - "Expecting Meta wrapper public constructor with two arguments {0} but not found when resolving: {1}"), - UnableToSelectFromManyRegistrationsWithMatchingMetadata = Of( - "Unable to select from multiple registrations matching the Metadata type {0}:" + NewLine + - "{1}" + NewLine + - "When resolving: {2}"), - ImplTypeIsNotSpecifiedForAutoCtorSelection = Of( - "Implementation type is not specified when using automatic constructor selection: {0}"), - NoImplementationForPlaceholder = Of( - "There is no real implementation, only a placeholder for the service {0}." + NewLine + - "Please Register the implementation with the ifAlreadyRegistered.Replace parameter to fill the placeholder."), - UnableToFindSingletonInstance = Of( - "Expecting the instance to be stored in singleton scope, but unable to find anything here." + NewLine + - "Likely, you've called UseInstance from the scoped container, but resolving from another container or injecting into a singleton."), - DecoratorShouldNotBeRegisteredWithServiceKey = Of( - "Registering Decorator {0} with service key {1} is not supported," + NewLine + - "because instead of decorator with the key you actually want a decorator for service registered with the key." + NewLine + - "To apply decorator for service with the key, please use `Setup.DecoratorOf(decorateeServiceKey: \"a service key\")`"), - PassedCtorOrMemberIsNull = Of( - "The constructor of member info passed to `Made.Of` or `FactoryMethod.Of` is null"), - PassedMemberIsNotStaticButInstanceFactoryIsNull = Of( - "The member info {0} passed to `Made.Of` or `FactoryMethod.Of` is NOT static, but instance factory is not provided or null"), - PassedMemberIsStaticButInstanceFactoryIsNotNull = Of( - "You are passing constructor or STATIC member info {0} to `Made.Of` or `FactoryMethod.Of`, but then why are you passing factory INSTANCE: {1}"), - UndefinedMethodWhenGettingTheSingleMethod = Of( - "Undefined Method '{0}' in Type {1} (including non-public={2})"), - UndefinedMethodWhenGettingMethodWithSpecifiedParameters = Of( - "Undefined Method '{0}' in Type {1} with parameters {2}."), - UndefinedPropertyWhenGettingProperty = Of("Undefined property {0} in type {1}"), - UndefinedFieldWhenGettingField = Of("Undefined field {0} in type {1}"), - UnableToFindConstructorWithArgs = Of("Unable to find a constructor in Type {0} with args: {1}"), - UnableToFindSingleConstructor = Of( - "Unable to find a single constructor in Type {0} (including non-public={1})"), - DisposerTrackForDisposeError = Of("Something is {0} already."), - NoServicesWereRegisteredByRegisterMany = Of( - "No service types were discovered in `RegisterMany` (or in `RegisterInstanceMany`) for the specified implementation types: " + NewLine + - "[{0}]" + NewLine + - "Maybe you missed the implementation or service type(s), " + - "e.g. provided only abstract or compiler-generated implementation types, " + - "or specified a wrong `serviceTypeCondition`," + - "or did not specify to use `nonPublicServiceTypes`, etc."), - FoundNoRootsToValidate = Of( - "No roots to Validate found. Check the `condition` passed to Validate method for container: {0}" + NewLine + - "You may also examine all container registrations via `container.container.GetServiceRegistrations()` method."), - UnableToInterpretTheNestedLambda = Of( - "Unable to interpret the nested lambda with Body:" + NewLine + - "{0}") - ; - -#pragma warning restore 1591 // "Missing XML-comment" - - private static int Of(string message) - { - var errorIndex = Interlocked.Increment(ref _errorIndex); - Messages[errorIndex] = message; - return errorIndex; - } - - /// Returns the name of error with the provided error code. - public static string NameOf(int error) => - typeof(Error).GetTypeInfo().DeclaredFields - .Where(f => f.FieldType == typeof(int)).Where((_, i) => i == error + 1) - .FirstOrDefault()?.Name; - - static Error() - { - Throw.GetMatchedException = ContainerException.Of; - } - } - - /// Checked error condition, possible error sources. - public enum ErrorCheck - { - /// Unspecified, just throw. - Unspecified, - /// Predicate evaluated to false. - InvalidCondition, - /// Checked object is null. - IsNull, - /// Checked object is of unexpected type. - IsNotOfType, - /// Checked type is not assignable to expected type - TypeIsNotOfType, - /// Invoked operation throws, it is source of inner exception. - OperationThrows, - } - - /// Enables more clean error message formatting and a bit of code contracts. - public static class Throw - { - private static string[] CreateDefaultMessages() - { - var messages = new string[(int)ErrorCheck.OperationThrows + 1]; - messages[(int)ErrorCheck.Unspecified] = "The error reason is unspecified, which is bad thing."; - messages[(int)ErrorCheck.InvalidCondition] = "Argument {0} of type {1} has invalid condition."; - messages[(int)ErrorCheck.IsNull] = "Argument of type {0} is null."; - messages[(int)ErrorCheck.IsNotOfType] = "Argument {0} is not of type {1}."; - messages[(int)ErrorCheck.TypeIsNotOfType] = "Type argument {0} is not assignable from type {1}."; - messages[(int)ErrorCheck.OperationThrows] = "Invoked operation throws the inner exception {0}."; - return messages; - } - - private static readonly string[] _defaultMessages = CreateDefaultMessages(); - - /// Returns the default message specified for code. - public static string GetDefaultMessage(ErrorCheck error) => - _defaultMessages[(int)error]; - - /// Declares mapping between type and code to specific . - public delegate Exception GetMatchedExceptionHandler(ErrorCheck errorCheck, int error, object arg0, object arg1, object arg2, object arg3, Exception inner); - - /// Returns matched exception for error check and error code. - public static GetMatchedExceptionHandler GetMatchedException = ContainerException.Of; - - /// Throws matched exception with provided error code if throw condition is true. - public static void If(bool throwCondition, int error = -1, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - if (throwCondition) - throw GetMatchedException(ErrorCheck.InvalidCondition, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws matched exception with provided error code if throw condition is true. - /// Otherwise returns source . - public static T ThrowIf(this T arg0, bool throwCondition, int error = -1, object arg1 = null, object arg2 = null, object arg3 = null) - { - if (!throwCondition) return arg0; - throw GetMatchedException(ErrorCheck.InvalidCondition, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws exception if is null, otherwise returns . - public static T ThrowIfNull(this T arg, int error = -1, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - where T : class - { - if (arg != null) return arg; - throw GetMatchedException(ErrorCheck.IsNull, error, arg0 ?? typeof(T), arg1, arg2, arg3, null); - } - - /// Throws exception if is not assignable to type specified by , - /// otherwise just returns . - public static T ThrowIfNotInstanceOf(this T arg0, Type arg1, int error = -1, object arg2 = null, object arg3 = null) - where T : class - { - if (arg1.IsTypeOf(arg0)) return arg0; - throw GetMatchedException(ErrorCheck.IsNotOfType, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws if is not assignable from . - public static Type ThrowIfNotImplementedBy(this Type arg0, Type arg1, int error = -1, object arg2 = null, object arg3 = null) - { - if (arg1.IsAssignableTo(arg0)) return arg0; - throw GetMatchedException(ErrorCheck.TypeIsNotOfType, error, arg0, arg1, arg2, arg3, null); - } - - /// Invokes and in case of re-throws it as inner-exception. - public static T IfThrows(Func operation, bool throwCondition, int error, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) where TEx : Exception - { - try - { - return operation(); - } - catch (TEx ex) - { - if (throwCondition) - throw GetMatchedException(ErrorCheck.OperationThrows, error, arg0, arg1, arg2, arg3, ex); - return default(T); - } - } - - /// Just throws the exception with the code. - public static object It(int error, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - throw GetMatchedException(ErrorCheck.Unspecified, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws instead of returning value of . - /// Supposed to be used in expression that require some return value. - public static T For(int error, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - throw GetMatchedException(ErrorCheck.Unspecified, error, arg0, arg1, arg2, arg3, null); - } - - /// Throws if contidion is true, otherwise returns the `default(T)` value - public static T For(bool throwCondition, int error, - object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null) - { - if (!throwCondition) return default(T); - throw GetMatchedException(ErrorCheck.Unspecified, error, arg0, arg1, arg2, arg3, null); - } - } - - /// Called from generated code. - public static class ThrowInGeneratedCode - { - /// Throws if object is null. - public static object WeakRefReuseWrapperGCed(this object obj) - { - if (obj == null) Throw.It(Error.WeakRefReuseWrapperGCed); - return obj; - } - } - - /// Contains helper methods to work with Type: for instance to find Type implemented base types and interfaces, etc. - public static class ReflectionTools - { -#if SUPPORTS_DELEGATE_METHOD - private static Lazy> _preserveExceptionStackTraceAction = new Lazy>(() => - typeof(Exception).GetSingleMethodOrNull("InternalPreserveStackTrace", true) - ?.To(x => x.CreateDelegate(typeof(Action)).To>())); - - /// Preserves the stack trace becfore re-throwing. - public static void TryRethrowWithPreservedStackTrace(this Exception ex) - { - _preserveExceptionStackTraceAction.Value?.Invoke(ex); - throw ex; - } -#else - /// Preserves the stack trace becfore re-throwing. - public static void TryRethrowWithPreservedStackTrace(this Exception ex) - { - throw ex; - } -#endif - - /// Flags for method. - [Flags] - public enum AsImplementedType - { - /// Include nor object not source type. - None = 0, - /// Include source type to list of implemented types. - SourceType = 1, - /// Include type to list of implemented types. - ObjectType = 2 - } - - /// Returns all interfaces and all base types (in that order) implemented by . - /// Specify to include itself as first item and - /// type as the last item. - public static Type[] GetImplementedTypes(this Type sourceType, AsImplementedType asImplementedType = AsImplementedType.None) - { - Type[] results; - - var sourceTypeInfo = sourceType.GetTypeInfo(); - var interfaces = sourceTypeInfo.ImplementedInterfaces.ToArrayOrSelf(); - var interfaceStartIndex = (asImplementedType & AsImplementedType.SourceType) == 0 ? 0 : 1; - var includingObjectType = (asImplementedType & AsImplementedType.ObjectType) == 0 ? 0 : 1; - var sourcePlusInterfaceCount = interfaceStartIndex + interfaces.Length; - - var baseType = sourceTypeInfo.BaseType; - if (baseType == null || baseType == typeof(object)) - results = new Type[sourcePlusInterfaceCount + includingObjectType]; - else - { - List baseBaseTypes = null; - for (var bb = baseType.GetTypeInfo().BaseType; bb != null && bb != typeof(object); bb = bb.GetTypeInfo().BaseType) - (baseBaseTypes ?? (baseBaseTypes = new List(2))).Add(bb); - - if (baseBaseTypes == null) - results = new Type[sourcePlusInterfaceCount + includingObjectType + 1]; - else - { - results = new Type[sourcePlusInterfaceCount + baseBaseTypes.Count + includingObjectType + 1]; - baseBaseTypes.CopyTo(results, sourcePlusInterfaceCount + 1); - } - - results[sourcePlusInterfaceCount] = baseType; - } - - if (interfaces.Length == 1) - results[interfaceStartIndex] = interfaces[0]; - else if (interfaces.Length > 1) - Array.Copy(interfaces, 0, results, interfaceStartIndex, interfaces.Length); - - if (interfaceStartIndex == 1) - results[0] = sourceType; - if (includingObjectType == 1) - results[results.Length - 1] = typeof(object); - - return results; - } - - /// Gets a collection of the interfaces implemented by the current type and its base types. - public static Type[] GetImplementedInterfaces(this Type type) => - type.GetTypeInfo().ImplementedInterfaces.ToArrayOrSelf(); - - /// Gets all declared and if specified, the base members too. - public static IEnumerable GetAllMembers(this Type type, bool includeBase = false) => - type.GetMembers(t => - t.DeclaredMethods.Cast().Concat( - t.DeclaredProperties.Cast().Concat( - t.DeclaredFields.Cast())), - includeBase); - - /// Returns true if the contains all generic parameters - /// from . - public static bool ContainsAllGenericTypeParameters(this Type openGenericType, Type[] genericParameters) - { - if (!openGenericType.IsOpenGeneric()) - return false; - - var matchedParams = new Type[genericParameters.Length]; - Array.Copy(genericParameters, 0, matchedParams, 0, genericParameters.Length); - - ClearGenericParametersReferencedInConstraints(matchedParams); - ClearMatchesFoundInGenericParameters(matchedParams, openGenericType.GetGenericParamsAndArgs()); - - for (var i = 0; i < matchedParams.Length; i++) - if (matchedParams[i] != null) - return false; - return true; - } - - /// Returns true if class is compiler generated. Checking for CompilerGeneratedAttribute - /// is not enough, because this attribute is not applied for classes generated from "async/await". - public static bool IsCompilerGenerated(this Type type) => - type.FullName != null && type.FullName.Contains("<>c__DisplayClass"); - - /// Returns true if type is generic. - public static bool IsGeneric(this Type type) => - type.GetTypeInfo().IsGenericType; - - /// Returns true if type is generic type definition (open type). - public static bool IsGenericDefinition(this Type type) => - type.GetTypeInfo().IsGenericTypeDefinition; - - /// Returns true if type is closed generic: does not have open generic parameters, only closed/concrete ones. - public static bool IsClosedGeneric(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsGenericType && - !typeInfo.IsGenericTypeDefinition && - AreAllTypeArgumentsClosed(typeInfo.GetGenericParamsAndArgsUnsafe()); - } - - /// Returns true if type if open generic: contains at list one open generic parameter. Could be - /// generic type definition as well. - public static bool IsOpenGeneric(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsGenericTypeDefinition || - typeInfo.IsGenericType && - !AreAllTypeArgumentsClosed(typeInfo.GetGenericParamsAndArgsUnsafe()); - } - - /// Checks that all type args are closed. - internal static bool AreAllTypeArgumentsClosed(this Type[] typeArgs) - { - foreach (var typeArg in typeArgs) - { - if (typeArg.IsGenericParameter) - return false; - var typeArgInfo = typeArg.GetTypeInfo(); - if (!typeArgInfo.IsGenericType) - continue; - if (!AreAllTypeArgumentsClosed(typeArgInfo.GetGenericParamsAndArgsUnsafe())) - return false; - } - - return true; - } - - /// Returns generic type definition if type is generic and null otherwise. - public static Type GetGenericDefinitionOrNull(this Type type) => - type != null && type.GetTypeInfo().IsGenericType ? type.GetGenericTypeDefinition() : null; - - /// Returns generic type parameters and arguments in order they specified. If type is not generic, returns empty array. - public static Type[] GetGenericParamsAndArgs(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsGenericTypeDefinition || typeInfo.IsGenericType - ? typeInfo.GetGenericParamsAndArgsUnsafe() - : Empty(); - } - - [MethodImpl((MethodImplOptions)256)] - internal static Type[] GetGenericParamsAndArgsUnsafe(this TypeInfo typeInfo) - { -#if PCL || PCL259 || PCL328 || NET35 || NET40 || NET403 || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 - return typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters : typeInfo.GenericTypeArguments; -#else - return typeInfo.GetGenericArguments(); -#endif - } - - /// Returns array of interface and base class constraints for provider generic parameter type. - public static Type[] GetGenericParamConstraints(this Type type) => - type.GetTypeInfo().GetGenericParameterConstraints(); - - /// If type is array returns is element type, otherwise returns null. - /// Source type. Array element type or null. - public static Type GetArrayElementTypeOrNull(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsArray ? typeInfo.GetElementType() : null; - } - - /// Return base type or null, if not exist (the case for only for object type). - public static Type GetBaseType(this Type type) => - type.GetTypeInfo().BaseType; - - /// Checks if type is public or nested public in public type. - public static bool IsPublicOrNestedPublic(this Type type) - { - var ti = type.GetTypeInfo(); - return ti.IsPublic || ti.IsNestedPublic && ti.DeclaringType.IsPublicOrNestedPublic(); - } - - /// Returns true if type is class. - public static bool IsClass(this Type type) => - type.GetTypeInfo().IsClass; - - /// Returns true if type is value type. - public static bool IsValueType(this Type type) => - type.GetTypeInfo().IsValueType; - - /// Returns true if type is interface. - public static bool IsInterface(this Type type) => - type.GetTypeInfo().IsInterface; - - /// Returns true if type if abstract or interface. - public static bool IsAbstract(this Type type) => - type.GetTypeInfo().IsAbstract; - - /// Returns true if type is static. - public static bool IsStatic(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsAbstract && typeInfo.IsSealed; - } - - /// Returns true if type is enum type. - public static bool IsEnum(this Type type) => - type.GetTypeInfo().IsEnum; - - /// Returns true if type can be casted with conversion operators. - public static bool HasConversionOperatorTo(this Type sourceType, Type targetType) => - (sourceType.FindConvertOperator(sourceType, targetType) ?? - targetType.FindConvertOperator(sourceType, targetType)) != null; - - /// Returns `target source.op_(Explicit|Implicit)(source)` or null if not found - public static MethodInfo GetSourceConversionOperatorToTarget(this Type sourceType, Type targetType) => - sourceType.FindConvertOperator(sourceType, targetType); - - /// Returns `target target.op_(Explicit|Implicit)(source)` or null if not found - public static MethodInfo GetTargetConversionOperatorFromSource(this Type sourceType, Type targetType) => - targetType.FindConvertOperator(sourceType, targetType); - - internal static MethodInfo FindConvertOperator(this Type type, Type sourceType, Type targetType) - { - var methods = type.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsStatic && m.IsSpecialName && m.ReturnType == targetType) - { - var n = m.Name; - // n == "op_Implicit" || n == "op_Explicit" - if (n.Length == 11 && - n[2] == '_' && n[5] == 'p' && n[6] == 'l' && n[7] == 'i' && n[8] == 'c' && n[9] == 'i' && n[10] == 't' && - m.GetParameters()[0].ParameterType == sourceType) - return m; - } - } - - return null; - } - - /// Returns true if type is assignable to type. - public static bool IsAssignableTo(this Type type, Type other) => - type != null && other != null && other.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()); - - /// Returns true if type is assignable to type. - public static bool IsAssignableTo(this Type type) => - type != null && typeof(T).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()); - - /// Returns true if type of is assignable to source . - public static bool IsTypeOf(this Type type, object obj) => - obj != null && obj.GetType().IsAssignableTo(type); - - /// Returns true if provided type IsPrimitive in .Net terms, or enum, or string, - /// or array of primitives if is true. - public static bool IsPrimitive(this Type type, bool orArrayOfPrimitives = false) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsPrimitive || typeInfo.IsEnum || type == typeof(string) - || orArrayOfPrimitives && typeInfo.IsArray && typeInfo.GetElementType().IsPrimitive(true); - } - - /// Returns all attributes defined on . - public static Attribute[] GetAttributes(this Type type, Type attributeType = null, bool inherit = false) => - type.GetTypeInfo().GetCustomAttributes(attributeType ?? typeof(Attribute), inherit) - // ReSharper disable once RedundantEnumerableCastCall - .Cast() // required in .NET 4.5 - .ToArrayOrSelf(); - - /// Recursive method to enumerate all input type and its base types for specific details. - /// Details are returned by delegate. - public static IEnumerable GetMembers( - this Type type, Func> getMembers, bool includeBase = false) - { - var typeInfo = type.GetTypeInfo(); - var members = getMembers(typeInfo); - if (!includeBase || typeInfo.BaseType == null || typeInfo.BaseType == typeof(object)) - return members; - return members.Append(typeInfo.BaseType.GetMembers(getMembers, true)); - } - - /// Returns all public instance constructors for the type - public static IEnumerable PublicConstructors(this Type type) - { - foreach (var x in type.GetTypeInfo().DeclaredConstructors) - if (x.IsPublic && !x.IsStatic) - yield return x; - } - - /// Returns all public instance constructors for the type - public static IEnumerable PublicAndInternalConstructors(this Type type) - { - foreach (var x in type.GetTypeInfo().DeclaredConstructors) - if (!x.IsPrivate && !x.IsStatic) - yield return x; - } - - //todo: [Obsolete("Prefer to use `GetInstanceConstructors` and `GetPublicInstanceConstructors`")] - /// Enumerates all constructors from input type. - public static IEnumerable Constructors(this Type type, - bool includeNonPublic = false, bool includeStatic = false) - { - var ctors = type.GetTypeInfo().DeclaredConstructors.ToArrayOrSelf(); - if (ctors.Length == 0) - return ctors; - - var ctor0 = ctors[0]; - var skip0 = !includeNonPublic && !ctor0.IsPublic || !includeStatic && ctor0.IsStatic; - if (ctors.Length == 1) - return skip0 ? ArrayTools.Empty() : ctors; - - if (ctors.Length == 2) - { - var ctor1 = ctors[1]; - var skip1 = !includeNonPublic && !ctor1.IsPublic || !includeStatic && ctor1.IsStatic; - if (skip0 && skip1) - return ArrayTools.Empty(); - if (skip0) - return new[] { ctor1 }; - if (skip1) - return new[] { ctor0 }; - return ctors; - } - - if (!includeNonPublic && !includeStatic) - return ctors.Match(x => !x.IsStatic && x.IsPublic); - if (!includeNonPublic) - return ctors.Match(x => x.IsPublic); - if (!includeStatic) - return ctors.Match(x => !x.IsStatic); - return ctors; - } - - /// Returns public and non-public instance constructors. - public static ConstructorInfo[] GetInstanceConstructors(this Type type) - { - var ctorsEnumerable = type.GetTypeInfo().DeclaredConstructors; - var ctors = ctorsEnumerable as ConstructorInfo[] ?? ctorsEnumerable.ToArray(); - if (ctors.Length == 0) - return ctors; - - var ctor0 = ctors[0]; - if (ctors.Length == 1) - return ctor0.IsStatic ? ArrayTools.Empty() : ctors; - - if (ctors.Length == 2) - { - var ctor1 = ctors[1]; - if (ctor0.IsStatic && ctor1.IsStatic) - return ArrayTools.Empty(); - if (ctor0.IsStatic) - return new[] { ctor1 }; - if (ctor1.IsStatic) - return new[] { ctor0 }; - return ctors; - } - - return ctors.MatchUnsafe(x => !x.IsStatic); - } - - /// Returns public-only instance constructors. - public static ConstructorInfo[] GetPublicInstanceConstructors(this Type type) - { - var ctorsEnumerable = type.GetTypeInfo().DeclaredConstructors; - var ctors = ctorsEnumerable as ConstructorInfo[] ?? ctorsEnumerable.ToArray(); - if (ctors.Length == 0) - return ctors; - - var ctor0 = ctors[0]; - var skip0 = !ctor0.IsPublic || ctor0.IsStatic; - if (ctors.Length == 1) - return skip0 ? ArrayTools.Empty() : ctors; - - if (ctors.Length == 2) - { - var ctor1 = ctors[1]; - var skip1 = !ctor1.IsPublic || ctor1.IsStatic; - if (skip0 && skip1) - return ArrayTools.Empty(); - if (skip0) - return new[] { ctor1 }; - if (skip1) - return new[] { ctor0 }; - return ctors; - } - - return ctors.MatchUnsafe(x => !x.IsStatic && x.IsPublic); - } - - /// Searches and returns the first constructor by its signature, e.g. with the same number of parameters of the same type. - public static ConstructorInfo GetConstructorOrNull(this Type type, bool includeNonPublic = false, params Type[] args) - { - var argsLength = args.Length; - var ctors = includeNonPublic ? type.GetInstanceConstructors() : type.GetPublicInstanceConstructors(); - foreach (var ctor in ctors) - { - var ctorParams = ctor.GetParameters(); - if (ctorParams.Length == argsLength) - { - var i = 0; - for (; i < argsLength; ++i) - { - var paramType = ctorParams[i].ParameterType; - if (paramType != args[i] && paramType.GetGenericDefinitionOrNull() != args[i]) - break; - } - - if (i == argsLength) - return ctor; - } - } - - return null; - } - - /// Searches and returns constructor by its signature. - public static ConstructorInfo GetConstructorOrNull(this Type type, params Type[] args) => - type.GetConstructorOrNull(true, args); - - /// Searches and returns constructor by its signature, or throws if not found - public static ConstructorInfo Constructor(this Type type, params Type[] args) => - type.GetConstructorOrNull(includeNonPublic: true, args: args).ThrowIfNull(Error.UnableToFindConstructorWithArgs, type, args); - - /// Returns single constructor otherwise (if no constructor or more than one) returns null. - public static ConstructorInfo GetSingleConstructorOrNull(this Type type, bool includeNonPublic = false) - { - ConstructorInfo foundCtor = null; - if (includeNonPublic) - { - foreach (var ctor in type.GetTypeInfo().DeclaredConstructors) - { - if (!ctor.IsStatic) - { - if (foundCtor != null) - return null; - foundCtor = ctor; - } - } - } - else - { - foreach (var ctor in type.GetTypeInfo().DeclaredConstructors) - { - if (!ctor.IsStatic && ctor.IsPublic) - { - if (foundCtor != null) - return null; - foundCtor = ctor; - } - } - } - return foundCtor; - } - - /// Returns single constructor otherwise (if no or more than one) throws an exception - public static ConstructorInfo SingleConstructor(this Type type, bool includeNonPublic = false) => - type.GetSingleConstructorOrNull(includeNonPublic).ThrowIfNull(Error.UnableToFindSingleConstructor, type, includeNonPublic); - - /// Looks up for single declared method with the specified name. Returns null if method is not found. - public static MethodInfo GetSingleMethodOrNull(this Type type, string name, bool includeNonPublic = false) - { - MethodInfo foundMethod = null; - if (includeNonPublic) - { - foreach (var method in type.GetTypeInfo().DeclaredMethods) - if (method.Name == name) - { - if (foundMethod != null) - return null; - foundMethod = method; - } - } - else - { - foreach (var method in type.GetTypeInfo().DeclaredMethods) - if (method.IsPublic && method.Name == name) - { - if (foundMethod != null) - return null; - foundMethod = method; - } - } - return foundMethod; - } - - /// Looks for single declared (not inherited) method by name, and throws if not found. - public static MethodInfo SingleMethod(this Type type, string name, bool includeNonPublic = false) => - type.GetSingleMethodOrNull(name, includeNonPublic).ThrowIfNull( - Error.UndefinedMethodWhenGettingTheSingleMethod, name, type, includeNonPublic); - - /// Looks up for method with and specified parameter types. - public static MethodInfo Method(this Type type, string name, params Type[] args) => - type.GetMethodOrNull(name, args).ThrowIfNull( - Error.UndefinedMethodWhenGettingMethodWithSpecifiedParameters, name, type, args); - - /// Looks up for method with and specified parameter types. - public static MethodInfo GetMethodOrNull(this Type type, string name, params Type[] paramTypes) - { - var pTypesCount = paramTypes.Length; - var methods = type.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - foreach (var method in methods) - { - if (method.Name == name) - { - var ps = method.GetParameters(); - if (ps.Length == pTypesCount) - { - var p = 0; - for (; p < pTypesCount; ++p) - { - var paramType = ps[p].ParameterType; - if (paramType != paramTypes[p] && paramType.GetGenericDefinitionOrNull() != paramTypes[p]) - break; - } - - if (p == pTypesCount) - return method; - } - } - } - - return null; - } - - /// Returns property by name, including inherited. Or null if not found. - public static PropertyInfo Property(this Type type, string name, bool includeBase = false) => - type.GetPropertyOrNull(name, includeBase).ThrowIfNull(Error.UndefinedPropertyWhenGettingProperty, name, type); - - /// Returns property by name, including inherited. Or null if not found. - public static PropertyInfo GetPropertyOrNull(this Type type, string name, bool includeBase = false) - { - var props = type.GetTypeInfo().DeclaredProperties.ToArrayOrSelf(); - for (var i = 0; i < props.Length; i++) - { - var p = props[i]; - if (p.Name == name) - return p; - } - - return !includeBase ? null : type.GetTypeInfo().BaseType?.GetPropertyOrNull(name, includeBase); - } - - /// Returns field by name, including inherited. Or null if not found. - public static FieldInfo Field(this Type type, string name, bool includeBase = false) => - type.GetFieldOrNull(name, includeBase).ThrowIfNull(Error.UndefinedFieldWhenGettingField, name, type); - - /// Returns field by name, including inherited. Or null if not found. - public static FieldInfo GetFieldOrNull(this Type type, string name, bool includeBase = false) - { - var fields = type.GetTypeInfo().DeclaredFields.ToArrayOrSelf(); - for (var i = 0; i < fields.Length; i++) - { - var f = fields[i]; - if (f.Name == name) - return f; - } - - return !includeBase ? null : type.GetTypeInfo().BaseType?.GetFieldOrNull(name, includeBase); - } - - /// Returns type assembly. - public static Assembly GetAssembly(this Type type) => type.GetTypeInfo().Assembly; - - /// Is true for interface declared property explicitly implemented, e.g. IInterface.Prop - public static bool IsExplicitlyImplemented(this PropertyInfo property) => property.Name.Contains("."); - - /// Returns true if member is static, otherwise returns false. - public static bool IsStatic(this MemberInfo member) => - member is MethodInfo method ? method.IsStatic - : member is FieldInfo field ? field.IsStatic - : member is PropertyInfo prop && !prop.IsExplicitlyImplemented() && prop.IsStatic(true); - - /// Find if property is static - public static bool IsStatic(this PropertyInfo property, bool includeNonPublic = false) - { - // e.g.: set_Blah or get_Blah - var propName = property.Name; - var methods = property.DeclaringType.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var index = 0; index < methods.Length; index++) - { - var m = methods[index]; - if (m.IsSpecialName && (includeNonPublic || m.IsPublic)) - { - var name = m.Name; - var nameLength = name.Length; - if (nameLength > 4 && name[3] == '_' && nameLength - 4 == propName.Length) - { - var i = 4; - for (var j = 0; i < nameLength; i++, j++) - if (name[i] != propName[j]) - break; - if (i == nameLength) - return m.IsStatic; - } - } - } - - return false; - } - - /// Return either , or , - /// . - public static Type GetReturnTypeOrDefault(this MemberInfo member) => - member is ConstructorInfo ? member.DeclaringType - : (member as MethodInfo) ?.ReturnType - ?? (member as PropertyInfo)?.PropertyType - ?? (member as FieldInfo) ?.FieldType; - - /// Returns true if field is backing field for property. - public static bool IsBackingField(this FieldInfo field) => - field.Name[0] == '<'; - - /// Returns true if property is indexer: aka this[]. - public static bool IsIndexer(this PropertyInfo property) => - property.GetIndexParameters().Length != 0; - - /// Returns true if type is generated type of hoisted closure. - public static bool IsClosureType(this Type type) => - type.Name.Contains("<>c__DisplayClass"); - - /// Returns attributes defined for the member/method. - public static IEnumerable GetAttributes(this MemberInfo member, Type attributeType = null, bool inherit = false) => - member.GetCustomAttributes(attributeType ?? typeof(Attribute), inherit).Cast(); - - /// Returns attributes defined for parameter. - public static IEnumerable GetAttributes(this ParameterInfo parameter, Type attributeType = null, bool inherit = false) => - parameter.GetCustomAttributes(attributeType ?? typeof(Attribute), inherit).Cast(); - - /// Get types from assembly that are loaded successfully. - /// Hacks the to get failing to load types metadata. - public static Type[] GetLoadedTypes(this Assembly assembly) - { - try - { - return Portable.GetAssemblyTypes(assembly).ToArrayOrSelf(); - } - catch (ReflectionTypeLoadException ex) - { - return ex.Types.Where(type => type != null).ToArray(); - } - } - - private static void ClearGenericParametersReferencedInConstraints(Type[] genericParams) - { - for (var i = 0; i < genericParams.Length; i++) - { - var genericParam = genericParams[i]; - if (genericParam == null) - continue; - - var genericConstraints = genericParam.GetGenericParamConstraints(); - for (var j = 0; j < genericConstraints.Length; j++) - { - var genericConstraint = genericConstraints[j]; - if (genericConstraint.IsOpenGeneric()) - { - var constraintGenericParams = genericConstraint.GetGenericParamsAndArgs(); - for (var k = 0; k < constraintGenericParams.Length; k++) - { - var constraintGenericParam = constraintGenericParams[k]; - if (constraintGenericParam != genericParam) - { - for (var g = 0; g < genericParams.Length; ++g) - if (genericParams[g] == constraintGenericParam) - { - genericParams[g] = null; // match - break; - } - } - } - } - } - } - } - - private static void ClearMatchesFoundInGenericParameters(Type[] matchedParams, Type[] genericParams) - { - for (var i = 0; i < genericParams.Length; i++) - { - var genericParam = genericParams[i]; - if (genericParam.IsGenericParameter) - { - for (var j = 0; j < matchedParams.Length; ++j) - if (matchedParams[j] == genericParam) - { - matchedParams[j] = null; // match - break; - } - } - else if (genericParam.IsOpenGeneric()) - ClearMatchesFoundInGenericParameters(matchedParams, genericParam.GetGenericParamsAndArgs()); - } - } - - internal static T GetDefault() => default(T); - internal static readonly MethodInfo GetDefaultMethod = - typeof(ReflectionTools).SingleMethod(nameof(GetDefault), true); - - /// Creates default(T) expression for provided . - public static Expression GetDefaultValueExpression(this Type type) => - !type.IsValueType() ? Constant(null, type) : (Expression)Call(GetDefaultMethod.MakeGenericMethod(type), Empty()); - } - - /// Provides pretty printing/debug view for number of types. - public static class PrintTools - { - /// Default separator used for printing enumerable. - public static string DefaultItemSeparator = ", " + NewLine; - - /// Prints input object by using corresponding Print methods for know types. - /// Builder to append output to. Object to print. - /// (optional) Quote to use for quoting string object. - /// (optional) Separator for enumerable. - /// (optional) Custom type printing policy. - /// String builder with appended output. - public static StringBuilder Print(this StringBuilder s, object x, - string quote = "\"", string itemSeparator = null, Func getTypeName = null) => - x == null ? s.Append("null") - : x is string ? s.Print((string)x, quote) - : x is Type ? s.Print((Type)x, getTypeName) - : x is IPrintable ? ((IPrintable)x).Print(s, (b, p) => b.Print(p, quote, itemSeparator, getTypeName)) - : x is IScope || x is Request ? s.Append(x) // prevent recursion for IEnumerable - : x.GetType().IsEnum() ? s.Print(x.GetType()).Append('.').Append(Enum.GetName(x.GetType(), x)) - : (x is IEnumerable || x is IEnumerable) && - !x.GetType().IsAssignableTo(typeof(IEnumerable<>).MakeGenericType(x.GetType())) // exclude infinite recursion and StackOverflowEx - ? s.Print((IEnumerable)x, itemSeparator ?? DefaultItemSeparator, (_, o) => _.Print(o, quote, null, getTypeName)) - : s.Append(x); - - /// Appends string to string builder quoting with if provided. - /// String builder to append string to. String to print. - /// (optional) Quote to add before and after string. - /// String builder with appended string. - public static StringBuilder Print(this StringBuilder s, string str, string quote = "\"") => - quote == null ? s.Append(str) : s.Append(quote).Append(str).Append(quote); - - /// Prints enumerable by using corresponding Print method for known item type. - /// String builder to append output to. - /// Items to print. - /// (optional) Custom separator if provided. - /// (optional) Custom item printer if provided. - /// String builder with appended output. - public static StringBuilder Print(this StringBuilder s, IEnumerable items, - string separator = ", ", Action printItem = null) - { - if (items == null) - return s; - printItem = printItem ?? ((_, x) => _.Print(x)); - var itemCount = 0; - foreach (var item in items) - printItem(itemCount++ == 0 ? s : s.Append(separator), item); - return s; - } - - /// Default delegate to print Type details: by default prints Type FullName and - /// skips namespace if it start with "System." - public static Func GetTypeNameDefault = t => -#if DEBUG - t.Name; -#else - t.FullName != null && t.Namespace != null && !t.Namespace.StartsWith("System") ? t.FullName : t.Name; -#endif - - /// Pretty prints the in proper C# representation. - /// Allows to specify if you want Name instead of FullName. - public static StringBuilder Print(this StringBuilder s, Type type, Func getTypeName = null) - { - if (type == null) - return s; - - var isArray = type.IsArray; - if (isArray) - type = type.GetElementType(); - - var typeName = (getTypeName ?? GetTypeNameDefault).Invoke(type); - - if (!type.IsGeneric()) - return s.Append(typeName.Replace('+', '.')); - - var tickIndex = typeName.IndexOf('`'); - if (tickIndex != -1) - typeName = typeName.Substring(0, tickIndex); - - s.Append(typeName.Replace('+', '.')); - - s.Append('<'); - var genericArgs = type.GetGenericParamsAndArgs(); - if (type.IsGenericDefinition()) - s.Append(',', genericArgs.Length - 1); - else - s.Print(genericArgs, ", ", (b, t) => b.Print((Type)t, getTypeName)); - s.Append('>'); - - if (isArray) - s.Append("[]"); - - return s; - } - - /// Pretty-prints the type - public static string Print(this Type type, Func getTypeName = null) => - new StringBuilder().Print(type, getTypeName).ToString(); - } - - /// Ports some methods from .Net 4.0/4.5 - public static partial class Portable - { - /// Portable version of Assembly.GetTypes or Assembly.DefinedTypes. - public static IEnumerable GetAssemblyTypes(Assembly a) => _getAssemblyTypes.Value(a); - - private static readonly Lazy>> _getAssemblyTypes = Lazy.Of(GetAssemblyTypesMethod); - private static Func> GetAssemblyTypesMethod() - { - var asmExpr = Parameter(typeof(Assembly), "a"); - - var asmDefinedTypesProperty = typeof(Assembly).GetPropertyOrNull("DefinedTypes"); - - var typesExpr = asmDefinedTypesProperty == null - ? Call(asmExpr, typeof(Assembly).SingleMethod("GetTypes"), Empty()) - : asmDefinedTypesProperty.PropertyType == typeof(IEnumerable) - ? Call(typeof(Portable).SingleMethod(nameof(ToTypes), true), Property(asmExpr, asmDefinedTypesProperty)) - : (Expression)Property(asmExpr, asmDefinedTypesProperty); - - return FastExpressionCompiler.LightExpression.ExpressionCompiler.CompileFast( - Lambda>>(typesExpr, asmExpr)); - } - - internal static IEnumerable ToTypes(IEnumerable x) => x.Select(t => t.AsType()); - - /// Portable version of PropertyInfo.GetGetMethod. - public static MethodInfo GetGetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) - { - var name = "get_" + p.Name; - var methods = p.DeclaringType.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && (includeNonPublic || m.IsPublic) && m.Name == name) - return m; - } - - return null; - } - - /// Portable version of PropertyInfo.GetSetMethod. - public static MethodInfo GetSetMethodOrNull(this PropertyInfo p, bool includeNonPublic = false) - { - var name = "set_" + p.Name; - var methods = p.DeclaringType.GetTypeInfo().DeclaredMethods.ToArrayOrSelf(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && (includeNonPublic || m.IsPublic) && m.Name == name) - return m; - } - - return null; - } - - private static readonly Lazy> _getEnvCurrentManagedThreadId = Lazy.Of(() => - { - var method = typeof(Environment).GetMethodOrNull("get_CurrentManagedThreadId", Empty()); - if (method == null) - return null; - - return Lambda>(Call(method, Empty()), Empty()) -#if SUPPORTS_FAST_EXPRESSION_COMPILER - .ToLambdaExpression() -#endif - .Compile(); - }); - - /// Returns managed Thread ID either from Environment or Thread.CurrentThread whichever is available. - [MethodImpl((MethodImplOptions)256)] - public static int GetCurrentManagedThreadID() - { - var resultID = -1; - GetCurrentManagedThreadID(ref resultID); - if (resultID == -1) - resultID = _getEnvCurrentManagedThreadId.Value(); - return resultID; - } - - static partial void GetCurrentManagedThreadID(ref int threadID); - } -} // end of DryIoc namespace - -#if SUPPORTS_ASYNC_LOCAL -namespace DryIoc -{ - using System.Threading; - - /// Stores scopes propagating through async-await boundaries. - public sealed class AsyncExecutionFlowScopeContext : IScopeContext - { - /// Statically known name of root scope in this context. - public static readonly string ScopeContextName = typeof(AsyncExecutionFlowScopeContext).FullName; - - /// It is fine to use a default instance, cause the async local scope are actually a static one - public static readonly AsyncExecutionFlowScopeContext Default = new AsyncExecutionFlowScopeContext(); - - private static readonly AsyncLocal _ambientScope = new AsyncLocal(); - - /// Returns current scope or null if no ambient scope available at the moment. - /// Current scope or null. - public IScope GetCurrentOrDefault() => _ambientScope.Value; - - /// Changes current scope using provided delegate. Delegate receives current scope as input and should return new current scope. - /// Delegate to change the scope. - /// Important: may be called multiple times in concurrent environment. - /// Make it predictable by removing any side effects. - /// New current scope. It is convenient to use method in "using (var newScope = ctx.SetCurrent(...))". - public IScope SetCurrent(SetCurrentScopeHandler changeCurrentScope) => - _ambientScope.Value = changeCurrentScope(GetCurrentOrDefault()); - - /// Nothing to dispose. - public void Dispose() { } - } -} -#endif -#if !SUPPORTS_ASYNC_LOCAL && SUPPORTS_SERIALIZABLE && !NETSTANDARD2_0 -namespace DryIoc -{ - using System; - using System.Threading; - - /// Stores scopes propagating through async-await boundaries. - public sealed class AsyncExecutionFlowScopeContext : IScopeContext - { - /// Statically known name of root scope in this context. - public static readonly string ScopeContextName = typeof(AsyncExecutionFlowScopeContext).FullName; - - /// It is fine to use a default instance, cause the async local scope are actually a static one - public static readonly AsyncExecutionFlowScopeContext Default = new AsyncExecutionFlowScopeContext(); - - [Serializable] - internal sealed class ScopeEntry : MarshalByRefObject - { - public readonly T Value; - public ScopeEntry(T value) { Value = value; } - } - - private static int _seedKey; - private readonly string _scopeEntryKey = ScopeContextName + Interlocked.Increment(ref _seedKey); - - /// Returns current scope or null if no ambient scope available at the moment. - /// Current scope or null. - public IScope GetCurrentOrDefault() => - ((ScopeEntry)System.Runtime.Remoting.Messaging.CallContext.LogicalGetData(_scopeEntryKey))?.Value; - - /// Changes current scope using provided delegate. Delegate receives current scope as input and should return new current scope. - /// Delegate to change the scope. - /// Important: may be called multiple times in concurrent environment. - /// Make it predictable by removing any side effects. - /// New current scope. It is convenient to use method in "using (var newScope = ctx.SetCurrent(...))". - public IScope SetCurrent(SetCurrentScopeHandler changeCurrentScope) - { - var newScope = changeCurrentScope(GetCurrentOrDefault()); - var scopeEntry = newScope == null ? null : new ScopeEntry(newScope); - System.Runtime.Remoting.Messaging.CallContext.LogicalSetData(_scopeEntryKey, scopeEntry); - return newScope; - } - - /// Nothing to dispose. - public void Dispose() { } - } -} -#endif - -#if !SUPPORTS_STACK_TRACE -namespace DryIoc -{ - internal class StackTrace - { - public override string ToString() => ""; - } -} -#endif -#if PCL328 || NET35 || NET40 || NET403 -namespace DryIoc -{ - using System.Threading; - - /// Something for portability - public static partial class Portable - { - // ReSharper disable once RedundantAssignment - static partial void GetCurrentManagedThreadID(ref int threadID) - { - threadID = Thread.CurrentThread.ManagedThreadId; - } - } -} - -namespace System.Reflection -{ - using Collections.Generic; - using Linq; - - /// Provides for the type. - public static class TypeInfoTools - { - /// Wraps input type into structure. - /// Input type. Type info wrapper. - public static TypeInfo GetTypeInfo(this Type type) => new TypeInfo(type); - } - - /// Partial analog of TypeInfo existing in .NET 4.5 and higher. - public struct TypeInfo - { - private readonly Type _type; - - /// Creates type info by wrapping input type. Type to wrap. - public TypeInfo(Type type) - { - _type = type; - } - -#pragma warning disable 1591 // "Missing XML-comment" - public Type AsType() => _type; - - public Assembly Assembly => _type.Assembly; - - public MethodInfo GetDeclaredMethod(string name) => _type.GetMethod(name); - public PropertyInfo GetDeclaredProperty(string name) => _type.GetProperty(name); - public FieldInfo GetDeclaredField(string name) => _type.GetField(name); - - public IEnumerable DeclaredConstructors => - _type.GetConstructors(ALL_DECLARED ^ BindingFlags.Static); - - public IEnumerable DeclaredMembers => - _type.GetMembers(ALL_DECLARED); - - public IEnumerable DeclaredMethods => - _type.GetMethods(ALL_DECLARED); - - public IEnumerable DeclaredFields => - _type.GetFields(ALL_DECLARED); - - public IEnumerable DeclaredProperties => - _type.GetProperties(ALL_DECLARED); - - public IEnumerable ImplementedInterfaces => - _type.GetInterfaces(); - - public IEnumerable GetCustomAttributes(Type attributeType, bool inherit) => - _type.GetCustomAttributes(attributeType, inherit).Cast(); - - public Type BaseType => _type.BaseType; - public bool IsGenericType => _type.IsGenericType; - public bool IsGenericTypeDefinition => _type.IsGenericTypeDefinition; - public bool ContainsGenericParameters => _type.ContainsGenericParameters; - public Type[] GenericTypeParameters => _type.GetGenericArguments(); - public Type[] GenericTypeArguments => _type.GetGenericArguments(); - - public bool IsClass => _type.IsClass; - public bool IsInterface => _type.IsInterface; - public bool IsValueType => _type.IsValueType; - public bool IsPrimitive => _type.IsPrimitive; - public bool IsArray => _type.IsArray; - public bool IsPublic => _type.IsPublic; - public bool IsNestedPublic => _type.IsNestedPublic; - public Type DeclaringType => _type.DeclaringType; - public bool IsAbstract => _type.IsAbstract; - public bool IsSealed => _type.IsSealed; - public bool IsEnum => _type.IsEnum; - - public Type[] GetGenericParameterConstraints() => _type.GetGenericParameterConstraints(); - public Type GetElementType() => _type.GetElementType(); - - public bool IsAssignableFrom(TypeInfo typeInfo) => _type.IsAssignableFrom(typeInfo.AsType()); -#pragma warning restore 1591 // "Missing XML-comment" - - private const BindingFlags ALL_DECLARED = - BindingFlags.Instance | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic | - BindingFlags.DeclaredOnly; - } -} -#endif - -#if NET35 -namespace System -{ - /// Func with 5 input parameters. - public delegate TResult Func( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5); - - /// Func with 6 input parameters. - public delegate TResult Func( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6); - - /// Func with 7 input parameters. - public delegate TResult Func( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6, - T7 arg7); - - /// Action with 5 input parameters. - public delegate void Action( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5); - - /// Action with 6 input parameters. - public delegate void Action( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6); - - /// Action with 7 input parameters. - public delegate void Action( - T1 arg1, - T2 arg2, - T3 arg3, - T4 arg4, - T5 arg5, - T6 arg6, - T7 arg7); - - - /// Wrapper for value computation required on-demand. Since computed the same value will be returned over and over again. - /// Type of value. - public sealed class Lazy - { - /// Creates lazy object with passed value computation delegate. - /// Value computation. Will be stored until computation is done. - /// Throws for null computation. - public Lazy(Func valueFactory) - { - if (valueFactory == null) throw new ArgumentNullException("valueFactory"); - _valueFactory = valueFactory; - } - - /// Indicates if value is computed already, or not. - public bool IsValueCreated { get; private set; } - - /// Computes value if it was not before, and returns it. - /// Value is guaranteed to be computed only once despite possible thread contention. - /// Throws if value computation is recursive. - public T Value => IsValueCreated ? _value : Create(); - -#region Implementation - - private Func _valueFactory; - private T _value; - private readonly object _valueCreationLock = new object(); - - private T Create() - { - lock (_valueCreationLock) - { - if (!IsValueCreated) - { - if (_valueFactory == null) throw new InvalidOperationException("The initialization function tries to access Value on this instance."); - var factory = _valueFactory; - _valueFactory = null; - _value = factory(); - IsValueCreated = true; - } - } - - return _value; - } - -#endregion - } - - /// Contains utility methods for creating and working with tuple. - public static class Tuple - { - /// Creates a new 2-tuple, or pair. - /// A 2-tuple whose value is (, ). - /// The value of the first component of the tuple.The value of the second component of the tuple.The type of the first component of the tuple.The type of the second component of the tuple. - public static Tuple Create(T1 item1, T2 item2) => new Tuple(item1, item2); - } - - /// Represents a 2-tuple, or pair. - /// The type of the tuple's first component.The type of the tuple's second component.2 - public sealed class Tuple - { - private readonly T1 _item1; - private readonly T2 _item2; - - /// Gets the value of the first component. - public T1 Item1 => _item1; - - /// Gets the value of the current second component. - public T2 Item2 => _item2; - - /// Initializes a new instance of the class. - /// The value of the tuple's first component.The value of the tuple's second component. - public Tuple(T1 item1, T2 item2) - { - _item1 = item1; - _item2 = item2; - } - - /// Returns a value that indicates whether the current object is equal to a specified object. - /// The object to compare with this instance. - /// true if the current instance is equal to the specified object; otherwise, false. - public override bool Equals(object obj) - { - var other = obj as Tuple; - return other != null && Equals(other.Item1, Item1) && Equals(other.Item2, Item2); - } - - /// Returns the hash code for the current object. - /// A 32-bit signed integer hash code. - public override int GetHashCode() - { - var h1 = _item1 == null ? 0 : _item1.GetHashCode(); - var h2 = _item2 == null ? 0 : _item2.GetHashCode(); - return (h1 << 5) + h1 ^ h2; - } - - /// Returns a string that represents the value of this instance. - public override string ToString() => "(" + _item1 + ", " + _item2 + ")"; - } -} -#endif -#if SUPPORTS_VARIANCE -namespace DryIoc.Messages -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - - /// Base type for messages - public interface IMessage { } - - /// Type for an empty response - public struct EmptyResponse - { - /// Single value of empty response - public static readonly EmptyResponse Value = new EmptyResponse(); - - /// Single completed task for the empty response - public static readonly Task Task = System.Threading.Tasks.Task.FromResult(Value); - } - - /// Message extensions - public static class MessageExtensions - { - /// Converts the task to empty response task - public static async Task ToEmptyResponse(this Task task) - { - await task; - return EmptyResponse.Value; - } - } - - /// Message with empty response - public interface IMessage : IMessage { } - - /// Base message handler - public interface IMessageHandler where M : IMessage - { - /// Generic handler - Task Handle(M message, CancellationToken cancellationToken); - } - - /// Base message handler for message with empty response - public interface IMessageHandler : IMessageHandler where M : IMessage { } - - /// Message handler middleware to handle the message and pass the result to the next middleware - public interface IMessageMiddleware - { - /// `0` means the default registration order, - /// lesser numbers incuding the `-1`, `-2` mean execute as a first, - /// bigger numbers mean execute as a last - int RelativeOrder { get; } - - /// Handles message and passes to the next middleware - Task Handle(M message, CancellationToken cancellationToken, Func> nextMiddleware); - } - - /// Base class for implementing async handlers - public abstract class AsyncMessageHandler : IMessageHandler - where M : IMessage - { - /// Base method to implement in your inheritor - protected abstract Task Handle(M message, CancellationToken cancellationToken); - - async Task IMessageHandler.Handle(M message, CancellationToken cancellationToken) => - await Handle(message, cancellationToken).ConfigureAwait(false); - } - - /// Sequential middleware type of message handler decorator - public class MiddlewareMessageHandler : IMessageHandler where M : IMessage - { - private readonly IMessageHandler _handler; - private readonly IEnumerable> _middlewares; - - /// Decorates message handler with optional middlewares - public MiddlewareMessageHandler(IMessageHandler handler, IEnumerable> middlewares) - { - _handler = handler; - _middlewares = middlewares; - } - - /// Composes middlewares with handler - public Task Handle(M message, CancellationToken cancellationToken) - { - return _middlewares - .OrderBy(x => x.RelativeOrder) - .Reverse() - .Aggregate( - new Func>(() => _handler.Handle(message, cancellationToken)), - (f, middleware) => () => middleware.Handle(message, cancellationToken, f)) - .Invoke(); - } - } - - /// Broadcasting type of message handler decorator - public class BroadcastMessageHandler: IMessageHandler - where M : IMessage - { - private readonly IEnumerable> _handlers; - - /// Constructs the hub with the handler and optional middlewares - public BroadcastMessageHandler(IEnumerable> handlers) => - _handlers = handlers; - - /// Composes middlewares with handler - public async Task Handle(M message, CancellationToken cancellationToken) - { - await Task.WhenAll(_handlers.Select(h => h.Handle(message, cancellationToken))); - return EmptyResponse.Value; - } - } - - /// A subject - public class MessageMediator - { - private readonly IResolver _resolver; - - /// Constructs with resolver - public MessageMediator(IResolver resolver) => - _resolver = resolver; - - /// Sends message with response to resolved handlers - public Task Send(M message, CancellationToken cancellationToken) where M : IMessage => - _resolver.Resolve>().Handle(message, cancellationToken); - - /// Sends message with empty response to resolved handlers - public Task Send(M message, CancellationToken cancellationToken) where M : IMessage => - _resolver.Resolve>().Handle(message, cancellationToken); - } -} -#endif diff --git a/BattleFieldSimulator/ConsoleClient/DryIoc/Expression.cs b/BattleFieldSimulator/ConsoleClient/DryIoc/Expression.cs deleted file mode 100644 index 960f07b..0000000 --- a/BattleFieldSimulator/ConsoleClient/DryIoc/Expression.cs +++ /dev/null @@ -1,2789 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2016-2020 Maksim Volkau - -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 -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. -*/ - -// ReSharper disable CoVariantArrayConversion -/* -// Lists the target platforms that are Not supported by FEC - simplifies the direct referencing of Expression.cs file -// Don't forget to uncomment #endif at the end of file -*/ -#if !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_FAST_EXPRESSION_COMPILER -#endif - -#if SUPPORTS_FAST_EXPRESSION_COMPILER - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using static System.Environment; -using SysExpr = System.Linq.Expressions.Expression; - -namespace FastExpressionCompiler.LightExpression -{ -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - - /// Facade for constructing Expression. - public abstract class Expression - { - /// Expression node type. - public abstract ExpressionType NodeType { get; } - - /// All expressions should have a Type. - public abstract Type Type { get; } - - internal struct LightAndSysExpr - { - public Expression LightExpr; - public SysExpr SysExpr; - } - - public SysExpr ToExpression() - { - var exprsConverted = new LiveCountArray(Tools.Empty()); - return ToExpression(ref exprsConverted); - } - - /// Converts back to respective System Expression, so you may Compile it by usual means. - internal SysExpr ToExpression(ref LiveCountArray exprsConverted) - { - var i = exprsConverted.Count - 1; - while (i != -1 && !ReferenceEquals(exprsConverted.Items[i].LightExpr, this)) --i; - if (i != -1) - return exprsConverted.Items[i].SysExpr; - - var sysExpr = CreateSysExpression(ref exprsConverted); - - ref var item = ref exprsConverted.PushSlot(); - item.LightExpr = this; - item.SysExpr = sysExpr; - - return sysExpr; - } - - internal abstract SysExpr CreateSysExpression(ref LiveCountArray exprsConverted); - - /// Tries to print the expression in its constructing syntax - helpful to get it from debug and put into code to test, - /// e.g. . - /// - /// NOTE: It is trying hard but the Parameter expression are not consolidated into one. Hopefully R# will help you to re-factor them into a single variable. - public abstract string CodeString { get; } - - /// Converts to Expression and outputs its as string - public override string ToString() => ToExpression().ToString(); - - /// Reduces the Expression to simple ones - public virtual Expression Reduce() => this; - - internal static SysExpr[] ToExpressions(IReadOnlyList exprs, ref LiveCountArray exprsConverted) - { - if (exprs.Count == 0) - return Tools.Empty(); - - if (exprs.Count == 1) - return new[] { exprs[0].ToExpression(ref exprsConverted) }; - - var result = new SysExpr[exprs.Count]; - for (var i = 0; i < result.Length; ++i) - result[i] = exprs[i].ToExpression(ref exprsConverted); - return result; - } - - internal static string ToParamsCode(IReadOnlyList arguments) where T : Expression - { - if (arguments.Count == 0) - return $"new {typeof(T).Name}[0]"; - - var s = new StringBuilder(); - for (var i = 0; i < arguments.Count; i++) - { - if (i > 0) - s.Append(","); - s.Append(arguments[i].CodeString); - } - - return s.ToString(); - } - - public static ParameterExpression Parameter(Type type, string name = null) => - new ParameterExpression(type.IsByRef ? type.GetElementType() : type, name, type.IsByRef); - - public static ParameterExpression Variable(Type type, string name = null) => Parameter(type, name); - - public static readonly ConstantExpression NullConstant = new TypedConstantExpression(null, typeof(object)); - public static readonly ConstantExpression FalseConstant = new ConstantExpression(false); - public static readonly ConstantExpression TrueConstant = new ConstantExpression(true); - public static readonly ConstantExpression ZeroConstant = new ConstantExpression(0); - - public static ConstantExpression Constant(bool value) => - value ? TrueConstant : FalseConstant; - - public static ConstantExpression Constant(int value) => - value == 0 ? ZeroConstant : new TypedConstantExpression(value); - - public static ConstantExpression Constant(T value) => - new TypedConstantExpression(value); - - public static ConstantExpression Constant(object value) - { - if (value == null) - return NullConstant; - - if (value is bool b) - return b ? TrueConstant : FalseConstant; - - if (value is int n) - return n == 0 ? ZeroConstant : new TypedConstantExpression(n); - - return new ConstantExpression(value); - } - - public static ConstantExpression Constant(object value, Type type) => - new TypedConstantExpression(value, type); - - public static NewExpression New(Type type) - { - if (type.IsValueType()) - return new NewValueTypeExpression(type); - - foreach (var x in type.GetTypeInfo().DeclaredConstructors) - if (x.GetParameters().Length == 0) - return new NewExpression(x); - - throw new ArgumentException($"The type {type} is missing the default constructor"); - } - - public static NewExpression New(ConstructorInfo ctor, params Expression[] arguments) => - arguments == null || arguments.Length == 0 ? new NewExpression(ctor): new ManyArgumentsNewExpression(ctor, arguments); - - public static NewExpression New(ConstructorInfo ctor, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - return args == null || args.Count == 0 ? new NewExpression(ctor) : new ManyArgumentsNewExpression(ctor, args); - } - - public static NewExpression New(ConstructorInfo ctor) => new NewExpression(ctor); - - public static NewExpression New(ConstructorInfo ctor, Expression arg) => - new OneArgumentNewExpression(ctor, arg); - - public static NewExpression New(ConstructorInfo ctor, Expression arg0, Expression arg1) => - new TwoArgumentsNewExpression(ctor, arg0, arg1); - - public static NewExpression New(ConstructorInfo ctor, Expression arg0, Expression arg1, Expression arg2) => - new ThreeArgumentsNewExpression(ctor, arg0, arg1, arg2); - - public static NewExpression New(ConstructorInfo ctor, - Expression arg0, Expression arg1, Expression arg2, Expression arg3) => - new FourArgumentsNewExpression(ctor, arg0, arg1, arg2, arg3); - - public static NewExpression New(ConstructorInfo ctor, - Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) => - new FiveArgumentsNewExpression(ctor, arg0, arg1, arg2, arg3, arg4); - - public static MethodCallExpression Call(MethodInfo method, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new MethodCallExpression(method); - return new ManyArgumentsMethodCallExpression(method, arguments); - } - - public static MethodCallExpression Call(MethodInfo method, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new MethodCallExpression(method); - return new ManyArgumentsMethodCallExpression(method, args); - } - - public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new InstanceMethodCallExpression(instance, method); - return new InstanceManyArgumentsMethodCallExpression(instance, method, arguments); - } - - public static MethodCallExpression Call(Expression instance, MethodInfo method, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new InstanceMethodCallExpression(instance, method); - return new InstanceManyArgumentsMethodCallExpression(instance, method, args); - } - - public static MethodCallExpression Call(Type type, string methodName, Type[] typeArguments, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new MethodCallExpression(type.FindMethod(methodName, typeArguments, arguments, isStatic: true)); - return new ManyArgumentsMethodCallExpression(type.FindMethod(methodName, typeArguments, arguments, isStatic: true), arguments); - } - - public static MethodCallExpression Call(Type type, string methodName, Type[] typeArguments, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new MethodCallExpression(type.FindMethod(methodName, typeArguments, args, isStatic: true)); - return new ManyArgumentsMethodCallExpression(type.FindMethod(methodName, typeArguments, args, isStatic: true), args); - } - - public static MethodCallExpression Call(Expression instance, string methodName, Type[] typeArguments, params Expression[] arguments) - { - if (arguments == null || arguments.Length == 0) - return new InstanceMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, arguments)); - return new InstanceManyArgumentsMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, arguments), arguments); - } - - public static MethodCallExpression Call(Expression instance, string methodName, Type[] typeArguments, IEnumerable arguments) - { - var args = arguments.AsReadOnlyList(); - if (args == null || args.Count == 0) - return new InstanceMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, args)); - return new InstanceManyArgumentsMethodCallExpression(instance, instance.Type.FindMethod(methodName, typeArguments, args), args); - } - - public static MethodCallExpression Call(MethodInfo method) => - new MethodCallExpression(method); - - public static MethodCallExpression Call(Expression instance, MethodInfo method) => - instance == null - ? new MethodCallExpression(method) - : new InstanceMethodCallExpression(instance, method); - - public static MethodCallExpression Call(MethodInfo method, Expression argument) => - new OneArgumentMethodCallExpression(method, argument); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression argument) => - instance == null - ? new OneArgumentMethodCallExpression(method, argument) - : new InstanceOneArgumentMethodCallExpression(instance, method, argument); - - public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1) => - new TwoArgumentsMethodCallExpression(method, arg0, arg1); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1) => - instance == null - ? new TwoArgumentsMethodCallExpression(method, arg0, arg1) - : new InstanceTwoArgumentsMethodCallExpression(instance, method, arg0, arg1); - - public static MethodCallExpression Call(MethodInfo method, Expression arg0, Expression arg1, Expression arg2) => - new ThreeArgumentsMethodCallExpression(method, arg0, arg1, arg2); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1, Expression arg2) => - instance == null - ? new ThreeArgumentsMethodCallExpression(method, arg0, arg1, arg2) - : new InstanceThreeArgumentsMethodCallExpression(instance, method, arg0, arg1, arg2); - - public static MethodCallExpression Call(MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3) => - new FourArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3) => - instance == null - ? new FourArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3) - : new InstanceFourArgumentsMethodCallExpression(instance, method, arg0, arg1, arg2, arg3); - - public static MethodCallExpression Call(MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) => - new FiveArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3, arg4); - - public static MethodCallExpression Call(Expression instance, MethodInfo method, - Expression arg0, Expression arg1, Expression arg2, Expression arg3, Expression arg4) => - instance == null - ? new FiveArgumentsMethodCallExpression(method, arg0, arg1, arg2, arg3, arg4) - : new InstanceFiveArgumentsMethodCallExpression(instance, method, arg0, arg1, arg2, arg3, arg4); - - public static Expression CallIfNotNull(Expression instance, MethodInfo method) => - CallIfNotNull(instance, method, Tools.Empty()); - - public static Expression CallIfNotNull(Expression instance, MethodInfo method, IEnumerable arguments) - { - var instanceVar = Parameter(instance.Type, "f"); - return Block(instanceVar, - Assign(instanceVar, instance), - Condition(Equal(instanceVar, Constant(null)), - Constant(null), Call(instanceVar, method, arguments))); - } - - public static MemberExpression Property(PropertyInfo property) => - new PropertyExpression(null, property); - - public static MemberExpression Property(Expression instance, PropertyInfo property) => - new PropertyExpression(instance, property); - - public static MemberExpression Property(Expression expression, string propertyName) => - Property(expression, expression.Type.FindProperty(propertyName) - ?? throw new ArgumentException($"Declared property with the name '{propertyName}' is not found in '{expression.Type}'", nameof(propertyName))); - - public static IndexExpression Property(Expression instance, PropertyInfo indexer, params Expression[] arguments) => - new IndexExpression(instance, indexer, arguments); - - public static IndexExpression Property(Expression instance, PropertyInfo indexer, IEnumerable arguments) => - new IndexExpression(instance, indexer, arguments.AsReadOnlyList()); - - public static MemberExpression PropertyOrField(Expression expression, string propertyName) => - expression.Type.FindProperty(propertyName) != null ? - (MemberExpression)new PropertyExpression(expression, expression.Type.FindProperty(propertyName) - ?? throw new ArgumentException($"Declared property with the name '{propertyName}' is not found in '{expression.Type}'", nameof(propertyName))) : - new FieldExpression(expression, expression.Type.FindField(propertyName) - ?? throw new ArgumentException($"Declared field with the name '{propertyName}' is not found '{expression.Type}'", nameof(propertyName))); - - public static MemberExpression MakeMemberAccess(Expression expression, MemberInfo member) - { - if (member is FieldInfo field) - return Field(expression, field); - if (member is PropertyInfo property) - return Property(expression, property); - throw new ArgumentException($"Member is not field or property: {member}", nameof(member)); - } - - public static IndexExpression MakeIndex(Expression instance, PropertyInfo indexer, IEnumerable arguments) => - indexer != null ? Property(instance, indexer, arguments) : ArrayAccess(instance, arguments); - - public static IndexExpression ArrayAccess(Expression array, params Expression[] indexes) => - new IndexExpression(array, null, indexes); - - public static IndexExpression ArrayAccess(Expression array, IEnumerable indexes) => - new IndexExpression(array, null, indexes.AsReadOnlyList()); - - public static MemberExpression Field(FieldInfo field) => - new FieldExpression(null, field); - - public static MemberExpression Field(Expression instance, FieldInfo field) => - new FieldExpression(instance, field); - - public static MemberExpression Field(Expression instance, string fieldName) => - new FieldExpression(instance, instance.Type.FindField(fieldName)); - - /// Creates a UnaryExpression that represents a bitwise complement operation. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Not and the Operand property set to the specified value. - public static UnaryExpression Not(Expression expression) => - new UnaryExpression(ExpressionType.Not, expression); - - /// Creates a UnaryExpression that represents an explicit reference or boxing conversion where null is supplied if the conversion fails. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to TypeAs and the Operand and Type properties set to the specified values. - public static UnaryExpression TypeAs(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.TypeAs, expression, type); - - public static TypeBinaryExpression TypeEqual(Expression operand, Type type) => - new TypeBinaryExpression(ExpressionType.TypeEqual, operand, type); - - public static TypeBinaryExpression TypeIs(Expression operand, Type type) => - new TypeBinaryExpression(ExpressionType.TypeIs, operand, type); - - /// Creates a UnaryExpression that represents an expression for obtaining the length of a one-dimensional array. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to ArrayLength and the Operand property equal to array. - public static UnaryExpression ArrayLength(Expression array) => - new TypedUnaryExpression(ExpressionType.ArrayLength, array); - - /// Creates a UnaryExpression that represents a type conversion operation. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to Convert and the Operand and Type properties set to the specified values. - public static UnaryExpression Convert(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.Convert, expression, type); - - /// Creates a UnaryExpression that represents a type conversion operation. - /// A Type to set the Type property equal to. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Convert and the Operand and Type properties set to the specified values. - public static UnaryExpression Convert(Expression expression) => - new TypedUnaryExpression(ExpressionType.Convert, expression); - - /// Creates a UnaryExpression that represents a conversion operation for which the implementing method is specified. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A MethodInfo to set the Method property equal to. - /// A UnaryExpression that has the NodeType property equal to Convert and the Operand, Type, and Method properties set to the specified values. - public static UnaryExpression Convert(Expression expression, Type type, MethodInfo method) => - new ConvertWithMethodUnaryExpression(ExpressionType.Convert, expression, type, method); - - /// Creates a UnaryExpression that represents a conversion operation that throws an exception if the target type is overflowed. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to ConvertChecked and the Operand and Type properties set to the specified values. - public static UnaryExpression ConvertChecked(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.ConvertChecked, expression, type); - - /// Creates a UnaryExpression that represents a conversion operation that throws an exception if the target type is overflowed and for which the implementing method is specified. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A MethodInfo to set the Method property equal to. - /// A UnaryExpression that has the NodeType property equal to ConvertChecked and the Operand, Type, and Method properties set to the specified values. - public static UnaryExpression ConvertChecked(Expression expression, Type type, MethodInfo method) => - new ConvertWithMethodUnaryExpression(ExpressionType.ConvertChecked, expression, type, method); - - /// Creates a UnaryExpression that represents the decrementing of the expression by 1. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the decremented expression. - public static UnaryExpression Decrement(Expression expression) => - new UnaryExpression(ExpressionType.Decrement, expression); - - /// Creates a UnaryExpression that represents the incrementing of the expression value by 1. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the incremented expression. - public static UnaryExpression Increment(Expression expression) => - new UnaryExpression(ExpressionType.Increment, expression); - - /// Returns whether the expression evaluates to false. - /// An Expression to set the Operand property equal to. - /// An instance of UnaryExpression. - public static UnaryExpression IsFalse(Expression expression) => - new TypedUnaryExpression(ExpressionType.IsFalse, expression); - - /// Returns whether the expression evaluates to true. - /// An Expression to set the Operand property equal to. - /// An instance of UnaryExpression. - public static UnaryExpression IsTrue(Expression expression) => - new TypedUnaryExpression(ExpressionType.IsTrue, expression); - - /// Creates a UnaryExpression, given an operand, by calling the appropriate factory method. - /// The ExpressionType that specifies the type of unary operation. - /// An Expression that represents the operand. - /// The Type that specifies the type to be converted to (pass null if not applicable). - /// The UnaryExpression that results from calling the appropriate factory method. - public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type) => - type == null - ? new UnaryExpression(unaryType, operand) - : new TypedUnaryExpression(unaryType, operand, type); - - /// Creates a UnaryExpression that represents an arithmetic negation operation. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Negate and the Operand property set to the specified value. - public static UnaryExpression Negate(Expression expression) => - new UnaryExpression(ExpressionType.Negate, expression); - - /// Creates a UnaryExpression that represents an arithmetic negation operation that has overflow checking. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to NegateChecked and the Operand property set to the specified value. - public static UnaryExpression NegateChecked(Expression expression) => - new UnaryExpression(ExpressionType.NegateChecked, expression); - - /// Returns the expression representing the ones complement. - /// An Expression to set the Operand property equal to. - /// An instance of UnaryExpression. - public static UnaryExpression OnesComplement(Expression expression) => - new UnaryExpression(ExpressionType.OnesComplement, expression); - - /// Creates a UnaryExpression that increments the expression by 1 and assigns the result back to the expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PreIncrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PreIncrementAssign, expression); - - /// Creates a UnaryExpression that represents the assignment of the expression followed by a subsequent increment by 1 of the original expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PostIncrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PostIncrementAssign, expression); - - /// Creates a UnaryExpression that decrements the expression by 1 and assigns the result back to the expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PreDecrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PreDecrementAssign, expression); - - /// Creates a UnaryExpression that represents the assignment of the expression followed by a subsequent decrement by 1 of the original expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the resultant expression. - public static UnaryExpression PostDecrementAssign(Expression expression) => - new UnaryExpression(ExpressionType.PostDecrementAssign, expression); - - /// Creates a UnaryExpression that represents an expression that has a constant value of type Expression. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to Quote and the Operand property set to the specified value. - public static UnaryExpression Quote(Expression expression) => - new UnaryExpression(ExpressionType.Quote, expression); - - /// Creates a UnaryExpression that represents a unary plus operation. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that has the NodeType property equal to UnaryPlus and the Operand property set to the specified value. - public static UnaryExpression UnaryPlus(Expression expression) => - new UnaryExpression(ExpressionType.UnaryPlus, expression); - - /// Creates a UnaryExpression that represents an explicit unboxing. - /// An Expression to set the Operand property equal to. - /// A Type to set the Type property equal to. - /// A UnaryExpression that has the NodeType property equal to unbox and the Operand and Type properties set to the specified values. - public static UnaryExpression Unbox(Expression expression, Type type) => - new TypedUnaryExpression(ExpressionType.Unbox, expression, type); - - public static LambdaExpression Lambda(Expression body) => - new LambdaExpression(Tools.GetFuncOrActionType(Tools.Empty(), body.Type), - body, body.Type); - - public static LambdaExpression Lambda(Type delegateType, Expression body) => - new LambdaExpression(delegateType, body, body.Type); - - public static LambdaExpression Lambda(Type delegateType, Expression body, Type returnType) => - new LambdaExpression(delegateType, body, returnType); - - public static LambdaExpression Lambda(Expression body, params ParameterExpression[] parameters) => - Lambda(Tools.GetFuncOrActionType(Tools.GetParamTypes(parameters), body.Type), body, parameters, body.Type); - - public static LambdaExpression Lambda(Type delegateType, Expression body, params ParameterExpression[] parameters) => - Lambda(delegateType, body, parameters, GetDelegateReturnType(delegateType)); - - public static LambdaExpression Lambda(Type delegateType, Expression body, ParameterExpression[] parameters, Type returnType) => - parameters == null || parameters.Length == 0 - ? new LambdaExpression(delegateType, body, returnType) - : new ManyParametersLambdaExpression(delegateType, body, parameters, returnType); - - public static Expression Lambda(Expression body) => - new Expression(body, typeof(TDelegate).FindDelegateInvokeMethod().ReturnType); - - public static Expression Lambda(Expression body, Type returnType) => - new Expression(body, returnType); - - public static Expression Lambda(Expression body, params ParameterExpression[] parameters) => - Lambda(body, parameters, GetDelegateReturnType(typeof(TDelegate))); - - public static Expression Lambda(Expression body, ParameterExpression[] parameters, Type returnType) => - parameters == null || parameters.Length == 0 - ? new Expression(body, returnType) - : new ManyParametersExpression(body, parameters, returnType); - - /// - /// is ignored for now, the method is just for compatibility with SysExpression - /// - public static Expression Lambda(Expression body, string name, params ParameterExpression[] parameters) where TDelegate : class => - new ManyParametersExpression(body, parameters, GetDelegateReturnType(typeof(TDelegate))); - - private static Type GetDelegateReturnType(Type delType) - { - var typeInfo = delType.GetTypeInfo(); - if (typeInfo.IsGenericType) - { - var typeArguments = typeInfo.GenericTypeArguments; - var index = typeArguments.Length - 1; - var typeDef = typeInfo.GetGenericTypeDefinition(); - if (typeDef == FuncTypes[index]) - return typeArguments[index]; - - if (typeDef == ActionTypes[index]) - return typeof(void); - } - else if (delType == typeof(Action)) - return typeof(void); - - return delType.FindDelegateInvokeMethod().ReturnType; - } - - internal static readonly Type[] FuncTypes = - { - typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>), - typeof(Func<,,,,,>), typeof(Func<,,,,,,>), typeof(Func<,,,,,,,>) - }; - - internal static readonly Type[] ActionTypes = - { - typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>), - typeof(Action<,,,,>), typeof(Action<,,,,,>), typeof(Action<,,,,,,>) - }; - - /// Creates a BinaryExpression that represents applying an array index operator to an array of rank one. - /// A Expression to set the Left property equal to. - /// A Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ArrayIndex and the Left and Right properties set to the specified values. - public static BinaryExpression ArrayIndex(Expression array, Expression index) => - new ArrayIndexExpression(array, index, array.Type.GetElementType()); - - public static MemberAssignment Bind(MemberInfo member, Expression expression) => - new MemberAssignment(member, expression); - - public static MemberInitExpression MemberInit(NewExpression newExpr, params MemberBinding[] bindings) => - new MemberInitExpression(newExpr, bindings); - - /// Does not present in System Expression. Enables member assignment on existing instance expression. - public static MemberInitExpression MemberInit(Expression instanceExpr, params MemberBinding[] assignments) => - new MemberInitExpression(instanceExpr, assignments); - - public static NewArrayExpression NewArrayInit(Type type, params Expression[] initializers) => - new NewArrayExpression(ExpressionType.NewArrayInit, type.MakeArrayType(), initializers); - - public static NewArrayExpression NewArrayBounds(Type type, params Expression[] bounds) => - new NewArrayExpression(ExpressionType.NewArrayBounds, - bounds.Length == 1 ? type.MakeArrayType() : type.MakeArrayType(bounds.Length), - bounds); - - /// Creates a BinaryExpression that represents an assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Assign and the Left and Right properties set to the specified values. - public static BinaryExpression Assign(Expression left, Expression right) => - new AssignBinaryExpression(left, right, left.Type); - - /// Creates a BinaryExpression that represents raising an expression to a power and assigning the result back to the expression. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to PowerAssign and the Left and Right properties set to the specified values. - public static BinaryExpression PowerAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.PowerAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents an addition assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AddAssign and the Left and Right properties set to the specified values. - public static BinaryExpression AddAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.AddAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents an addition assignment operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AddAssignChecked and the Left and Right properties set to the specified values. - public static BinaryExpression AddAssignChecked(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.AddAssignChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise AND assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AndAssign and the Left and Right properties set to the specified values. - public static BinaryExpression AndAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.AndAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise XOR assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ExclusiveOrAssign and the Left and Right properties set to the specified values. - public static BinaryExpression ExclusiveOrAssign(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.ExclusiveOrAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise left-shift assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LeftShiftAssign and the Left and Right properties set to the specified values. - public static BinaryExpression LeftShiftAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.LeftShiftAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a remainder assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ModuloAssign and the Left and Right properties set to the specified values. - public static BinaryExpression ModuloAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.ModuloAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise OR assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to OrAssign and the Left and Right properties set to the specified values. - public static BinaryExpression OrAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.OrAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise right-shift assignment operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to RightShiftAssign and the Left and Right properties set to the specified values. - public static BinaryExpression RightShiftAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.RightShiftAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a subtraction assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to SubtractAssign and the Left and Right properties set to the specified values. - public static BinaryExpression SubtractAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.SubtractAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a subtraction assignment operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to SubtractAssignChecked and the Left and Right properties set to the specified values. - public static BinaryExpression SubtractAssignChecked(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.SubtractAssignChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a multiplication assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to MultiplyAssign and the Left and Right properties set to the specified values. - public static BinaryExpression MultiplyAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.MultiplyAssign, left, right, left.Type); - - /// Creates a BinaryExpression that represents a multiplication assignment operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to MultiplyAssignChecked and the Left and Right properties set to the specified values. - public static BinaryExpression MultiplyAssignChecked(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.MultiplyAssignChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a division assignment operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to DivideAssign and the Left and Right properties set to the specified values. - public static BinaryExpression DivideAssign(Expression left, Expression right) => - new AssignBinaryExpression(ExpressionType.DivideAssign, left, right, left.Type); - - public static InvocationExpression Invoke(Expression lambda, IEnumerable args) => - new InvocationExpression(lambda, args.AsReadOnlyList(), - (lambda as LambdaExpression)?.ReturnType ?? lambda.Type.FindDelegateInvokeMethod().ReturnType); - - public static InvocationExpression Invoke(Expression lambda, params Expression[] args) => - Invoke(lambda, (IEnumerable)args); - - public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse) => - new ConditionalExpression(test, ifTrue, ifFalse); - - public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, Type type) => - new ConditionalExpression(test, ifTrue, ifFalse, type); - - public static ConditionalExpression IfThen(Expression test, Expression ifTrue) => - Condition(test, ifTrue, Empty(), typeof(void)); - - public static DefaultExpression Empty() => new DefaultExpression(typeof(void)); - - public static DefaultExpression Default(Type type) => - type == typeof(void) ? Empty() : new DefaultExpression(type); - - public static ConditionalExpression IfThenElse(Expression test, Expression ifTrue, Expression ifFalse) => - Condition(test, ifTrue, ifFalse, typeof(void)); - - /// Creates a BinaryExpression that represents an arithmetic addition operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Add and the Left and Right properties set to the specified values. - public static BinaryExpression Add(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Add, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic addition operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AddChecked and the Left and Right properties set to the specified values. - public static BinaryExpression AddChecked(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.AddChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise XOR operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to ExclusiveOr and the Left and Right properties set to the specified values. - public static BinaryExpression ExclusiveOr(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.ExclusiveOr, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise left-shift operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LeftShift and the Left and Right properties set to the specified values. - public static BinaryExpression LeftShift(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.LeftShift, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic remainder operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Modulo and the Left and Right properties set to the specified values. - public static BinaryExpression Modulo(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Modulo, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise OR operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Or and the Left and Right properties set to the specified values. - public static BinaryExpression Or(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Or, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise right-shift operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to RightShift and the Left and Right properties set to the specified values. - public static BinaryExpression RightShift(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.RightShift, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic subtraction operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Subtract and the Left and Right properties set to the specified values. - public static BinaryExpression Subtract(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Subtract, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic subtraction operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to SubtractChecked and the Left, Right, and Method properties set to the specified values. - public static BinaryExpression SubtractChecked(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.SubtractChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic multiplication operation that does not have overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Multiply and the Left and Right properties set to the specified values. - public static BinaryExpression Multiply(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Multiply, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic multiplication operation that has overflow checking. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to MultiplyChecked and the Left and Right properties set to the specified values. - public static BinaryExpression MultiplyChecked(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.MultiplyChecked, left, right, left.Type); - - /// Creates a BinaryExpression that represents an arithmetic division operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Divide and the Left and Right properties set to the specified values. - public static BinaryExpression Divide(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Divide, left, right, left.Type); - - /// Creates a BinaryExpression that represents raising a number to a power. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Power and the Left and Right properties set to the specified values. - public static BinaryExpression Power(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Power, left, right, left.Type); - - /// Creates a BinaryExpression that represents a bitwise AND operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to And, and the Left and Right properties are set to the specified values. - public static BinaryExpression And(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.And, left, right, left.Type); - - /// Creates a BinaryExpression that represents a conditional AND operation that evaluates the second operand only if the first operand evaluates to true. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to AndAlso and the Left and Right properties set to the specified values. - public static BinaryExpression AndAlso(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.AndAlso, left, right, left.Type); - - /// Creates a BinaryExpression that represents a conditional OR operation that evaluates the second operand only if the first operand evaluates to false. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to OrElse and the Left and Right properties set to the specified values. - public static BinaryExpression OrElse(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.OrElse, left, right, left.Type); - - /// Creates a BinaryExpression that represents an equality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Equal and the Left and Right properties set to the specified values. - public static BinaryExpression Equal(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Equal, left, right, typeof(bool)); - - /// Creates a BinaryExpression that represents a "greater than" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to GreaterThan and the Left and Right properties set to the specified values. - public static BinaryExpression GreaterThan(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.GreaterThan, left, right, left.Type); - - /// Creates a BinaryExpression that represents a "greater than or equal" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to GreaterThanOrEqual and the Left and Right properties set to the specified values. - public static BinaryExpression GreaterThanOrEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.GreaterThanOrEqual, left, right, left.Type); - - /// Creates a BinaryExpression that represents a "less than" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LessThan and the Left and Right properties set to the specified values. - public static BinaryExpression LessThan(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.LessThan, left, right, left.Type); - - /// Creates a BinaryExpression that represents a " less than or equal" numeric comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to LessThanOrEqual and the Left and Right properties set to the specified values. - public static BinaryExpression LessThanOrEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.LessThanOrEqual, left, right, left.Type); - - /// Creates a BinaryExpression that represents an inequality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to NotEqual and the Left and Right properties set to the specified values. - public static BinaryExpression NotEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.NotEqual, left, right, typeof(bool)); - - public static BlockExpression Block(params Expression[] expressions) => - Block(expressions[expressions.Length - 1].Type, Tools.Empty(), expressions); - - public static BlockExpression Block(IReadOnlyList expressions) => - Block(Tools.Empty(), expressions); - - public static BlockExpression Block(IEnumerable variables, params Expression[] expressions) => - Block(variables.AsReadOnlyList(), new List(expressions)); - - public static BlockExpression Block(IReadOnlyList variables, IReadOnlyList expressions) => - Block(expressions[expressions.Count - 1].Type, variables, expressions); - - public static BlockExpression Block(Type type, IEnumerable variables, params Expression[] expressions) => - new BlockExpression(type, variables.AsReadOnlyList(), expressions.AsReadOnlyList()); - - public static BlockExpression Block(Type type, IEnumerable variables, IEnumerable expressions) => - new BlockExpression(type, variables.AsReadOnlyList(), expressions.AsReadOnlyList()); - - public static Expression Block(ParameterExpression variable, Expression expression1, Expression expression2) => - expression2.NodeType == ExpressionType.Throw || expression1.NodeType == ExpressionType.Throw - ? (Expression)Block(new[] {variable}, expression1, expression2) - : new OneVariableTwoExpressionBlockExpression(variable, expression1, expression2); - - /// - /// Creates a LoopExpression with the given body and (optional) break target. - /// - /// The body of the loop. - /// The break target used by the loop body, if required. - /// The created LoopExpression. - public static LoopExpression Loop(Expression body, LabelTarget @break = null) => - @break == null ? new LoopExpression(body, null, null) : new LoopExpression(body, @break, null); - - /// - /// Creates a LoopExpression with the given body. - /// - /// The body of the loop. - /// The break target used by the loop body. - /// The continue target used by the loop body. - /// The created LoopExpression. - public static LoopExpression Loop(Expression body, LabelTarget @break, LabelTarget @continue) => - new LoopExpression(body, @break, @continue); - - public static TryExpression TryCatch(Expression body, params CatchBlock[] handlers) => - new TryExpression(body, null, handlers); - - public static TryExpression TryCatchFinally(Expression body, Expression @finally, params CatchBlock[] handlers) => - new TryExpression(body, @finally, handlers); - - public static TryExpression TryFinally(Expression body, Expression @finally) => - new TryExpression(body, @finally, null); - - public static CatchBlock Catch(ParameterExpression variable, Expression body) => - new CatchBlock(variable, body, null, variable.Type); - - public static CatchBlock Catch(Type test, Expression body) => - new CatchBlock(null, body, null, test); - - /// Creates a UnaryExpression that represents a throwing of an exception. - /// An Expression to set the Operand property equal to. - /// A UnaryExpression that represents the exception. - public static UnaryExpression Throw(Expression value) => Throw(value, typeof(void)); - - /// Creates a UnaryExpression that represents a throwing of an exception with a given type. - /// An Expression to set the Operand property equal to. - /// The Type of the expression. - /// A UnaryExpression that represents the exception. - public static UnaryExpression Throw(Expression value, Type type) => - new TypedUnaryExpression(ExpressionType.Throw, value, type); - - public static LabelExpression Label(LabelTarget target, Expression defaultValue = null) => - new LabelExpression(target, defaultValue); - - public static LabelTarget Label(Type type = null, string name = null) => - SysExpr.Label(type ?? typeof(void), name); - - public static LabelTarget Label(string name) => - SysExpr.Label(typeof(void), name); - - /// Creates a BinaryExpression, given the left and right operands, by calling an appropriate factory method. - /// The ExpressionType that specifies the type of binary operation. - /// An Expression that represents the left operand. - /// An Expression that represents the right operand. - /// The BinaryExpression that results from calling the appropriate factory method. - public static BinaryExpression MakeBinary(ExpressionType binaryType, Expression left, Expression right) - { - switch (binaryType) - { - case ExpressionType.AddAssign: - case ExpressionType.AddAssignChecked: - case ExpressionType.AndAssign: - case ExpressionType.Assign: - case ExpressionType.DivideAssign: - case ExpressionType.ExclusiveOrAssign: - case ExpressionType.LeftShiftAssign: - case ExpressionType.ModuloAssign: - case ExpressionType.MultiplyAssign: - case ExpressionType.MultiplyAssignChecked: - case ExpressionType.OrAssign: - case ExpressionType.PowerAssign: - case ExpressionType.RightShiftAssign: - case ExpressionType.SubtractAssign: - case ExpressionType.SubtractAssignChecked: - return new AssignBinaryExpression(binaryType, left, right, left.Type); - case ExpressionType.ArrayIndex: - return ArrayIndex(left, right); - case ExpressionType.Coalesce: - return Coalesce(left, right); - default: - return new SimpleBinaryExpression(binaryType, left, right, left.Type); - } - } - - public static GotoExpression MakeGoto(GotoExpressionKind kind, LabelTarget target, Expression value, Type type = null) => - new GotoExpression(kind, target, value, type ?? typeof(void)); - - public static GotoExpression Break(LabelTarget target, Expression value = null, Type type = null) => - MakeGoto(GotoExpressionKind.Break, target, value, type); - - public static GotoExpression Continue(LabelTarget target, Type type = null) => - MakeGoto(GotoExpressionKind.Continue, target, null, type); - - /// Creates a BinaryExpression that represents a reference equality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Equal and the Left and Right properties set to the specified values. - public static BinaryExpression ReferenceEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Equal, left, right, typeof(bool)); - - /// Creates a BinaryExpression that represents a reference inequality comparison. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to NotEqual and the Left and Right properties set to the specified values. - public static BinaryExpression ReferenceNotEqual(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.NotEqual, left, right, typeof(bool)); - - public static GotoExpression Return(LabelTarget target, Expression value = null, Type type = null) => - MakeGoto(GotoExpressionKind.Return, target, value, type); - - public static GotoExpression Goto(LabelTarget target, Expression value = null, Type type = null) => - MakeGoto(GotoExpressionKind.Goto, target, value, type); - - public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, params SwitchCase[] cases) => - new SwitchExpression(defaultBody.Type, switchValue, defaultBody, null, cases); - - public static SwitchExpression Switch(Expression switchValue, Expression defaultBody, MethodInfo comparison, params SwitchCase[] cases) => - new SwitchExpression(defaultBody.Type, switchValue, defaultBody, comparison, cases); - - public static SwitchExpression Switch(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, params SwitchCase[] cases) => - new SwitchExpression(type, switchValue, defaultBody, comparison, cases); - - public static SwitchExpression Switch(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable cases) => - new SwitchExpression(type, switchValue, defaultBody, comparison, cases.AsArray()); - - public static SwitchExpression Switch(Expression switchValue, params SwitchCase[] cases) => - new SwitchExpression(null, switchValue, null, null, cases); - - public static SwitchCase SwitchCase(Expression body, IEnumerable testValues) => - new SwitchCase(body, testValues); - - public static SwitchCase SwitchCase(Expression body, params Expression[] testValues) => - new SwitchCase(body, testValues); - - /// Creates a BinaryExpression that represents a coalescing operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A BinaryExpression that has the NodeType property equal to Coalesce and the Left and Right properties set to the specified values. - public static BinaryExpression Coalesce(Expression left, Expression right) => - new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, GetCoalesceType(left.Type, right.Type)); - - /// Creates a BinaryExpression that represents a coalescing operation. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// Result type - /// A BinaryExpression that has the NodeType property equal to Coalesce and the Left and Right properties set to the specified values. - public static BinaryExpression Coalesce(Expression left, Expression right, Type type) => - new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, type); - - /// Creates a BinaryExpression that represents a coalescing operation, given a conversion function. - /// An Expression to set the Left property equal to. - /// An Expression to set the Right property equal to. - /// A LambdaExpression to set the Conversion property equal to. - /// A BinaryExpression that has the NodeType property equal to Coalesce and the Left, Right and Conversion properties set to the specified values. - public static BinaryExpression Coalesce(Expression left, Expression right, LambdaExpression conversion) => - conversion == null ? - new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, GetCoalesceType(left.Type, right.Type)) : - (BinaryExpression)new CoalesceConversionBinaryExpression(left, right, conversion); - - private static Type GetCoalesceType(Type left, Type right) - { - var leftTypeInfo = left.GetTypeInfo(); - if (leftTypeInfo.IsGenericType && leftTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>)) - left = leftTypeInfo.GenericTypeArguments[0]; - - if (right == left) - return left; - - if (leftTypeInfo.IsAssignableFrom(right.GetTypeInfo()) || - right.IsImplicitlyBoxingConvertibleTo(left) || - right.IsImplicitlyNumericConvertibleTo(left)) - return left; - - if (right.GetTypeInfo().IsAssignableFrom(leftTypeInfo) || - left.IsImplicitlyBoxingConvertibleTo(right) || - left.IsImplicitlyNumericConvertibleTo(right)) - return right; - - throw new ArgumentException($"Unable to coalesce arguments of left type of {left} and right type of {right}."); - } - } - - internal static class TypeTools - { - internal static int GetFirstIndex(this IReadOnlyList source, T item) - { - if (source.Count != 0) - for (var i = 0; i < source.Count; ++i) - if (ReferenceEquals(source[i], item)) - return i; - return -1; - } - - internal static bool IsImplicitlyBoxingConvertibleTo(this Type source, Type target) => - source.GetTypeInfo().IsValueType && - (target == typeof(object) || - target == typeof(ValueType)) || - source.GetTypeInfo().IsEnum && target == typeof(Enum); - - internal static PropertyInfo FindProperty(this Type type, string propertyName) - { - var properties = type.GetTypeInfo().DeclaredProperties.AsArray(); - for (var i = 0; i < properties.Length; i++) - if (properties[i].Name == propertyName) - return properties[i]; - - return type.GetTypeInfo().BaseType?.FindProperty(propertyName); - } - - internal static FieldInfo FindField(this Type type, string fieldName) - { - var fields = type.GetTypeInfo().DeclaredFields.AsArray(); - for (var i = 0; i < fields.Length; i++) - if (fields[i].Name == fieldName) - return fields[i]; - - return type.GetTypeInfo().BaseType?.FindField(fieldName); - } - - internal static MethodInfo FindMethod(this Type type, - string methodName, Type[] typeArgs, IReadOnlyList args, bool isStatic = false) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (isStatic == m.IsStatic && methodName == m.Name) - { - typeArgs = typeArgs ?? Type.EmptyTypes; - var mTypeArgs = m.GetGenericArguments(); - - if (typeArgs.Length == mTypeArgs.Length && - (typeArgs.Length == 0 || AreTypesTheSame(typeArgs, mTypeArgs))) - { - args = args ?? Tools.Empty(); - var pars = m.GetParameters(); - if (args.Count == pars.Length && - (args.Count == 0 || AreArgExpressionsAndParamsOfTheSameType(args, pars))) - return m; - } - } - } - - return type.GetTypeInfo().BaseType?.FindMethod(methodName, typeArgs, args, isStatic); - } - - private static bool AreTypesTheSame(Type[] source, Type[] target) - { - for (var i = 0; i < source.Length; i++) - if (source[i] != target[i]) - return false; - return true; - } - - private static bool AreArgExpressionsAndParamsOfTheSameType(IReadOnlyList args, ParameterInfo[] pars) - { - for (var i = 0; i < pars.Length; i++) - if (pars[i].ParameterType != args[i].Type) - return false; - return true; - } - - public static IReadOnlyList AsReadOnlyList(this IEnumerable xs) - { - if (xs is IReadOnlyList list) - return list; - return xs == null ? null : new List(xs); - } - - internal static bool IsImplicitlyNumericConvertibleTo(this Type source, Type target) - { - if (source == typeof(Char)) - return - target == typeof(UInt16) || - target == typeof(Int32) || - target == typeof(UInt32) || - target == typeof(Int64) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(SByte)) - return - target == typeof(Int16) || - target == typeof(Int32) || - target == typeof(Int64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Byte)) - return - target == typeof(Int16) || - target == typeof(UInt16) || - target == typeof(Int32) || - target == typeof(UInt32) || - target == typeof(Int64) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Int16)) - return - target == typeof(Int32) || - target == typeof(Int64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(UInt16)) - return - target == typeof(Int32) || - target == typeof(UInt32) || - target == typeof(Int64) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Int32)) - return - target == typeof(Int64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(UInt32)) - return - target == typeof(UInt32) || - target == typeof(UInt64) || - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Int64) || - source == typeof(UInt64)) - return - target == typeof(Single) || - target == typeof(Double) || - target == typeof(Decimal); - - if (source == typeof(Single)) - return target == typeof(Double); - - return false; - } - } - - /// Converts the object of known type into the valid C# code representation - public static class CodePrinter - { - /// Replace with yours if needed - public static Func GetTypeNameDefault = t => t.Name; - - /// Converts the `typeof()` into the proper C# representation. - public static string ToCode(this Type type, Func getTypeName = null) => - type == null ? "null" : $"typeof({type.ToTypeCode()})"; - - /// Converts the into the proper C# representation. - public static string ToTypeCode(this Type type, Func getTypeName = null) - { - var isArray = type.IsArray; - if (isArray) - type = type.GetElementType(); - - var typeName = (getTypeName ?? GetTypeNameDefault)(type); - - var typeInfo = type.GetTypeInfo(); - if (!typeInfo.IsGenericType) - return typeName.Replace('+', '.'); - - var s = new StringBuilder(typeName.Substring(0, typeName.IndexOf('`')).Replace('+', '.')); - s.Append('<'); - - var genericArgs = typeInfo.GetGenericTypeParametersOrArguments(); - if (typeInfo.IsGenericTypeDefinition) - s.Append(',', genericArgs.Length - 1); - else - { - for (var i = 0; i < genericArgs.Length; i++) - { - if (i > 0) - s.Append(", "); - s.Append(genericArgs[i].ToTypeCode(getTypeName)); - } - } - - s.Append('>'); - - if (isArray) - s.Append("[]"); - - return s.ToString(); - } - - /// Prints valid C# Boolean - public static string ToCode(this bool x) => x ? "true" : "false"; - - /// Prints valid C# String escaping the things - public static string ToCode(this string x) => - x == null ? "null" - : $"\"{x.Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n")}\""; - - - /// Prints valid c# Enum literal - public static string ToEnumValueCode(this Type enumType, object x) - { - var enumTypeInfo = enumType.GetTypeInfo(); - if (enumTypeInfo.IsGenericType && enumTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - if (x == null) - return "null"; - enumType = GetGenericTypeParametersOrArguments(enumTypeInfo)[0]; - } - - return $"{enumType.ToTypeCode()}.{Enum.GetName(enumType, x)}"; - } - - private static Type[] GetGenericTypeParametersOrArguments(this TypeInfo typeInfo) => - typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters : typeInfo.GenericTypeArguments; - - /// Prints many code items as array initializer. - public static string ToCommaSeparatedCode(this IEnumerable items, Func notRecognizedToCode) - { - var s = new StringBuilder(); - var count = 0; - foreach (var item in items) - { - if (count++ != 0) - s.Append(", "); - s.Append(item.ToCode(notRecognizedToCode)); - } - return s.ToString(); - } - - /// Prints many code items as array initializer. - public static string ToArrayInitializerCode(this IEnumerable items, Type itemType, Func notRecognizedToCode) => - $"new {itemType.ToTypeCode()}[]{{{items.ToCommaSeparatedCode(notRecognizedToCode)}}}"; - - /// Prints valid C# for known type, - /// otherwise uses , - /// otherwise falls back to `ToString()` - public static string ToCode(this object x, Func notRecognizedToCode) - { - if (x == null) - return "null"; - - if (x is bool b) - return b.ToCode(); - - if (x is string s) - return s.ToCode(); - - if (x is Type t) - return t.ToCode(); - - var xTypeInfo = x.GetType().GetTypeInfo(); - if (xTypeInfo.IsEnum) - return x.GetType().ToEnumValueCode(x); - - if (x is IEnumerable e) - { - var elemType = xTypeInfo.IsArray - ? xTypeInfo.GetElementType() - : xTypeInfo.GetGenericTypeParametersOrArguments().GetFirst(); - if (elemType != null) - return e.ToArrayInitializerCode(elemType, notRecognizedToCode); - } - - if (xTypeInfo.IsPrimitive) - return x.ToString(); - - if (notRecognizedToCode != null) - return notRecognizedToCode(x); - - return x.ToString(); - } - } - - public class UnaryExpression : Expression - { - public override ExpressionType NodeType { get; } - - public override Type Type => Operand.Type; - public readonly Expression Operand; - - public virtual MethodInfo Method => null; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) - { - switch (NodeType) - { - case ExpressionType.ArrayLength: - return SysExpr.ArrayLength(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Convert: - return SysExpr.Convert(Operand.ToExpression(ref exprsConverted), Type, Method); - case ExpressionType.Decrement: - return SysExpr.Decrement(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Increment: - return SysExpr.Increment(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.IsFalse: - return SysExpr.IsFalse(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.IsTrue: - return SysExpr.IsTrue(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Negate: - return SysExpr.Negate(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.NegateChecked: - return SysExpr.NegateChecked(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.OnesComplement: - return SysExpr.OnesComplement(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PostDecrementAssign: - return SysExpr.PostDecrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PostIncrementAssign: - return SysExpr.PostIncrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PreDecrementAssign: - return SysExpr.PreDecrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.PreIncrementAssign: - return SysExpr.PreIncrementAssign(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Quote: - return SysExpr.Quote(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.UnaryPlus: - return SysExpr.UnaryPlus(Operand.ToExpression(ref exprsConverted)); - case ExpressionType.Unbox: - return SysExpr.Unbox(Operand.ToExpression(ref exprsConverted), Type); - case ExpressionType.Throw: - return SysExpr.Throw(Operand.ToExpression(ref exprsConverted), Type); - default: - throw new NotSupportedException("Cannot convert Expression to Expression of type " + NodeType); - } - } - - /// - public override string CodeString - { - get - { - switch (NodeType) - { - case ExpressionType.ArrayLength: - return $"ArrayLength({Operand.CodeString})"; - case ExpressionType.Convert: - if (Method == null) - return $"Convert({Operand.CodeString}, {Type.ToCode()})"; - var methodIndex = Method.DeclaringType.GetTypeInfo().DeclaredMethods.AsArray().GetFirstIndex(Method); - return $"Convert({Operand.CodeString}, {Type.ToCode()}, {Method.DeclaringType.ToCode()}.GetTypeInfo().DeclaredMethods.ToArray()[{methodIndex}])"; - case ExpressionType.Decrement: - return $"Decrement({Operand.CodeString})"; - case ExpressionType.Increment: - return $"Increment({Operand.CodeString})"; - case ExpressionType.IsFalse: - return $"IsFalse({Operand.CodeString})"; - case ExpressionType.IsTrue: - return $"IsTrue({Operand.CodeString})"; - case ExpressionType.Negate: - return $"Negate({Operand.CodeString})"; - case ExpressionType.NegateChecked: - return $"NegateChecked({Operand.CodeString})"; - case ExpressionType.OnesComplement: - return $"OnesComplement({Operand.CodeString})"; - case ExpressionType.PostDecrementAssign: - return $"PostDecrementAssign({Operand.CodeString})"; - case ExpressionType.PostIncrementAssign: - return $"PostIncrementAssign({Operand.CodeString})"; - case ExpressionType.PreDecrementAssign: - return $"PreDecrementAssign({Operand.CodeString})"; - case ExpressionType.PreIncrementAssign: - return $"PreIncrementAssign({Operand.CodeString})"; - case ExpressionType.Quote: - return $"Quote({Operand.CodeString})"; - case ExpressionType.UnaryPlus: - return $"UnaryPlus({Operand.CodeString})"; - case ExpressionType.Unbox: - return $"Unbox({Operand.CodeString}, {Type.ToCode()})"; - case ExpressionType.Throw: - return $"Throw({Operand.CodeString}, {Type.ToCode()})"; - default: - throw new NotSupportedException("Cannot convert Expression to Expression of type " + NodeType); - } - } - } - - public UnaryExpression(ExpressionType nodeType, Expression operand) - { - NodeType = nodeType; - Operand = operand; - } - } - - public class TypedUnaryExpression : UnaryExpression - { - public override Type Type { get; } - - public TypedUnaryExpression(ExpressionType nodeType, Expression operand, Type type) : base(nodeType, operand) => - Type = type; - } - - public sealed class TypedUnaryExpression : UnaryExpression - { - public override Type Type => typeof(T); - - public TypedUnaryExpression(ExpressionType nodeType, Expression operand) : base(nodeType, operand) { } - } - - public sealed class ConvertWithMethodUnaryExpression : TypedUnaryExpression - { - public override MethodInfo Method { get; } - public override Type Type => Method.ReturnType; - - public ConvertWithMethodUnaryExpression(ExpressionType nodeType, Expression operand, MethodInfo method) - : base(nodeType, operand, method.ReturnType) => - Method = method; - - public ConvertWithMethodUnaryExpression(ExpressionType nodeType, Expression operand, Type type, MethodInfo method) - : base(nodeType, operand, type) => - Method = method; - } - - public abstract class BinaryExpression : Expression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - public readonly Expression Left, Right; - - public override string CodeString => - $"{Enum.GetName(typeof(ExpressionType), NodeType)}(" + NewLine + - $"{Left.CodeString}," + NewLine + - $"{Right.CodeString})"; - - protected BinaryExpression(ExpressionType nodeType, Expression left, Expression right, Type type) - { - NodeType = nodeType; - - Left = left; - Right = right; - - if (nodeType == ExpressionType.Equal || - nodeType == ExpressionType.NotEqual || - nodeType == ExpressionType.GreaterThan || - nodeType == ExpressionType.GreaterThanOrEqual || - nodeType == ExpressionType.LessThan || - nodeType == ExpressionType.LessThanOrEqual || - nodeType == ExpressionType.And || - nodeType == ExpressionType.AndAlso || - nodeType == ExpressionType.Or || - nodeType == ExpressionType.OrElse) - { - Type = typeof(bool); - } - else - Type = type; - } - } - - public class TypeBinaryExpression : Expression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - public Type TypeOperand { get; } - - public readonly Expression Expression; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.TypeIs(Expression.ToExpression(ref exprsConverted), TypeOperand); - - public override string CodeString => $"TypeIs({Expression.CodeString}, {TypeOperand.ToCode()})"; - - internal TypeBinaryExpression(ExpressionType nodeType, Expression expression, Type typeOperand) - { - NodeType = nodeType; - Expression = expression; - Type = typeof(bool); - TypeOperand = typeOperand; - } - } - - public sealed class SimpleBinaryExpression : BinaryExpression - { - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) - { - switch (NodeType) - { - case ExpressionType.Add: - return SysExpr.Add(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Subtract: - return SysExpr.Subtract(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Multiply: - return SysExpr.Multiply(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Divide: - return SysExpr.Divide(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Power: - return SysExpr.Power(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Coalesce: - return SysExpr.Coalesce(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.And: - return SysExpr.And(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AndAlso: - return SysExpr.AndAlso(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Or: - return SysExpr.Or(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.OrElse: - return SysExpr.OrElse(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.Equal: - return SysExpr.Equal(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.NotEqual: - return SysExpr.NotEqual(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.GreaterThan: - return SysExpr.GreaterThan(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.GreaterThanOrEqual: - return SysExpr.GreaterThanOrEqual(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.LessThan: - return SysExpr.LessThan(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.LessThanOrEqual: - return SysExpr.LessThanOrEqual(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - default: - throw new NotSupportedException($"Not a valid {NodeType} for arithmetic or boolean binary expression."); - } - } - - internal SimpleBinaryExpression(ExpressionType nodeType, Expression left, Expression right, Type type) - : base(nodeType, left, right, type) { } - } - - public class CoalesceConversionBinaryExpression : BinaryExpression - { - public readonly LambdaExpression Conversion; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Coalesce(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted), Conversion.ToLambdaExpression()); - - public override string CodeString => - $"Coalesce({Left.CodeString}, {Right.CodeString}, {Conversion.CodeString})"; - - internal CoalesceConversionBinaryExpression(Expression left, Expression right, LambdaExpression conversion) - : base(ExpressionType.Coalesce, left, right, null) - { - Conversion = conversion; - } - } - - public sealed class ArrayIndexExpression : BinaryExpression - { - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.ArrayIndex(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - - internal ArrayIndexExpression(Expression left, Expression right, Type type) - : base(ExpressionType.ArrayIndex, left, right, type) { } - } - - public sealed class AssignBinaryExpression : BinaryExpression - { - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) - { - switch (NodeType) - { - case ExpressionType.Assign: - return SysExpr.Assign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AddAssign: - return SysExpr.AddAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AddAssignChecked: - return SysExpr.AddAssignChecked(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.SubtractAssign: - return SysExpr.SubtractAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.SubtractAssignChecked: - return SysExpr.SubtractAssignChecked(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.MultiplyAssign: - return SysExpr.MultiplyAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.MultiplyAssignChecked: - return SysExpr.MultiplyAssignChecked(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.DivideAssign: - return SysExpr.DivideAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.PowerAssign: - return SysExpr.PowerAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.AndAssign: - return SysExpr.AndAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - case ExpressionType.OrAssign: - return SysExpr.OrAssign(Left.ToExpression(ref exprsConverted), Right.ToExpression(ref exprsConverted)); - default: - throw new NotSupportedException($"Not a valid {NodeType} for Assign binary expression."); - } - } - - public override string CodeString => - $"{Enum.GetName(typeof(ExpressionType), NodeType)}({Left.CodeString}, {Right.CodeString})"; - - internal AssignBinaryExpression(Expression left, Expression right, Type type) - : base(ExpressionType.Assign, left, right, type) { } - - internal AssignBinaryExpression(ExpressionType expressionType, Expression left, Expression right, Type type) - : base(expressionType, left, right, type) { } - } - - public sealed class MemberInitExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.MemberInit; - public override Type Type => Expression.Type; - - public NewExpression NewExpression => Expression as NewExpression; - - public readonly Expression Expression; - public readonly IReadOnlyList Bindings; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.MemberInit((System.Linq.Expressions.NewExpression)NewExpression.ToExpression(ref exprsConverted), - BindingsToExpressions(Bindings, ref exprsConverted)); - - internal static System.Linq.Expressions.MemberBinding[] BindingsToExpressions( - IReadOnlyList ms, ref LiveCountArray exprsConverted) - { - if (ms.Count == 0) - return Tools.Empty(); - - if (ms.Count == 1) - return new[] { ms[0].ToMemberBinding(ref exprsConverted) }; - - var result = new System.Linq.Expressions.MemberBinding[ms.Count]; - for (var i = 0; i < result.Length; ++i) - result[i] = ms[i].ToMemberBinding(ref exprsConverted); - return result; - } - - public override string CodeString - { - get - { - var bindings = ""; - for (var i = 0; i < Bindings.Count; i++) - { - if (i > 0) - bindings += "," + NewLine; - bindings += Bindings[i].CodeString; - } - return $"MemberInit({Expression.CodeString}," + NewLine + - $"{bindings})"; - } - } - - internal MemberInitExpression(NewExpression newExpression, MemberBinding[] bindings) - : this((Expression)newExpression, bindings) { } - - internal MemberInitExpression(Expression expression, MemberBinding[] bindings) - { - Expression = expression; - Bindings = bindings ?? Tools.Empty(); - } - } - - public sealed class ParameterExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Parameter; - public override Type Type { get; } - - // todo: we need the version without this members - public readonly string Name; - public readonly bool IsByRef; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Parameter(IsByRef ? Type.MakeByRefType() : Type, Name); - - public override string CodeString => - Name != null - ? $"Parameter({Type.ToCode()}{(IsByRef ? ".MakeByRefType()" : "")}, \"{Name}\")" - : $"Parameter({Type.ToCode()}{(IsByRef ? ".MakeByRefType()" : "")})"; - - internal static System.Linq.Expressions.ParameterExpression[] ToParameterExpressions( - IReadOnlyList ps, ref LiveCountArray exprsConverted) - { - if (ps.Count == 0) - return Tools.Empty(); - - if (ps.Count == 1) - return new[] { (System.Linq.Expressions.ParameterExpression)ps[0].ToExpression(ref exprsConverted) }; - - var result = new System.Linq.Expressions.ParameterExpression[ps.Count]; - for (var i = 0; i < result.Length; ++i) - result[i] = (System.Linq.Expressions.ParameterExpression)ps[i].ToExpression(ref exprsConverted); - return result; - } - - internal ParameterExpression(Type type, string name, bool isByRef) - { - Type = type; - Name = name; - IsByRef = isByRef; - } - } - - public class ConstantExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Constant; - public override Type Type => Value.GetType(); - - public readonly object Value; - - internal ConstantExpression(object value) => Value = value; - - internal override SysExpr CreateSysExpression(ref LiveCountArray _) => - SysExpr.Constant(Value, Type); - - /// note: Change to your method to use in for spitting the C# code for the - /// You may try to use `ObjectToCode` from `https://www.nuget.org/packages/ExpressionToCodeLib` - public static Func NotRecognizedValueToCode = x => x.ToString(); - - public override string CodeString => - $"Constant({Value.ToCode(NotRecognizedValueToCode)}, {Type.ToCode()})"; - } - - public sealed class TypedConstantExpression : ConstantExpression - { - public override Type Type { get; } - - internal TypedConstantExpression(object value, Type type) : base(value) => Type = type; - } - - public sealed class TypedConstantExpression : ConstantExpression - { - public override Type Type => typeof(T); - - internal TypedConstantExpression(T value) : base(value) { } - } - - public abstract class ArgumentsExpression : Expression - { - public readonly IReadOnlyList Arguments; - - protected ArgumentsExpression(IReadOnlyList arguments) => Arguments = arguments ?? Tools.Empty(); - } - - public class NewExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.New; - public override Type Type => Constructor.DeclaringType; - - public readonly ConstructorInfo Constructor; - - public virtual int FewArgumentCount => 0; - public virtual IReadOnlyList Arguments => Tools.Empty(); - - internal NewExpression(ConstructorInfo constructor) => Constructor = constructor; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.New(Constructor, ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString - { - get - { - var ctorIndex = Constructor.DeclaringType.GetTypeInfo().DeclaredConstructors.ToArray().GetFirstIndex(Constructor); - return $"New({Type.ToCode()}.GetTypeInfo().DeclaredConstructors.ToArray()[{ctorIndex}]," + NewLine + - $"{ToParamsCode(Arguments)})"; - } - } - } - - public sealed class NewValueTypeExpression : NewExpression - { - public override Type Type { get; } - - internal NewValueTypeExpression(Type type) : base(null) => Type = type; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => SysExpr.New(Type); - - public override string CodeString => $"New({Type.ToCode()})"; - } - - public sealed class OneArgumentNewExpression : NewExpression - { - public readonly Expression Argument; - public override IReadOnlyList Arguments => new[] { Argument }; - public override int FewArgumentCount => 1; - - internal OneArgumentNewExpression(ConstructorInfo constructor, Expression argument) : base(constructor) => - Argument = argument; - } - - public sealed class TwoArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1 }; - public override int FewArgumentCount => 2; - - internal TwoArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - } - } - - public sealed class ThreeArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2 }; - public override int FewArgumentCount => 3; - - internal ThreeArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1, Expression argument2) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - } - } - - public sealed class FourArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3 }; - public override int FewArgumentCount => 4; - - internal FourArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1, Expression argument2, Expression argument3) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - } - } - - public sealed class FiveArgumentsNewExpression : NewExpression - { - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - public readonly Expression Argument4; - - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3, Argument4 }; - public override int FewArgumentCount => 5; - - internal FiveArgumentsNewExpression(ConstructorInfo constructor, - Expression argument0, Expression argument1, Expression argument2, Expression argument3, Expression argument4) : base(constructor) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - Argument4 = argument4; - } - } - - public sealed class ManyArgumentsNewExpression : NewExpression - { - public override IReadOnlyList Arguments { get; } - public override int FewArgumentCount => -1; - - internal ManyArgumentsNewExpression(ConstructorInfo constructor, IReadOnlyList arguments) : base(constructor) => - Arguments = arguments; - } - - public sealed class NewArrayExpression : ArgumentsExpression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - // I made it a ICollection for now to use Arguments as input, without changing Arguments type - public IReadOnlyList Expressions => Arguments; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - NodeType == ExpressionType.NewArrayInit - // ReSharper disable once AssignNullToNotNullAttribute - ? SysExpr.NewArrayInit(Type.GetElementType(), ToExpressions(Arguments, ref exprsConverted)) - // ReSharper disable once AssignNullToNotNullAttribute - : SysExpr.NewArrayBounds(Type.GetElementType(), ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString => - NodeType == ExpressionType.NewArrayInit - // ReSharper disable once AssignNullToNotNullAttribute - ? $"NewArrayInit({Type.GetElementType().ToCode()}," + NewLine + $"{ToParamsCode(Arguments)})" - // ReSharper disable once AssignNullToNotNullAttribute - : $"NewArrayBounds({Type.GetElementType().ToCode()}," + NewLine + $"{ToParamsCode(Arguments)})"; - - internal NewArrayExpression(ExpressionType expressionType, Type arrayType, IReadOnlyList elements) : base(elements) - { - NodeType = expressionType; - Type = arrayType; - } - } - - public class MethodCallExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Call; - public override Type Type => Method.ReturnType; - - public virtual Expression Object => null; - public virtual IReadOnlyList Arguments => Tools.Empty(); - public virtual int FewArgumentCount => 0; - - public readonly MethodInfo Method; - - internal MethodCallExpression(MethodInfo method) => Method = method; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Call(Object?.ToExpression(ref exprsConverted), Method, - ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString - { - get - { - var methodIndex = Method.DeclaringType.GetTypeInfo().GetDeclaredMethods(Method.Name).AsArray().GetFirstIndex(Method); - return $"Call({Object?.CodeString ?? "null"}," + NewLine + - $"{Method.DeclaringType.ToCode()}.GetTypeInfo().GetDeclaredMethods(\"{Method.Name}\").ToArray()[{methodIndex}]," + NewLine + - $"{ToParamsCode(Arguments)})"; - } - } - } - - public sealed class InstanceMethodCallExpression : MethodCallExpression - { - public override Expression Object { get; } - - internal InstanceMethodCallExpression(Expression instance, MethodInfo method) : base(method) => - Object = instance; - } - - public class ManyArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments { get; } - public override int FewArgumentCount => -1; - - internal ManyArgumentsMethodCallExpression(MethodInfo method, IReadOnlyList arguments) : base(method) => - Arguments = arguments; - } - - public sealed class InstanceManyArgumentsMethodCallExpression : ManyArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceManyArgumentsMethodCallExpression(Expression instance, MethodInfo method, IReadOnlyList arguments) - : base(method, arguments) => Object = instance; - } - - public class OneArgumentMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument }; - public override int FewArgumentCount => 1; - - public readonly Expression Argument; - - internal OneArgumentMethodCallExpression(MethodInfo method, Expression argument) : base(method) => - Argument = argument; - } - - public sealed class InstanceOneArgumentMethodCallExpression : OneArgumentMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceOneArgumentMethodCallExpression(Expression instance, MethodInfo method, Expression argument) - : base(method, argument) => Object = instance; - } - - public class TwoArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1 }; - public override int FewArgumentCount => 2; - - public readonly Expression Argument0; - public readonly Expression Argument1; - - internal TwoArgumentsMethodCallExpression(MethodInfo method, Expression argument0, Expression argument1) : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - } - } - - public sealed class InstanceTwoArgumentsMethodCallExpression : TwoArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceTwoArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1) : base(method, argument0, argument1) => Object = instance; - } - - public class ThreeArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2 }; - public override int FewArgumentCount => 3; - - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - - internal ThreeArgumentsMethodCallExpression(MethodInfo method, - Expression argument0, Expression argument1, Expression argument2) : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - } - } - - public sealed class InstanceThreeArgumentsMethodCallExpression : ThreeArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceThreeArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1, Expression argument2) - : base(method, argument0, argument1, argument2) => Object = instance; - } - - public class FourArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3 }; - public override int FewArgumentCount => 4; - - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - - internal FourArgumentsMethodCallExpression(MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3) : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - } - } - - public sealed class InstanceFourArgumentsMethodCallExpression : FourArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceFourArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3) - : base(method, argument0, argument1, argument2, argument3) => Object = instance; - } - - public class FiveArgumentsMethodCallExpression : MethodCallExpression - { - public override IReadOnlyList Arguments => new[] { Argument0, Argument1, Argument2, Argument3, Argument4 }; - public override int FewArgumentCount => 5; - - public readonly Expression Argument0; - public readonly Expression Argument1; - public readonly Expression Argument2; - public readonly Expression Argument3; - public readonly Expression Argument4; - - internal FiveArgumentsMethodCallExpression(MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3, Expression argument4) - : base(method) - { - Argument0 = argument0; - Argument1 = argument1; - Argument2 = argument2; - Argument3 = argument3; - Argument4 = argument4; - } - } - - public sealed class InstanceFiveArgumentsMethodCallExpression : FiveArgumentsMethodCallExpression - { - public override Expression Object { get; } - - internal InstanceFiveArgumentsMethodCallExpression(Expression instance, MethodInfo method, - Expression argument0, Expression argument1, Expression argument2, Expression argument3, Expression argument4) - : base(method, argument0, argument1, argument2, argument3, argument4) => Object = instance; - } - - public abstract class MemberExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.MemberAccess; - public readonly MemberInfo Member; - public readonly Expression Expression; - - protected MemberExpression(Expression expression, MemberInfo member) - { - Expression = expression; - Member = member; - } - } - - // todo: specialize to 2 class - with and without object expression - public sealed class PropertyExpression : MemberExpression - { - public override Type Type => PropertyInfo.PropertyType; - public PropertyInfo PropertyInfo => (PropertyInfo)Member; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Property(Expression?.ToExpression(ref exprsConverted), PropertyInfo); - - public override string CodeString - { - get - { - var propIndex = PropertyInfo.DeclaringType.GetTypeInfo().DeclaredProperties.AsArray().GetFirstIndex(PropertyInfo); - return $"Property({Expression?.CodeString ?? "null"}," + NewLine + - $"{PropertyInfo.DeclaringType.ToCode()}.GetTypeInfo().DeclaredProperties.ToArray()[{propIndex}])"; - } - } - - internal PropertyExpression(Expression instance, PropertyInfo property) : - base(instance, property) { } - } - - // todo: specialize to 2 class - with and without object expression - public sealed class FieldExpression : MemberExpression - { - public override Type Type => FieldInfo.FieldType; - public FieldInfo FieldInfo => (FieldInfo)Member; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Field(Expression?.ToExpression(ref exprsConverted), FieldInfo); - - public override string CodeString - { - get - { - var fieldIndex = FieldInfo.DeclaringType.GetTypeInfo().DeclaredFields.AsArray().GetFirstIndex(FieldInfo); - return $"Field({Expression?.CodeString ?? "null"}," + NewLine + - $"{FieldInfo.DeclaringType.ToCode()}.GetTypeInfo().DeclaredProperties.ToArray()[{fieldIndex}])"; - } - } - - internal FieldExpression(Expression instance, FieldInfo field) - : base(instance, field) { } - } - - public abstract class MemberBinding - { - public readonly MemberInfo Member; - - public abstract MemberBindingType BindingType { get; } - public abstract string CodeString { get; } - - internal abstract System.Linq.Expressions.MemberBinding ToMemberBinding(ref LiveCountArray exprsConverted); - - internal MemberBinding(MemberInfo member) - { - Member = member; - } - } - - public sealed class MemberAssignment : MemberBinding - { - public readonly Expression Expression; - - public override MemberBindingType BindingType => MemberBindingType.Assignment; - - internal override System.Linq.Expressions.MemberBinding ToMemberBinding(ref LiveCountArray exprsConverted) => - SysExpr.Bind(Member, Expression.ToExpression(ref exprsConverted)); - - public override string CodeString - { - get - { - var memberIndex = Member.DeclaringType.GetTypeInfo().DeclaredMembers.AsArray().GetFirstIndex(Member); - return $"Bind({Member.DeclaringType.ToCode()}.GetTypeInfo().DeclaredMembers.ToArray()[{memberIndex}]," + NewLine + - $"{Expression.CodeString})"; - } - } - - internal MemberAssignment(MemberInfo member, Expression expression) : base(member) - { - Expression = expression; - } - } - - public sealed class InvocationExpression : ArgumentsExpression - { - public override ExpressionType NodeType => ExpressionType.Invoke; - public override Type Type { get; } - - public readonly Expression Expression; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Invoke(Expression.ToExpression(ref exprsConverted), ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString => - $"Invoke({Expression.CodeString}," + NewLine + - $"{ToParamsCode(Arguments)})"; - - internal InvocationExpression(Expression expression, IReadOnlyList arguments, Type type) : base(arguments) - { - Expression = expression; - Type = type; - } - } - - public sealed class DefaultExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Default; - public override Type Type { get; } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - Type == typeof(void)? SysExpr.Empty() : SysExpr.Default(Type); - - public override string CodeString => - Type == typeof(void) ? "Empty()" : $"Default({Type.ToCode()})"; - - internal DefaultExpression(Type type) - { - Type = type; - } - } - - public sealed class ConditionalExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Conditional; - public override Type Type => _type ?? IfTrue.Type; - - public readonly Expression Test; - public readonly Expression IfTrue; - public readonly Expression IfFalse; - private readonly Type _type; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - _type == null - ? SysExpr.Condition(Test.ToExpression(ref exprsConverted), IfTrue.ToExpression(ref exprsConverted), IfFalse.ToExpression(ref exprsConverted)) - : SysExpr.Condition(Test.ToExpression(ref exprsConverted), IfTrue.ToExpression(ref exprsConverted), IfFalse.ToExpression(ref exprsConverted), _type); - - public override string CodeString => - _type == null - ? $"Condition({Test.CodeString}," + NewLine + $"{IfTrue.CodeString}," + NewLine + $"{IfFalse.CodeString})" - : $"Condition({Test.CodeString}," + NewLine + $"{IfTrue.CodeString}," + NewLine + $"{IfFalse.CodeString}," + NewLine + $"{_type.ToCode()})"; - - internal ConditionalExpression(Expression test, Expression ifTrue, Expression ifFalse, Type type = null) - { - Test = test; - IfTrue = ifTrue; - IfFalse = ifFalse; - _type = type; - } - } - - /// For indexer property or array access. - public sealed class IndexExpression : ArgumentsExpression - { - public override ExpressionType NodeType => ExpressionType.Index; - public override Type Type => Indexer != null ? Indexer.PropertyType : Object.Type.GetElementType(); - - public readonly Expression Object; - public readonly PropertyInfo Indexer; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.MakeIndex(Object.ToExpression(ref exprsConverted), Indexer, ToExpressions(Arguments, ref exprsConverted)); - - public override string CodeString - { - get - { - var propIndex = Indexer.DeclaringType.GetTypeInfo().DeclaredProperties.AsArray().GetFirstIndex(Indexer); - return $"MakeIndex({Object.CodeString}," + NewLine + - $"{Indexer.DeclaringType.ToCode()}.GetTypeInfo().DeclaredProperties.ToArray()[{propIndex}], {ToParamsCode(Arguments)})"; - } - } - - internal IndexExpression(Expression @object, PropertyInfo indexer, IReadOnlyList arguments) - : base(arguments) - { - Object = @object; - Indexer = indexer; - } - } - - /// Optimized version for the specific block structure - public sealed class OneVariableTwoExpressionBlockExpression : Expression - { - public static explicit operator BlockExpression(OneVariableTwoExpressionBlockExpression x) => - Block(new[] { x.Variable }, x.Expression1, x.Expression2); - - public override ExpressionType NodeType => ExpressionType.Block; - public override Type Type => Expression2.Type; - - public new readonly ParameterExpression Variable; - public readonly Expression Expression1; - public readonly Expression Expression2; - public Expression Result => Expression2; - - internal OneVariableTwoExpressionBlockExpression(ParameterExpression variable, Expression expression1, Expression expression2) - { - Variable = variable; - Expression1 = expression1; - Expression2 = expression2; - } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - ((BlockExpression)this).CreateSysExpression(ref exprsConverted); - - public override string CodeString => ((BlockExpression)this).CodeString; - } - - public sealed class BlockExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Block; - public override Type Type { get; } - - public readonly IReadOnlyList Variables; - public readonly IReadOnlyList Expressions; - public Expression Result => Expressions[Expressions.Count - 1]; - - internal BlockExpression(Type type, IReadOnlyList variables, IReadOnlyList expressions) - { - Variables = variables ?? Tools.Empty(); - Expressions = expressions ?? Tools.Empty(); - Type = type; - } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Block(Type, - ParameterExpression.ToParameterExpressions(Variables, ref exprsConverted), - ToExpressions(Expressions, ref exprsConverted)); - - public override string CodeString => - "Block(" + NewLine + - $"{Type.ToCode()}," + NewLine + - $"new ParameterExpression[]{{ {(Variables.Count == 0 ? "" : ToParamsCode(Variables))} }}," + NewLine + - $"{ToParamsCode(Expressions)})"; - } - - public sealed class LoopExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Loop; - - public override Type Type => typeof(void); - - public readonly Expression Body; - public readonly LabelTarget BreakLabel; - public readonly LabelTarget ContinueLabel; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - BreakLabel == null ? SysExpr.Loop(Body.ToExpression(ref exprsConverted)) : - ContinueLabel == null ? SysExpr.Loop(Body.ToExpression(ref exprsConverted), BreakLabel) : - SysExpr.Loop(Body.ToExpression(ref exprsConverted), BreakLabel, ContinueLabel); - - public override string CodeString => - BreakLabel == null ? $"Loop({Body.CodeString})" : - ContinueLabel == null ? $"Loop({Body.CodeString}," + NewLine + "Label(\"break\"))" : - $"Loop({Body.CodeString}," + NewLine + "Label(\"break\"), Label(\"continue\"))"; - - internal LoopExpression(Expression body, LabelTarget breakLabel, LabelTarget continueLabel) - { - Body = body; - BreakLabel = breakLabel; - ContinueLabel = continueLabel; - } - } - - public sealed class TryExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Try; - public override Type Type => Body.Type; - - public readonly Expression Body; - public IReadOnlyList Handlers => _handlers; - private readonly CatchBlock[] _handlers; - public readonly Expression Finally; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - Finally == null ? - SysExpr.TryCatch(Body.ToExpression(ref exprsConverted), - ToCatchBlocks(_handlers, ref exprsConverted)) : - Handlers == null ? - SysExpr.TryFinally(Body.ToExpression(ref exprsConverted), - Finally.ToExpression(ref exprsConverted)) : - SysExpr.TryCatchFinally(Body.ToExpression(ref exprsConverted), - Finally.ToExpression(ref exprsConverted), ToCatchBlocks(_handlers, ref exprsConverted)); - - private static System.Linq.Expressions.CatchBlock ToCatchBlock( - ref CatchBlock cb, ref LiveCountArray exprsConverted) => - SysExpr.MakeCatchBlock(cb.Test, - (System.Linq.Expressions.ParameterExpression)cb.Variable?.ToExpression(ref exprsConverted), - cb.Body.ToExpression(ref exprsConverted), - cb.Filter?.ToExpression(ref exprsConverted)); - - private static System.Linq.Expressions.CatchBlock[] ToCatchBlocks( - CatchBlock[] hs, ref LiveCountArray exprsConverted) - { - if (hs == null) - return Tools.Empty(); - var catchBlocks = new System.Linq.Expressions.CatchBlock[hs.Length]; - for (var i = 0; i < hs.Length; ++i) - catchBlocks[i] = ToCatchBlock(ref hs[i], ref exprsConverted); - return catchBlocks; - } - - public override string CodeString => - Finally == null ? $"TryCatch({Body.CodeString}," + NewLine + $"{ToCatchBlocksCode(Handlers)})" : - Handlers == null ? $"TryFinally({Body.CodeString}, " + NewLine + $"{Finally.CodeString})" : - $"TryCatchFinally({Body.CodeString}," + NewLine + $"{Finally.CodeString}," + NewLine + $"{ToCatchBlocksCode(Handlers)})"; - - private static string ToCatchBlocksCode(IReadOnlyList hs) - { - if (hs.Count == 0) - return "new CatchBlock[0]"; - - var s = ""; - for (var i = 0; i < hs.Count; i++) - { - if (i > 0) - s += ", " + NewLine; - s += hs[i].CodeString; - } - - return s; - } - - internal TryExpression(Expression body, Expression @finally, CatchBlock[] handlers) - { - Body = body; - _handlers = handlers; - Finally = @finally; - } - } - - public struct CatchBlock - { - public readonly ParameterExpression Variable; - public readonly Expression Body; - public readonly Expression Filter; - public readonly Type Test; - - internal CatchBlock(ParameterExpression variable, Expression body, Expression filter, Type test) - { - Variable = variable; - Body = body; - Filter = filter; - Test = test; - } - - internal string CodeString => - $"MakeCatchBlock({Test.ToCode()}, {Variable?.CodeString ?? "null"}," + NewLine + - $"{Body.CodeString}," + NewLine + - $"{Filter?.CodeString ?? "null"})"; - } - - public sealed class LabelExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Label; - public override Type Type => Target.Type; - - public readonly LabelTarget Target; - public readonly Expression DefaultValue; - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - DefaultValue == null ? SysExpr.Label(Target) : SysExpr.Label(Target, DefaultValue.ToExpression(ref exprsConverted)); - - // todo: Introduce proper LabelTarget instead of system one - public override string CodeString => - DefaultValue == null ? $"Label({Target})" : $"Label({Target}, {DefaultValue.CodeString})"; - - internal LabelExpression(LabelTarget target, Expression defaultValue) - { - Target = target; - DefaultValue = defaultValue; - } - } - - public sealed class GotoExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Goto; - public override Type Type { get; } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - Value == null - ? SysExpr.Goto(Target, Type) - : SysExpr.Goto(Target, Value.ToExpression(ref exprsConverted), Type); - - public override string CodeString => - Value == null - ? $"Goto({Target}, {Type.ToCode()})" - : $"Goto({Target}, {Value.CodeString}, {Type.ToCode()})"; - - public readonly Expression Value; - public readonly LabelTarget Target; - public readonly GotoExpressionKind Kind; - - internal GotoExpression(GotoExpressionKind kind, LabelTarget target, Expression value, Type type) - { - Type = type; - Kind = kind; - Value = value; - Target = target; - } - } - - public struct SwitchCase - { - public readonly IReadOnlyList TestValues; - public readonly Expression Body; - - public string CodeString => - $"SwitchCase({Body.CodeString}, {Expression.ToParamsCode(TestValues)})"; - - public SwitchCase(Expression body, IEnumerable testValues) - { - Body = body; - TestValues = testValues.AsReadOnlyList(); - } - } - - public class SwitchExpression : Expression - { - public override ExpressionType NodeType { get; } - public override Type Type { get; } - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Switch(SwitchValue.ToExpression(ref exprsConverted), - DefaultBody.ToExpression(ref exprsConverted), Comparison, - ToSwitchCaseExpressions(_cases, ref exprsConverted)); - - public override string CodeString - { - get - { - var methodIndex = Comparison.DeclaringType.GetTypeInfo().DeclaredMethods.AsArray().GetFirstIndex(Comparison); - return $"Switch({SwitchValue.CodeString}," + NewLine + - $"{DefaultBody.CodeString}," + NewLine + - $"{Comparison.DeclaringType.ToCode()}.GetTypeInfo().DeclaredMethods.ToArray()[{methodIndex}]," + NewLine + - $"{ToSwitchCasesCode(_cases)})"; - } - } - - internal static System.Linq.Expressions.SwitchCase ToSwitchCase(ref SwitchCase sw, ref LiveCountArray exprsConverted) => - SysExpr.SwitchCase(sw.Body.ToExpression(ref exprsConverted), ToExpressions(sw.TestValues, ref exprsConverted)); - - internal static System.Linq.Expressions.SwitchCase[] ToSwitchCaseExpressions( - SwitchCase[] sw, ref LiveCountArray exprsConverted) - { - if (sw.Length == 0) - return Tools.Empty(); - - var result = new System.Linq.Expressions.SwitchCase[sw.Length]; - for (var i = 0; i < result.Length; ++i) - result[i] = ToSwitchCase(ref sw[i], ref exprsConverted); - return result; - } - - internal static string ToSwitchCasesCode(IReadOnlyList items) - { - if (items.Count == 0) - return "new SwitchCase[0]"; - - var s = ""; - for (var i = 0; i < items.Count; i++) - { - if (i > 0) - s += "," + NewLine; - s += items[i].CodeString; - } - - return s; - } - - public readonly Expression SwitchValue; - public IReadOnlyList Cases => _cases; - private readonly SwitchCase[] _cases; - public readonly Expression DefaultBody; - public readonly MethodInfo Comparison; - - public SwitchExpression(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, SwitchCase[] cases) - { - NodeType = ExpressionType.Switch; - Type = type; - SwitchValue = switchValue; - DefaultBody = defaultBody; - Comparison = comparison; - _cases = cases; - } - } - - public class LambdaExpression : Expression - { - public override ExpressionType NodeType => ExpressionType.Lambda; - public override Type Type { get; } - - public readonly Type ReturnType; - public readonly Expression Body; - public virtual IReadOnlyList Parameters => Tools.Empty(); - - public System.Linq.Expressions.LambdaExpression ToLambdaExpression() => - (System.Linq.Expressions.LambdaExpression)ToExpression(); - - internal override SysExpr CreateSysExpression(ref LiveCountArray exprsConverted) => - SysExpr.Lambda(Type, Body.ToExpression(ref exprsConverted), ParameterExpression.ToParameterExpressions(Parameters, ref exprsConverted)); - - public override string CodeString => - $"Lambda({Type.ToCode()}," + NewLine + - $"{Body.CodeString}," + NewLine + - $"{ToParamsCode(Parameters)})"; - - internal LambdaExpression(Type delegateType, Expression body, Type returnType) - { - Body = body; - ReturnType = returnType; - - if (delegateType != null && delegateType != typeof(Delegate)) - Type = delegateType; - else - Type = Tools.GetFuncOrActionType(Tools.Empty(), ReturnType); - } - } - - public sealed class ManyParametersLambdaExpression : LambdaExpression - { - public override IReadOnlyList Parameters { get; } - - internal ManyParametersLambdaExpression(Type delegateType, Expression body, IReadOnlyList parameters, Type returnType) - : base(delegateType, body, returnType) => Parameters = parameters; - } - - public class Expression : LambdaExpression - { - public new System.Linq.Expressions.Expression ToLambdaExpression() - { - var exprsConverted = new LiveCountArray(Tools.Empty()); - return SysExpr.Lambda(Body.ToExpression(ref exprsConverted), - ParameterExpression.ToParameterExpressions(Parameters, ref exprsConverted)); - } - - internal Expression(Expression body, Type returnType) - : base(typeof(TDelegate), body, returnType) { } - } - - public sealed class ManyParametersExpression : Expression - { - public override IReadOnlyList Parameters { get; } - - internal ManyParametersExpression(Expression body, IReadOnlyList parameters, Type returnType) - : base(body, returnType) => Parameters = parameters; - } -} - -#endif diff --git a/BattleFieldSimulator/ConsoleClient/DryIoc/FastExpressionCompiler.cs b/BattleFieldSimulator/ConsoleClient/DryIoc/FastExpressionCompiler.cs deleted file mode 100644 index 674de92..0000000 --- a/BattleFieldSimulator/ConsoleClient/DryIoc/FastExpressionCompiler.cs +++ /dev/null @@ -1,4739 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2016-2020 Maksim Volkau - -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 AddOrUpdateServiceFactory -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. -*/ - -// ReSharper disable CoVariantArrayConversion - -/* -// Lists the target platforms that are Not supported by FEC - simplifies the direct referencing of Expression.cs file -*/ -#if !PCL && !NET35 && !NET40 && !NET403 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETCOREAPP1_0 && !NETCOREAPP1_1 -#define SUPPORTS_FAST_EXPRESSION_COMPILER -#endif - -#if !SUPPORTS_FAST_EXPRESSION_COMPILER -namespace FastExpressionCompiler.LightExpression -{ - using System; - using System.Linq.Expressions; - - /// Polyfill for absence of FastExpressionCompiler: https://github.com/dadhi/FastExpressionCompiler - public static class ExpressionCompiler - { - internal static object TryCompileBoundToFirstClosureParam(Type delegateType, - Expression bodyExpr, ParameterExpression[] paramExprs, Type[] closurePlusParamTypes, Type returnType) => null; - - internal static Func CompileFast(this Expression> lambdaExpr) => lambdaExpr.Compile(); - - internal class ArrayClosure {} - } -} -#else -// ReSharper disable CoVariantArrayConversion -namespace FastExpressionCompiler.LightExpression -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Reflection; - using System.Reflection.Emit; - using System.Threading; - - /// Compiles expression to delegate ~20 times faster than Expression.Compile. - /// Partial to extend with your things when used as source file. - // ReSharper disable once PartialTypeWithSinglePart - public static partial class ExpressionCompiler - { -#region Expression.CompileFast overloads for Delegate, Func, and Action - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static TDelegate CompileFast(this LambdaExpression lambdaExpr, - bool ifFastFailedReturnNull = false) where TDelegate : class => - (TDelegate)(TryCompileBoundToFirstClosureParam(typeof(TDelegate) == typeof(Delegate) ? lambdaExpr.Type : typeof(TDelegate), - lambdaExpr.Body, lambdaExpr.Parameters, GetClosureTypeToParamTypes(lambdaExpr.Parameters), lambdaExpr.ReturnType) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys())); - - /// Compiles a static method to the passed IL Generator. - /// Could be used as alternative for `CompileToMethod` like this . - /// Check `IssueTests.Issue179_Add_something_like_LambdaExpression_CompileToMethod.cs` for example. - public static bool CompileFastToIL(this LambdaExpression lambdaExpr, ILGenerator il, bool ifFastFailedReturnNull = false) - { - var closureInfo = new ClosureInfo(ClosureStatus.ShouldBeStaticMethod); - - var parentFlags = lambdaExpr.ReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(lambdaExpr.Body, lambdaExpr.Parameters, il, ref closureInfo, parentFlags)) - return false; - - il.Emit(OpCodes.Ret); - return true; - } - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Delegate CompileFast(this LambdaExpression lambdaExpr, bool ifFastFailedReturnNull = false) => - (Delegate)TryCompileBoundToFirstClosureParam(lambdaExpr.Type, lambdaExpr.Body, lambdaExpr.Parameters, - GetClosureTypeToParamTypes(lambdaExpr.Parameters), lambdaExpr.ReturnType) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Unifies Compile for System.Linq.Expressions and FEC.LightExpression - public static TDelegate CompileSys(this Expression lambdaExpr) where TDelegate : class => - lambdaExpr - .ToLambdaExpression() - .Compile(); - - /// Unifies Compile for System.Linq.Expressions and FEC.LightExpression - public static Delegate CompileSys(this LambdaExpression lambdaExpr) => - lambdaExpr - .ToLambdaExpression() - .Compile(); - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static TDelegate CompileFast(this Expression lambdaExpr, - bool ifFastFailedReturnNull = false) - where TDelegate : class => ((LambdaExpression)lambdaExpr).CompileFast(ifFastFailedReturnNull); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, _closureAsASingleParamType, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1), typeof(T2) }, - typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Func CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Func)TryCompileBoundToFirstClosureParam(typeof(Func), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }, typeof(R)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, _closureAsASingleParamType, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, new[] { typeof(ArrayClosure), typeof(T1), typeof(T2) }, - typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast(this Expression> lambdaExpr, - bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - - /// Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing. - public static Action CompileFast( - this Expression> lambdaExpr, bool ifFastFailedReturnNull = false) => - (Action)TryCompileBoundToFirstClosureParam(typeof(Action), - lambdaExpr.Body, lambdaExpr.Parameters, - new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }, typeof(void)) - ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()); - -#endregion - - /// Tries to compile lambda expression to - public static TDelegate TryCompile(this LambdaExpression lambdaExpr) where TDelegate : class => - (TDelegate)TryCompileBoundToFirstClosureParam(typeof(TDelegate) == typeof(Delegate) ? lambdaExpr.Type : typeof(TDelegate), - lambdaExpr.Body, lambdaExpr.Parameters, GetClosureTypeToParamTypes(lambdaExpr.Parameters), lambdaExpr.ReturnType); - - /// Tries to compile lambda expression to - /// with the provided closure object and constant expressions (or lack there of) - - /// Constant expression should be the in order of Fields in closure object! - /// Note 1: Use it on your own risk - FEC won't verify the expression is compile-able with passed closure, it is up to you! - /// Note 2: The expression with NESTED LAMBDA IS NOT SUPPORTED! - /// Note 3: `Label` and `GoTo` are not supported in this case, because they need first round to collect out-of-order labels - public static TDelegate TryCompileWithPreCreatedClosure(this LambdaExpression lambdaExpr, - params ConstantExpression[] closureConstantsExprs) - where TDelegate : class - { - var constValues = new object[closureConstantsExprs.Length]; - for (var i = 0; i < constValues.Length; i++) - constValues[i] = closureConstantsExprs[i].Value; - - var closureInfo = new ClosureInfo(ClosureStatus.UserProvided | ClosureStatus.HasClosure, constValues); - - var closurePlusParamTypes = GetClosureTypeToParamTypes(lambdaExpr.Parameters); - var method = new DynamicMethod(string.Empty, lambdaExpr.ReturnType, closurePlusParamTypes, - typeof(ExpressionCompiler), skipVisibility: true); - - var il = method.GetILGenerator(); - - EmittingVisitor.EmitLoadConstantsAndNestedLambdasIntoVars(il, ref closureInfo); - - var parentFlags = lambdaExpr.ReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(lambdaExpr.Body, lambdaExpr.Parameters, il, ref closureInfo, parentFlags)) - return null; - il.Emit(OpCodes.Ret); - - var delegateType = typeof(TDelegate) != typeof(Delegate) ? typeof(TDelegate) : lambdaExpr.Type; - var @delegate = (TDelegate)(object)method.CreateDelegate(delegateType, new ArrayClosure(constValues)); - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return @delegate; - } - - /// Tries to compile expression to "static" delegate, skipping the step of collecting the closure object. - public static TDelegate TryCompileWithoutClosure(this LambdaExpression lambdaExpr) - where TDelegate : class - { - var closureInfo = new ClosureInfo(ClosureStatus.UserProvided); - var closurePlusParamTypes = GetClosureTypeToParamTypes(lambdaExpr.Parameters); - - var method = new DynamicMethod(string.Empty, lambdaExpr.ReturnType, closurePlusParamTypes, - typeof(ArrayClosure), skipVisibility: true); - - var il = method.GetILGenerator(); - var parentFlags = lambdaExpr.ReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(lambdaExpr.Body, lambdaExpr.Parameters, il, ref closureInfo, parentFlags)) - return null; - il.Emit(OpCodes.Ret); - - var delegateType = typeof(TDelegate) != typeof(Delegate) ? typeof(TDelegate) : lambdaExpr.Type; - var @delegate = (TDelegate)(object)method.CreateDelegate(delegateType, EmptyArrayClosure); - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return @delegate; - } - -#region Obsolete - - /// Obsolete - [Obsolete("Not used - candidate for removal")] - public static TDelegate TryCompile( - Expression bodyExpr, IReadOnlyList paramExprs, Type[] paramTypes, Type returnType) - where TDelegate : class => - (TDelegate)TryCompile(typeof(TDelegate), bodyExpr, paramExprs, paramTypes, returnType); - - /// Obsolete - [Obsolete("Not used - candidate for removal")] - public static object TryCompile(Type delegateType, - Expression bodyExpr, IReadOnlyList paramExprs, Type[] paramTypes, Type returnType) => - TryCompileBoundToFirstClosureParam( - delegateType != typeof(Delegate) ? delegateType : Tools.GetFuncOrActionType(paramTypes, returnType), - bodyExpr, paramExprs, GetClosureTypeToParamTypes(paramExprs), returnType); - -#endregion - - internal static object TryCompileBoundToFirstClosureParam(Type delegateType, - Expression bodyExpr, IReadOnlyList paramExprs, Type[] closurePlusParamTypes, Type returnType) - { - var closureInfo = new ClosureInfo(ClosureStatus.ToBeCollected); - if (!TryCollectBoundConstants(ref closureInfo, bodyExpr, paramExprs, false, ref closureInfo)) - return null; - - var nestedLambdas = closureInfo.NestedLambdas; - if (nestedLambdas.Length != 0) - for (var i = 0; i < nestedLambdas.Length; ++i) - if (!TryCompileNestedLambda(ref closureInfo, i)) - return null; - - var closure = (closureInfo.Status & ClosureStatus.HasClosure) == 0 - ? EmptyArrayClosure - : new ArrayClosure(closureInfo.GetArrayOfConstantsAndNestedLambdas()); - - var method = new DynamicMethod(string.Empty, - returnType, closurePlusParamTypes, typeof(ArrayClosure), true); - - var il = method.GetILGenerator(); - - if (closure.ConstantsAndNestedLambdas != null) - EmittingVisitor.EmitLoadConstantsAndNestedLambdasIntoVars(il, ref closureInfo); - - var parentFlags = returnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(bodyExpr, paramExprs, il, ref closureInfo, parentFlags)) - return null; - - il.Emit(OpCodes.Ret); - - var @delegate = method.CreateDelegate(delegateType, closure); - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return @delegate; - } - - private static Type[] PrependClosureTypeToParamTypes(IReadOnlyList paramExprs) - { - var count = paramExprs.Count; - var closureAndParamTypes = new Type[count + 1]; - closureAndParamTypes[0] = typeof(ArrayClosure); - for (var i = 0; i < count; i++) - { - var parameterExpr = paramExprs[i]; - closureAndParamTypes[i + 1] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type; - } - return closureAndParamTypes; - } - - private static readonly Type[] _closureAsASingleParamType = { typeof(ArrayClosure) }; - private static readonly Type[][] _closureTypePlusParamTypesPool = new Type[8][]; - - private static Type[] GetClosureTypeToParamTypes(IReadOnlyList paramExprs) - { - var paramCount = paramExprs.Count; - if (paramCount == 0) - return _closureAsASingleParamType; - - if (paramCount < 8) - { - var closureAndParamTypes = Interlocked.Exchange(ref _closureTypePlusParamTypesPool[paramCount], null); - if (closureAndParamTypes != null) - { - for (var i = 0; i < paramExprs.Count; i++) - { - var parameterExpr = paramExprs[i]; - closureAndParamTypes[i + 1] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type; - } - return closureAndParamTypes; - } - } - - return PrependClosureTypeToParamTypes(paramExprs); - } - - private static void ReturnClosureTypeToParamTypesToPool(Type[] closurePlusParamTypes) - { - var paramCount = closurePlusParamTypes.Length - 1; - if (paramCount != 0 && paramCount < 8) - Interlocked.Exchange(ref _closureTypePlusParamTypesPool[paramCount], closurePlusParamTypes); - } - - private struct BlockInfo - { - public object VarExprs; // ParameterExpression | IReadOnlyList - public int[] VarIndexes; - } - - [Flags] - private enum ClosureStatus - { - ToBeCollected = 1, - UserProvided = 1 << 1, - HasClosure = 1 << 2, - ShouldBeStaticMethod = 1 << 3 - } - - /// Track the info required to build a closure object + some context information not directly related to closure. - private struct ClosureInfo - { - public bool LastEmitIsAddress; - - /// Helpers to know if a Return GotoExpression's Label should be emitted. - /// First set bit is ContainsReturnGoto, the rest is ReturnLabelIndex - private int[] _tryCatchFinallyInfos; - public int CurrentTryCatchFinallyIndex; - - /// Tracks the stack of blocks where are we in emit phase - private LiveCountArray _blockStack; - - /// Dictionary for the used Labels in IL - private KeyValuePair[] _labels; - - public ClosureStatus Status; - - /// Constant expressions to find an index (by reference) of constant expression from compiled expression. - public LiveCountArray Constants; - - /// Parameters not passed through lambda parameter list But used inside lambda body. - /// The top expression should Not contain not passed parameters. - public ParameterExpression[] NonPassedParameters; - - /// All nested lambdas recursively nested in expression - public NestedLambdaInfo[] NestedLambdas; - - /// Constant usage count and variable index - public LiveCountArray ConstantUsage; - - /// Populates info directly with provided closure object and constants. - public ClosureInfo(ClosureStatus status, object[] constValues = null) - { - Status = status; - - Constants = new LiveCountArray(constValues ?? Tools.Empty()); - ConstantUsage = new LiveCountArray(constValues == null ? Tools.Empty() : new int[constValues.Length]); - - NonPassedParameters = Tools.Empty(); - NestedLambdas = Tools.Empty(); - - LastEmitIsAddress = false; - CurrentTryCatchFinallyIndex = -1; - _tryCatchFinallyInfos = null; - _labels = null; - _blockStack = new LiveCountArray(Tools.Empty()); - } - - public void AddConstant(object value) - { - Status |= ClosureStatus.HasClosure; - - var constItems = Constants.Items; - var constIndex = Constants.Count - 1; - while (constIndex != -1 && !ReferenceEquals(constItems[constIndex], value)) - --constIndex; - if (constIndex == -1) - { - Constants.PushSlot(value); - ConstantUsage.PushSlot(1); - } - else - { - ++ConstantUsage.Items[constIndex]; - } - } - - public void AddNonPassedParam(ParameterExpression expr) - { - Status |= ClosureStatus.HasClosure; - - if (NonPassedParameters.Length == 0) - { - NonPassedParameters = new[] { expr }; - return; - } - - var count = NonPassedParameters.Length; - for (var i = 0; i < count; ++i) - if (ReferenceEquals(NonPassedParameters[i], expr)) - return; - - if (NonPassedParameters.Length == 1) - NonPassedParameters = new[] { NonPassedParameters[0], expr }; - else if (NonPassedParameters.Length == 2) - NonPassedParameters = new[] { NonPassedParameters[0], NonPassedParameters[1], expr }; - else - { - var newItems = new ParameterExpression[count + 1]; - Array.Copy(NonPassedParameters, 0, newItems, 0, count); - newItems[count] = expr; - NonPassedParameters = newItems; - } - } - - public void AddNestedLambda(NestedLambdaInfo nestedLambdaInfo) - { - Status |= ClosureStatus.HasClosure; - - var nestedLambdas = NestedLambdas; - var count = nestedLambdas.Length; - if (count == 0) - NestedLambdas = new[] { nestedLambdaInfo }; - else if (count == 1) - NestedLambdas = new[] { nestedLambdas[0], nestedLambdaInfo }; - else if (count == 2) - NestedLambdas = new[] { nestedLambdas[0], nestedLambdas[1], nestedLambdaInfo }; - else - { - var newNestedLambdas = new NestedLambdaInfo[count + 1]; - Array.Copy(nestedLambdas, 0, newNestedLambdas, 0, count); - newNestedLambdas[count] = nestedLambdaInfo; - NestedLambdas = newNestedLambdas; - } - } - - public void AddLabel(LabelTarget labelTarget) - { - if (labelTarget != null && - GetLabelIndex(labelTarget) == -1) - _labels = _labels.WithLast(new KeyValuePair(labelTarget, null)); - } - - public Label GetOrCreateLabel(LabelTarget labelTarget, ILGenerator il) => - GetOrCreateLabel(GetLabelIndex(labelTarget), il); - - public Label GetOrCreateLabel(int index, ILGenerator il) - { - var labelPair = _labels[index]; - var label = labelPair.Value; - if (!label.HasValue) - _labels[index] = new KeyValuePair(labelPair.Key, label = il.DefineLabel()); - return label.Value; - } - - public int GetLabelIndex(LabelTarget labelTarget) - { - if (_labels != null) - for (var i = 0; i < _labels.Length; ++i) - if (_labels[i].Key == labelTarget) - return i; - return -1; - } - - public void AddTryCatchFinallyInfo() - { - ++CurrentTryCatchFinallyIndex; - var infos = _tryCatchFinallyInfos; - if (infos == null) - _tryCatchFinallyInfos = new int[1]; - else if (infos.Length == 1) - _tryCatchFinallyInfos = new[] { infos[0], 0 }; - else if (infos.Length == 2) - _tryCatchFinallyInfos = new[] { infos[0], infos[1], 0 }; - else - { - var sourceLength = infos.Length; - var newInfos = new int[sourceLength + 1]; - Array.Copy(infos, newInfos, sourceLength); - _tryCatchFinallyInfos = newInfos; - } - } - - public void MarkAsContainsReturnGotoExpression() - { - if (CurrentTryCatchFinallyIndex != -1) - _tryCatchFinallyInfos[CurrentTryCatchFinallyIndex] |= 1; - } - - public void MarkReturnLabelIndex(int index) - { - if (CurrentTryCatchFinallyIndex != -1) - _tryCatchFinallyInfos[CurrentTryCatchFinallyIndex] |= index << 1; - } - - public bool TryCatchFinallyContainsReturnGotoExpression() => - _tryCatchFinallyInfos != null && (_tryCatchFinallyInfos[++CurrentTryCatchFinallyIndex] & 1) != 0; - - public object[] GetArrayOfConstantsAndNestedLambdas() - { - var constCount = Constants.Count; - var nestedLambdas = NestedLambdas; - if (constCount == 0) - { - if (nestedLambdas.Length == 0) - return null; - - var nestedLambdaItems = new object[nestedLambdas.Length]; - for (var i = 0; i < nestedLambdas.Length; i++) - { - var nestedLambda = nestedLambdas[i]; - if (nestedLambda.ClosureInfo.NonPassedParameters.Length == 0) - nestedLambdaItems[i] = nestedLambda.Lambda; - else - nestedLambdaItems[i] = new NestedLambdaWithConstantsAndNestedLambdas( - nestedLambda.Lambda, nestedLambda.ClosureInfo.GetArrayOfConstantsAndNestedLambdas()); - } - - return nestedLambdaItems; - } - - var constItems = Constants.Items; - if (nestedLambdas.Length == 0) - return constItems; - - var itemCount = constCount + nestedLambdas.Length; - - var closureItems = constItems; - if (itemCount > constItems.Length) - { - closureItems = new object[itemCount]; - for (var i = 0; i < constCount; ++i) - closureItems[i] = constItems[i]; - } - - for (var i = 0; i < nestedLambdas.Length; i++) - { - var nestedLambda = nestedLambdas[i]; - if (nestedLambda.ClosureInfo.NonPassedParameters.Length == 0) - closureItems[constCount + i] = nestedLambda.Lambda; - else - closureItems[constCount + i] = new NestedLambdaWithConstantsAndNestedLambdas( - nestedLambda.Lambda, nestedLambda.ClosureInfo.GetArrayOfConstantsAndNestedLambdas()); - } - - return closureItems; - } - - /// LocalVar maybe a `null` in collecting phase when we only need to decide if ParameterExpression is an actual parameter or variable - public void PushBlockWithVars(ParameterExpression blockVarExpr) - { - ref var block = ref _blockStack.PushSlot(); - block.VarExprs = blockVarExpr; - } - - public void PushBlockWithVars(ParameterExpression blockVarExpr, int varIndex) - { - ref var block = ref _blockStack.PushSlot(); - block.VarExprs = blockVarExpr; - block.VarIndexes = new[] { varIndex }; - } - - /// LocalVars maybe a `null` in collecting phase when we only need to decide if ParameterExpression is an actual parameter or variable - public void PushBlockWithVars(IReadOnlyList blockVarExprs, int[] localVarIndexes = null) - { - ref var block = ref _blockStack.PushSlot(); - block.VarExprs = blockVarExprs; - block.VarIndexes = localVarIndexes; - } - - public void PushBlockAndConstructLocalVars(IReadOnlyList blockVarExprs, ILGenerator il) - { - var localVars = new int[blockVarExprs.Count]; - for (var i = 0; i < localVars.Length; i++) - localVars[i] = il.GetNextLocalVarIndex(blockVarExprs[i].Type); - - PushBlockWithVars(blockVarExprs, localVars); - } - - public void PopBlock() => _blockStack.Pop(); - - public bool IsLocalVar(object varParamExpr) - { - for (var i = _blockStack.Count - 1; i > -1; --i) - { - var varExprObj = _blockStack.Items[i].VarExprs; - if (ReferenceEquals(varExprObj, varParamExpr)) - return true; - - if (varExprObj is IReadOnlyList varExprs) - for (var j = 0; j < varExprs.Count; j++) - if (ReferenceEquals(varExprs[j], varParamExpr)) - return true; - } - - return false; - } - - public int GetDefinedLocalVarOrDefault(ParameterExpression varParamExpr) - { - for (var i = _blockStack.Count - 1; i > -1; --i) - { - ref var block = ref _blockStack.Items[i]; - var varExprObj = block.VarExprs; - - if (ReferenceEquals(varExprObj, varParamExpr)) - return block.VarIndexes[0]; - - if (varExprObj is IReadOnlyList varExprs) - for (var j = 0; j < varExprs.Count; j++) - if (ReferenceEquals(varExprs[j], varParamExpr)) - return block.VarIndexes[j]; - } - return -1; - } - - public bool IsTryReturnLabel(int index) - { - var tryCatchFinallyInfos = _tryCatchFinallyInfos; - if (tryCatchFinallyInfos != null) - for (var i = 0; i < tryCatchFinallyInfos.Length; ++i) - if (tryCatchFinallyInfos[i] >> 1 == index) - return true; - return false; - } - } - -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - - public static readonly ArrayClosure EmptyArrayClosure = new ArrayClosure(null); - - public static FieldInfo ArrayClosureArrayField = - typeof(ArrayClosure).GetTypeInfo().GetDeclaredField(nameof(ArrayClosure.ConstantsAndNestedLambdas)); - - public static FieldInfo ArrayClosureWithNonPassedParamsField = - typeof(ArrayClosureWithNonPassedParams).GetTypeInfo().GetDeclaredField(nameof(ArrayClosureWithNonPassedParams.NonPassedParams)); - - public static ConstructorInfo ArrayClosureWithNonPassedParamsConstructor = - typeof(ArrayClosureWithNonPassedParams).GetTypeInfo().DeclaredConstructors.GetFirst(); - - public class ArrayClosure - { - public readonly object[] ConstantsAndNestedLambdas; - public ArrayClosure(object[] constantsAndNestedLambdas) => ConstantsAndNestedLambdas = constantsAndNestedLambdas; - } - - public sealed class ArrayClosureWithNonPassedParams : ArrayClosure - { - public readonly object[] NonPassedParams; - - public ArrayClosureWithNonPassedParams(object[] constantsAndNestedLambdas, object[] nonPassedParams) : base(constantsAndNestedLambdas) => - NonPassedParams = nonPassedParams; - } - - public sealed class NestedLambdaWithConstantsAndNestedLambdas - { - public static FieldInfo NestedLambdaField = - typeof(NestedLambdaWithConstantsAndNestedLambdas).GetTypeInfo().GetDeclaredField(nameof(NestedLambda)); - - public static FieldInfo ConstantsAndNestedLambdasField = - typeof(NestedLambdaWithConstantsAndNestedLambdas).GetTypeInfo().GetDeclaredField(nameof(ConstantsAndNestedLambdas)); - - public readonly object NestedLambda; - public readonly object ConstantsAndNestedLambdas; - public NestedLambdaWithConstantsAndNestedLambdas(object nestedLambda, object constantsAndNestedLambdas) - { - NestedLambda = nestedLambda; - ConstantsAndNestedLambdas = constantsAndNestedLambdas; - } - } - - private sealed class NestedLambdaInfo - { - public readonly LambdaExpression LambdaExpression; - public ClosureInfo ClosureInfo; - public object Lambda; - public int UsageCountOrVarIndex; - - public NestedLambdaInfo(LambdaExpression lambdaExpression) - { - LambdaExpression = lambdaExpression; - ClosureInfo = new ClosureInfo(ClosureStatus.ToBeCollected); - Lambda = null; - } - } - - internal static class CurryClosureFuncs - { - public static readonly MethodInfo[] Methods = - typeof(CurryClosureFuncs).GetTypeInfo().DeclaredMethods.AsArray(); - - public static Func Curry(Func f, C c) => - () => f(c); - - public static Func Curry(Func f, C c) => - t1 => f(c, t1); - - public static Func Curry(Func f, C c) => - (t1, t2) => f(c, t1, t2); - - public static Func Curry(Func f, C c) => - (t1, t2, t3) => f(c, t1, t2, t3); - - public static Func Curry(Func f, C c) => - (t1, t2, t3, t4) => f(c, t1, t2, t3, t4); - - public static Func Curry(Func f, - C c) => (t1, t2, t3, t4, t5) => f(c, t1, t2, t3, t4, t5); - - public static Func - Curry(Func f, C c) => - (t1, t2, t3, t4, t5, t6) => f(c, t1, t2, t3, t4, t5, t6); - } - - internal static class CurryClosureActions - { - public static readonly MethodInfo[] Methods = - typeof(CurryClosureActions).GetTypeInfo().DeclaredMethods.AsArray(); - - public static Action Curry(Action a, C c) => - () => a(c); - - public static Action Curry(Action f, C c) => - t1 => f(c, t1); - - public static Action Curry(Action f, C c) => - (t1, t2) => f(c, t1, t2); - - public static Action Curry(Action f, C c) => - (t1, t2, t3) => f(c, t1, t2, t3); - - public static Action Curry(Action f, C c) => - (t1, t2, t3, t4) => f(c, t1, t2, t3, t4); - - public static Action Curry(Action f, - C c) => (t1, t2, t3, t4, t5) => f(c, t1, t2, t3, t4, t5); - - public static Action - Curry(Action f, C c) => - (t1, t2, t3, t4, t5, t6) => f(c, t1, t2, t3, t4, t5, t6); - } - -#region Collect Bound Constants - - /// Helps to identify constants as the one to be put into the Closure - public static bool IsClosureBoundConstant(object value, TypeInfo type) => - value is Delegate || - !type.IsPrimitive && !type.IsEnum && value is string == false && value is Type == false && value is decimal == false; - - // @paramExprs is required for nested lambda compilation - private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression expr, - IReadOnlyList paramExprs, bool isNestedLambda, ref ClosureInfo rootClosure) - { - while (true) - { - if (expr == null) - return false; - - switch (expr.NodeType) - { - case ExpressionType.Constant: - var constantExpr = (ConstantExpression)expr; - var value = constantExpr.Value; - if (value != null && IsClosureBoundConstant(value, value.GetType().GetTypeInfo())) - closure.AddConstant(value); - return true; - - case ExpressionType.Quote: - //var operand = ((UnaryExpression)expr).Operand; - //if (operand != null && IsClosureBoundConstant(operand, expr.Type.GetTypeInfo())) - // closure.AddConstant(operand); - return false; - - case ExpressionType.Parameter: - // if parameter is used BUT is not in passed parameters and not in local variables, - // it means parameter is provided by outer lambda and should be put in closure for current lambda - var p = paramExprs.Count - 1; - while (p != -1 && !ReferenceEquals(paramExprs[p], expr)) --p; - if (p == -1 && !closure.IsLocalVar(expr)) - { - if (!isNestedLambda) - return false; - closure.AddNonPassedParam((ParameterExpression)expr); - } - return true; - - case ExpressionType.Call: - var callExpr = (MethodCallExpression)expr; - var callObjectExpr = callExpr.Object; - - var fewCallArgCount = callExpr.FewArgumentCount; - if (fewCallArgCount == 0) - { - if (callObjectExpr != null) - { - expr = callObjectExpr; - continue; - } - - return true; - } - - if (fewCallArgCount > 0) - { - if (callObjectExpr != null && - !TryCollectBoundConstants(ref closure, callObjectExpr, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - if (fewCallArgCount == 1) - { - expr = ((OneArgumentMethodCallExpression)callExpr).Argument; - continue; - } - - if (fewCallArgCount == 2) - { - var twoArgsExpr = (TwoArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, twoArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = twoArgsExpr.Argument1; - continue; - } - - if (fewCallArgCount == 3) - { - var threeArgsExpr = (ThreeArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, threeArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, threeArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = threeArgsExpr.Argument2; - continue; - } - - if (fewCallArgCount == 4) - { - var fourArgsExpr = (FourArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, fourArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fourArgsExpr.Argument3; - continue; - } - - var fiveArgsExpr = (FiveArgumentsMethodCallExpression)callExpr; - if (!TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument3, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fiveArgsExpr.Argument4; - continue; - } - - var methodArgs = callExpr.Arguments; - var methodArgCount = methodArgs.Count; - if (methodArgCount == 0) - { - if (callObjectExpr != null) - { - expr = callObjectExpr; - continue; - } - - return true; - } - - if (callObjectExpr != null && - !TryCollectBoundConstants(ref closure, callExpr.Object, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - for (var i = 0; i < methodArgCount - 1; i++) - if (!TryCollectBoundConstants(ref closure, methodArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - - expr = methodArgs[methodArgCount - 1]; - continue; - - case ExpressionType.MemberAccess: - var memberExpr = ((MemberExpression)expr).Expression; - if (memberExpr == null) - return true; - expr = memberExpr; - continue; - - case ExpressionType.New: - var newExpr = (NewExpression)expr; - - var fewArgCount = newExpr.FewArgumentCount; - if (fewArgCount == 0) - return true; - - if (fewArgCount > 0) - { - if (fewArgCount == 1) - { - expr = ((OneArgumentNewExpression)newExpr).Argument; - continue; - } - - if (fewArgCount == 2) - { - var twoArgsExpr = (TwoArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, twoArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = twoArgsExpr.Argument1; - continue; - } - - if (fewArgCount == 3) - { - var threeArgsExpr = (ThreeArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, threeArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, threeArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = threeArgsExpr.Argument2; - continue; - } - - if (fewArgCount == 4) - { - var fourArgsExpr = (FourArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, fourArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fourArgsExpr.Argument3; - continue; - } - - if (fewArgCount == 4) - { - var fourArgsExpr = (FourArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, fourArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fourArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fourArgsExpr.Argument3; - continue; - } - - var fiveArgsExpr = (FiveArgumentsNewExpression)newExpr; - if (!TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument0, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument2, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, fiveArgsExpr.Argument3, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = fiveArgsExpr.Argument4; - continue; - } - - var ctorArgs = ((NewExpression)expr).Arguments; - var ctorLastArgIndex = ctorArgs.Count - 1; - if (ctorLastArgIndex == -1) - return true; - - for (var i = 0; i < ctorLastArgIndex; i++) - if (!TryCollectBoundConstants(ref closure, ctorArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = ctorArgs[ctorLastArgIndex]; - continue; - - case ExpressionType.NewArrayBounds: - case ExpressionType.NewArrayInit: - var elemExprs = ((NewArrayExpression)expr).Expressions; - var elemExprsCount = elemExprs.Count; - if (elemExprsCount == 0) - return true; - - for (var i = 0; i < elemExprsCount - 1; i++) - if (!TryCollectBoundConstants(ref closure, elemExprs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - - expr = elemExprs[elemExprsCount - 1]; - continue; - - case ExpressionType.MemberInit: - return TryCollectMemberInitExprConstants( - ref closure, (MemberInitExpression)expr, paramExprs, isNestedLambda, ref rootClosure); - - case ExpressionType.Lambda: - var nestedLambdaExpr = (LambdaExpression)expr; - - // Look for the already collected lambdas and if we have the same lambda, start from the root - var nestedLambdas = rootClosure.NestedLambdas; - if (nestedLambdas.Length != 0) - { - var foundLambdaInfo = FindAlreadyCollectedNestedLambdaInfo(nestedLambdas, nestedLambdaExpr, out var foundInLambdas); - if (foundLambdaInfo != null) - { - // if the lambda is not found on the same level, then add it - if (foundInLambdas == closure.NestedLambdas) - { - ++foundLambdaInfo.UsageCountOrVarIndex; - } - else - { - closure.AddNestedLambda(foundLambdaInfo); - var foundLambdaNonPassedParams = foundLambdaInfo.ClosureInfo.NonPassedParameters; - if (foundLambdaNonPassedParams.Length != 0) - PropagateNonPassedParamsToOuterLambda(ref closure, paramExprs, nestedLambdaExpr.Parameters, foundLambdaNonPassedParams); - } - - return true; - } - } - - var nestedLambdaInfo = new NestedLambdaInfo(nestedLambdaExpr); - if (!TryCollectBoundConstants(ref nestedLambdaInfo.ClosureInfo, - nestedLambdaExpr.Body, nestedLambdaExpr.Parameters, true, ref rootClosure)) - return false; - - closure.AddNestedLambda(nestedLambdaInfo); - var nestedNonPassedParams = nestedLambdaInfo.ClosureInfo.NonPassedParameters; - if (nestedNonPassedParams.Length != 0) - PropagateNonPassedParamsToOuterLambda(ref closure, paramExprs, nestedLambdaExpr.Parameters, nestedNonPassedParams); - - return true; - - case ExpressionType.Invoke: - var invokeExpr = (InvocationExpression)expr; - var invokeArgs = invokeExpr.Arguments; - var invokeArgsCount = invokeArgs.Count; - if (invokeArgsCount == 0) - { - // optimization #138: we inline the invoked lambda body (only for lambdas without arguments) - // therefore we skipping collecting the lambda and invocation arguments and got directly to lambda body. - // This approach is repeated in `TryEmitInvoke` - expr = (invokeExpr.Expression as LambdaExpression)?.Body ?? invokeExpr.Expression; - continue; - } - else if (!TryCollectBoundConstants(ref closure, invokeExpr.Expression, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - for (var i = 0; i < invokeArgs.Count - 1; i++) - if (!TryCollectBoundConstants(ref closure, invokeArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - - expr = invokeArgs[invokeArgsCount - 1]; - continue; - - case ExpressionType.Conditional: - var condExpr = (ConditionalExpression)expr; - if (!TryCollectBoundConstants(ref closure, condExpr.Test, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, condExpr.IfFalse, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = condExpr.IfTrue; - continue; - - case ExpressionType.Block: - - if (expr is OneVariableTwoExpressionBlockExpression simpleBlock) - { - closure.PushBlockWithVars(simpleBlock.Variable); - if (!TryCollectBoundConstants(ref closure, simpleBlock.Expression1, paramExprs, isNestedLambda, ref rootClosure) || - !TryCollectBoundConstants(ref closure, simpleBlock.Expression2, paramExprs, isNestedLambda, ref rootClosure)) - return false; - closure.PopBlock(); - return true; - } - - var blockExpr = (BlockExpression)expr; - var blockVarExprs = blockExpr.Variables; - var blockExprs = blockExpr.Expressions; - - if (blockVarExprs.Count == 0) - { - for (var i = 0; i < blockExprs.Count - 1; i++) - if (!TryCollectBoundConstants(ref closure, blockExprs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = blockExprs[blockExprs.Count - 1]; - continue; - } - else - { - if (blockVarExprs.Count == 1) - closure.PushBlockWithVars(blockVarExprs[0]); - else - closure.PushBlockWithVars(blockVarExprs); - - for (var i = 0; i < blockExprs.Count; i++) - if (!TryCollectBoundConstants(ref closure, blockExprs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - closure.PopBlock(); - } - - return true; - - case ExpressionType.Loop: - var loopExpr = (LoopExpression)expr; - closure.AddLabel(loopExpr.BreakLabel); - closure.AddLabel(loopExpr.ContinueLabel); - expr = loopExpr.Body; - continue; - - case ExpressionType.Index: - var indexExpr = (IndexExpression)expr; - var indexArgs = indexExpr.Arguments; - for (var i = 0; i < indexArgs.Count; i++) - if (!TryCollectBoundConstants(ref closure, indexArgs[i], paramExprs, isNestedLambda, ref rootClosure)) - return false; - if (indexExpr.Object == null) - return true; - expr = indexExpr.Object; - continue; - - case ExpressionType.Try: - return TryCollectTryExprConstants(ref closure, (TryExpression)expr, paramExprs, isNestedLambda, ref rootClosure); - - case ExpressionType.Label: - var labelExpr = (LabelExpression)expr; - var defaultValueExpr = labelExpr.DefaultValue; - closure.AddLabel(labelExpr.Target); - if (defaultValueExpr == null) - return true; - expr = defaultValueExpr; - continue; - - case ExpressionType.Goto: - var gotoExpr = (GotoExpression)expr; - if (gotoExpr.Kind == GotoExpressionKind.Return) - closure.MarkAsContainsReturnGotoExpression(); - - if (gotoExpr.Value == null) - return true; - - expr = gotoExpr.Value; - continue; - - case ExpressionType.Switch: - var switchExpr = ((SwitchExpression)expr); - if (!TryCollectBoundConstants(ref closure, switchExpr.SwitchValue, paramExprs, isNestedLambda, ref rootClosure) || - switchExpr.DefaultBody != null && - !TryCollectBoundConstants(ref closure, switchExpr.DefaultBody, paramExprs, isNestedLambda, ref rootClosure)) - return false; - var switchCases = switchExpr.Cases; - for (var i = 0; i < switchCases.Count - 1; i++) - if (!TryCollectBoundConstants(ref closure, switchCases[i].Body, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = switchCases[switchCases.Count - 1].Body; - continue; - - case ExpressionType.Extension: - expr = expr.Reduce(); - continue; - - case ExpressionType.Default: - return true; - - default: - if (expr is UnaryExpression unaryExpr) - { - expr = unaryExpr.Operand; - continue; - } - - if (expr is BinaryExpression binaryExpr) - { - if (!TryCollectBoundConstants(ref closure, binaryExpr.Left, paramExprs, isNestedLambda, ref rootClosure)) - return false; - expr = binaryExpr.Right; - continue; - } - - if (expr is TypeBinaryExpression typeBinaryExpr) - { - expr = typeBinaryExpr.Expression; - continue; - } - - return false; - } - } - } - - private static void PropagateNonPassedParamsToOuterLambda( - ref ClosureInfo closure, IReadOnlyList paramExprs, - IReadOnlyList nestedLambdaParamExprs, ParameterExpression[] nestedNonPassedParams) - { - // If nested non passed parameter is not matched with any outer passed parameter, - // then ensure it goes to outer non passed parameter. - // But check that having a non-passed parameter in root expression is invalid. - for (var i = 0; i < nestedNonPassedParams.Length; i++) - { - var nestedNonPassedParam = nestedNonPassedParams[i]; - - var isInNestedLambda = false; - if (nestedLambdaParamExprs.Count != 0) - for (var p = 0; !isInNestedLambda && p < nestedLambdaParamExprs.Count; ++p) - isInNestedLambda = ReferenceEquals(nestedLambdaParamExprs[p], nestedNonPassedParam); - - var isInOuterLambda = false; - if (paramExprs.Count != 0) - for (var p = 0; !isInOuterLambda && p < paramExprs.Count; ++p) - isInOuterLambda = ReferenceEquals(paramExprs[p], nestedNonPassedParam); - - if (!isInNestedLambda && !isInOuterLambda) - closure.AddNonPassedParam(nestedNonPassedParam); - } - } - - private static NestedLambdaInfo FindAlreadyCollectedNestedLambdaInfo( - NestedLambdaInfo[] nestedLambdas, LambdaExpression nestedLambdaExpr, out NestedLambdaInfo[] foundInLambdas) - { - for (var i = 0; i < nestedLambdas.Length; i++) - { - var lambdaInfo = nestedLambdas[i]; - if (ReferenceEquals(lambdaInfo.LambdaExpression, nestedLambdaExpr)) - { - foundInLambdas = nestedLambdas; - return lambdaInfo; - } - - var deeperNestedLambdas = lambdaInfo.ClosureInfo.NestedLambdas; - if (deeperNestedLambdas.Length != 0) - { - var deeperLambdaInfo = FindAlreadyCollectedNestedLambdaInfo(deeperNestedLambdas, nestedLambdaExpr, out foundInLambdas); - if (deeperLambdaInfo != null) - return deeperLambdaInfo; - } - } - - foundInLambdas = null; - return null; - } - - private static bool TryCompileNestedLambda(ref ClosureInfo outerClosureInfo, int nestedLambdaIndex) - { - // 1. Try to compile nested lambda in place - // 2. Check that parameters used in compiled lambda are passed or closed by outer lambda - // 3. Add the compiled lambda to closure of outer lambda for later invocation - var nestedLambdaInfo = outerClosureInfo.NestedLambdas[nestedLambdaIndex]; - if (nestedLambdaInfo.Lambda != null) - return true; - - var nestedLambdaExpr = nestedLambdaInfo.LambdaExpression; - ref var nestedLambdaClosureInfo = ref nestedLambdaInfo.ClosureInfo; - - var nestedLambdaParamExprs = nestedLambdaExpr.Parameters; - var nestedLambdaNestedLambdas = nestedLambdaClosureInfo.NestedLambdas; - if (nestedLambdaNestedLambdas.Length != 0) - for (var i = 0; i < nestedLambdaNestedLambdas.Length; ++i) - if (!TryCompileNestedLambda(ref nestedLambdaClosureInfo, i)) - return false; - - ArrayClosure nestedLambdaClosure = null; - if (nestedLambdaClosureInfo.NonPassedParameters.Length == 0) - { - if ((nestedLambdaClosureInfo.Status & ClosureStatus.HasClosure) == 0) - nestedLambdaClosure = EmptyArrayClosure; - else - nestedLambdaClosure = new ArrayClosure(nestedLambdaClosureInfo.GetArrayOfConstantsAndNestedLambdas()); - } - - var nestedReturnType = nestedLambdaExpr.ReturnType; - var closurePlusParamTypes = GetClosureTypeToParamTypes(nestedLambdaParamExprs); - - var method = new DynamicMethod(string.Empty, - nestedReturnType, closurePlusParamTypes, typeof(ArrayClosure), true); - - var il = method.GetILGenerator(); - - if ((nestedLambdaClosureInfo.Status & ClosureStatus.HasClosure) != 0) - EmittingVisitor.EmitLoadConstantsAndNestedLambdasIntoVars(il, ref nestedLambdaClosureInfo); - - var parentFlags = nestedReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.Empty; - if (!EmittingVisitor.TryEmit(nestedLambdaExpr.Body, nestedLambdaParamExprs, il, ref nestedLambdaClosureInfo, parentFlags)) - return false; - il.Emit(OpCodes.Ret); - - if (nestedLambdaClosure != null) - { - nestedLambdaInfo.Lambda = method.CreateDelegate(nestedLambdaExpr.Type, nestedLambdaClosure); - } - else - { - // Otherwise create a static or an open delegate to pass closure later with `TryEmitNestedLambda`, - // constructing the new closure with non-passed arguments and the rest of items - nestedLambdaInfo.Lambda = method.CreateDelegate( - Tools.GetFuncOrActionType(closurePlusParamTypes, nestedReturnType), - null); - } - - ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes); - return true; - } - - private static bool TryCollectMemberInitExprConstants(ref ClosureInfo closure, MemberInitExpression expr, - IReadOnlyList paramExprs, bool isNestedLambda, ref ClosureInfo rootClosure) - { - var newExpr = expr.NewExpression - ?? expr.Expression - ; - if (!TryCollectBoundConstants(ref closure, newExpr, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - var memberBindings = expr.Bindings; - for (var i = 0; i < memberBindings.Count; ++i) - { - var memberBinding = memberBindings[i]; - if (memberBinding.BindingType == MemberBindingType.Assignment && - !TryCollectBoundConstants( - ref closure, ((MemberAssignment)memberBinding).Expression, paramExprs, isNestedLambda, ref rootClosure)) - return false; - } - - return true; - } - - private static bool TryCollectTryExprConstants(ref ClosureInfo closure, TryExpression tryExpr, - IReadOnlyList paramExprs, bool isNestedLambda, ref ClosureInfo rootClosure) - { - closure.AddTryCatchFinallyInfo(); - - if (!TryCollectBoundConstants(ref closure, tryExpr.Body, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - var catchBlocks = tryExpr.Handlers; - for (var i = 0; i < catchBlocks.Count; i++) - { - var catchBlock = catchBlocks[i]; - var catchExVar = catchBlock.Variable; - if (catchExVar != null) - { - closure.PushBlockWithVars(catchExVar); - if (!TryCollectBoundConstants(ref closure, catchExVar, paramExprs, isNestedLambda, ref rootClosure)) - return false; - } - - if (catchBlock.Filter != null && - !TryCollectBoundConstants(ref closure, catchBlock.Filter, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - if (!TryCollectBoundConstants(ref closure, catchBlock.Body, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - if (catchExVar != null) - closure.PopBlock(); - } - - if (tryExpr.Finally != null && - !TryCollectBoundConstants(ref closure, tryExpr.Finally, paramExprs, isNestedLambda, ref rootClosure)) - return false; - - --closure.CurrentTryCatchFinallyIndex; - return true; - } - -#endregion - - // The minimal context-aware flags set by parent - [Flags] - internal enum ParentFlags - { - Empty = 0, - IgnoreResult = 1 << 1, - Call = 1 << 2, - MemberAccess = 1 << 3, // Any Parent Expression is a MemberExpression - Arithmetic = 1 << 4, - Coalesce = 1 << 5, - InstanceAccess = 1 << 6, - DupMemberOwner = 1 << 7, - TryCatch = 1 << 8, - InstanceCall = Call | InstanceAccess - } - - internal static bool IgnoresResult(this ParentFlags parent) => (parent & ParentFlags.IgnoreResult) != 0; - - /// Supports emitting of selected expressions, e.g. lambdaExpr are not supported yet. - /// When emitter find not supported expression it will return false from , so I could fallback - /// to normal and slow Expression.Compile. - private static class EmittingVisitor - { -#if NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 - private static readonly MethodInfo _getTypeFromHandleMethod = - typeof(Type).GetTypeInfo().GetDeclaredMethod("GetTypeFromHandle"); - - private static readonly MethodInfo _objectEqualsMethod = GetObjectEquals(); - private static MethodInfo GetObjectEquals() - { - var ms = typeof(object).GetTypeInfo().GetDeclaredMethods("Equals"); - foreach (var m in ms) - if (m.GetParameters().Length == 2) - return m; - throw new InvalidOperationException("object.Equals is not found"); - } -#else - private static readonly MethodInfo _getTypeFromHandleMethod = - ((Func)Type.GetTypeFromHandle).Method; - - private static readonly MethodInfo _objectEqualsMethod = - ((Func)object.Equals).Method; -#endif - - public static bool TryEmit(Expression expr, IReadOnlyList paramExprs, - ILGenerator il, ref ClosureInfo closure, ParentFlags parent, int byRefIndex = -1) - { - while (true) - { - closure.LastEmitIsAddress = false; - - switch (expr.NodeType) - { - case ExpressionType.Parameter: - return (parent & ParentFlags.IgnoreResult) != 0 || - TryEmitParameter((ParameterExpression)expr, paramExprs, il, ref closure, parent, byRefIndex); - - case ExpressionType.TypeAs: - case ExpressionType.IsTrue: - case ExpressionType.IsFalse: - case ExpressionType.Increment: - case ExpressionType.Decrement: - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - case ExpressionType.OnesComplement: - case ExpressionType.UnaryPlus: - case ExpressionType.Unbox: - return TryEmitSimpleUnaryExpression((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Quote: - //return TryEmitNotNullConstant(true, expr.Type, ((UnaryExpression)expr).Operand, il, ref closure); - return false; - - case ExpressionType.TypeIs: - return TryEmitTypeIs((TypeBinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Not: - return TryEmitNot((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Convert: - case ExpressionType.ConvertChecked: - return TryEmitConvert((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.ArrayIndex: - var arrIndexExpr = (BinaryExpression)expr; - return TryEmit(arrIndexExpr.Left, paramExprs, il, ref closure, parent) && - TryEmit(arrIndexExpr.Right, paramExprs, il, ref closure, parent) && - TryEmitArrayIndex(expr.Type, il); - - case ExpressionType.ArrayLength: - var arrLengthExpr = (UnaryExpression)expr; - return TryEmitArrayLength(arrLengthExpr, paramExprs, il, ref closure, parent); - - case ExpressionType.Constant: - var constExpr = (ConstantExpression)expr; - if ((parent & ParentFlags.IgnoreResult) != 0) - return true; - - if (constExpr.Value == null) - { - il.Emit(OpCodes.Ldnull); - return true; - } - - return TryEmitNotNullConstant(true, constExpr.Type, constExpr.Value, il, ref closure); - - case ExpressionType.Call: - return TryEmitMethodCall(expr, paramExprs, il, ref closure, parent); - - case ExpressionType.MemberAccess: - return TryEmitMemberAccess((MemberExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.New: - return TryEmitNew(expr, paramExprs, il, ref closure, parent); - - case ExpressionType.NewArrayBounds: - case ExpressionType.NewArrayInit: - return EmitNewArray((NewArrayExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.MemberInit: - return EmitMemberInit((MemberInitExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Lambda: - return TryEmitNestedLambda((LambdaExpression)expr, paramExprs, il, ref closure); - - case ExpressionType.Invoke: - // optimization #138: we inline the invoked lambda body (only for lambdas without arguments) - if (((InvocationExpression)expr).Expression is LambdaExpression lambdaExpr && lambdaExpr.Parameters.Count == 0) - { - expr = lambdaExpr.Body; - continue; - } - - return TryEmitInvoke((InvocationExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.Equal: - case ExpressionType.NotEqual: - var binaryExpr = (BinaryExpression)expr; - return TryEmitComparison(binaryExpr.Left, binaryExpr.Right, binaryExpr.NodeType, - paramExprs, il, ref closure, parent); - - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.Divide: - case ExpressionType.Modulo: - case ExpressionType.Power: - case ExpressionType.And: - case ExpressionType.Or: - case ExpressionType.ExclusiveOr: - case ExpressionType.LeftShift: - case ExpressionType.RightShift: - var arithmeticExpr = (BinaryExpression)expr; - return TryEmitArithmetic(arithmeticExpr, expr.NodeType, paramExprs, il, ref closure, parent); - - case ExpressionType.AndAlso: - case ExpressionType.OrElse: - return TryEmitLogicalOperator((BinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Coalesce: - return TryEmitCoalesceOperator((BinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Conditional: - return TryEmitConditional((ConditionalExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.PostIncrementAssign: - case ExpressionType.PreIncrementAssign: - case ExpressionType.PostDecrementAssign: - case ExpressionType.PreDecrementAssign: - return TryEmitIncDecAssign((UnaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.AddAssign: - case ExpressionType.AddAssignChecked: - case ExpressionType.SubtractAssign: - case ExpressionType.SubtractAssignChecked: - case ExpressionType.MultiplyAssign: - case ExpressionType.MultiplyAssignChecked: - case ExpressionType.DivideAssign: - case ExpressionType.ModuloAssign: - case ExpressionType.PowerAssign: - case ExpressionType.AndAssign: - case ExpressionType.OrAssign: - case ExpressionType.ExclusiveOrAssign: - case ExpressionType.LeftShiftAssign: - case ExpressionType.RightShiftAssign: - case ExpressionType.Assign: - return TryEmitAssign((BinaryExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Block: - if (expr is OneVariableTwoExpressionBlockExpression simpleBlockExpr) - { - closure.PushBlockWithVars(simpleBlockExpr.Variable, il.GetNextLocalVarIndex(simpleBlockExpr.Variable.Type)); - if (!TryEmit(simpleBlockExpr.Expression1, paramExprs, il, ref closure, parent | ParentFlags.IgnoreResult) || - !TryEmit(simpleBlockExpr.Expression2, paramExprs, il, ref closure, parent)) - return false; - closure.PopBlock(); - return true; - } - - var blockExpr = (BlockExpression)expr; - var blockVarExprs = blockExpr.Variables; - var blockVarCount = blockVarExprs.Count; - if (blockVarCount == 1) - closure.PushBlockWithVars(blockVarExprs[0], il.GetNextLocalVarIndex(blockVarExprs[0].Type)); - else if (blockVarCount > 1) - closure.PushBlockAndConstructLocalVars(blockVarExprs, il); - - var statementExprs = blockExpr.Expressions; // Trim the expressions after the Throw - #196 - var statementCount = statementExprs.Count; - expr = statementExprs[statementCount - 1]; // The last (result) statement in block will provide the result - - // Try to trim the statements up to the Throw (if any) - if (statementCount > 1) - { - var throwIndex = statementCount - 1; - while (throwIndex != -1 && statementExprs[throwIndex].NodeType != ExpressionType.Throw) - --throwIndex; - - // If we have a Throw and it is not the last one - if (throwIndex != -1 && throwIndex != statementCount - 1) - { - // Change the Throw return type to match the one for the Block, and adjust the statement count - expr = Expression.Throw(((UnaryExpression)statementExprs[throwIndex]).Operand, blockExpr.Type); - statementCount = throwIndex + 1; - } - } - - // handle the all statements in block excluding the last one - if (statementCount > 1) - for (var i = 0; i < statementCount - 1; i++) - if (!TryEmit(statementExprs[i], paramExprs, il, ref closure, parent | ParentFlags.IgnoreResult)) - return false; - - if (blockVarCount == 0) - continue; // OMG, no recursion, continue with last expression - - if (!TryEmit(expr, paramExprs, il, ref closure, parent)) - return false; - - closure.PopBlock(); - return true; - - case ExpressionType.Loop: - return TryEmitLoop((LoopExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Try: - return TryEmitTryCatchFinallyBlock((TryExpression)expr, paramExprs, il, ref closure, - parent | ParentFlags.TryCatch); - - case ExpressionType.Throw: - { - if (!TryEmit(((UnaryExpression)expr).Operand, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - il.Emit(OpCodes.Throw); - return true; - } - - case ExpressionType.Default: - if (expr.Type != typeof(void) && (parent & ParentFlags.IgnoreResult) == 0) - EmitDefault(expr.Type, il); - return true; - - case ExpressionType.Index: - return TryEmitIndex((IndexExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Goto: - return TryEmitGoto((GotoExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Label: - return TryEmitLabel((LabelExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Switch: - return TryEmitSwitch((SwitchExpression)expr, paramExprs, il, ref closure, parent); - - case ExpressionType.Extension: - expr = expr.Reduce(); - continue; - - default: - return false; - } - } - } - - private static bool TryEmitNew(Expression expr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - var newExpr = (NewExpression)expr; - - var argCount = newExpr.FewArgumentCount; - if (argCount > 0) - { - var args = newExpr.Constructor.GetParameters(); - if (argCount == 1) - { - var argExpr = ((OneArgumentNewExpression)newExpr).Argument; - if (!TryEmit(argExpr, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1)) - return false; - } - else if (argCount == 2) - { - var twoArgsExpr = (TwoArgumentsNewExpression)newExpr; - if (!TryEmit(twoArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(twoArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1)) - return false; - } - else if (argCount == 3) - { - var threeArgsExpr = (ThreeArgumentsNewExpression)newExpr; - if (!TryEmit(threeArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(threeArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(threeArgsExpr.Argument2, paramExprs, il, ref closure, parent, args[2].ParameterType.IsByRef ? 2 : -1)) - return false; - } - else if (argCount == 4) - { - var fourArgsExpr = (FourArgumentsNewExpression)newExpr; - if (!TryEmit(fourArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fourArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fourArgsExpr.Argument2, paramExprs, il, ref closure, parent, args[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fourArgsExpr.Argument3, paramExprs, il, ref closure, parent, args[3].ParameterType.IsByRef ? 3 : -1)) - return false; - } - else if (argCount == 5) - { - var fourArgsExpr = (FiveArgumentsNewExpression)newExpr; - if (!TryEmit(fourArgsExpr.Argument0, paramExprs, il, ref closure, parent, args[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fourArgsExpr.Argument1, paramExprs, il, ref closure, parent, args[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fourArgsExpr.Argument2, paramExprs, il, ref closure, parent, args[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fourArgsExpr.Argument3, paramExprs, il, ref closure, parent, args[3].ParameterType.IsByRef ? 3 : -1) || - !TryEmit(fourArgsExpr.Argument4, paramExprs, il, ref closure, parent, args[4].ParameterType.IsByRef ? 4 : -1)) - return false; - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (newExpr.Constructor != null) - il.Emit(OpCodes.Newobj, newExpr.Constructor); - else if (newExpr.Type.IsValueType()) - EmitLoadLocalVariable(il, InitValueTypeVariable(il, newExpr.Type)); - else - return false; - return true; - } - - var argExprs = newExpr.Arguments; - if (argExprs.Count != 0) - { - var args = newExpr.Constructor.GetParameters(); - for (var i = 0; i < args.Length; ++i) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent, args[i].ParameterType.IsByRef ? i : -1)) - return false; - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (newExpr.Constructor != null) - il.Emit(OpCodes.Newobj, newExpr.Constructor); - else if (newExpr.Type.IsValueType()) - EmitLoadLocalVariable(il, InitValueTypeVariable(il, newExpr.Type)); - else - return false; - return true; - } - - private static bool TryEmitArrayLength(UnaryExpression arrLengthExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - if (!TryEmit(arrLengthExpr.Operand, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) == 0) - { - il.Emit(OpCodes.Ldlen); - il.Emit(OpCodes.Conv_I4); - } - - return true; - } - - private static bool TryEmitLoop(LoopExpression loopExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - // Mark the start of the loop body: - var loopBodyLabel = il.DefineLabel(); - il.MarkLabel(loopBodyLabel); - - if (loopExpr.ContinueLabel != null) - il.MarkLabel(closure.GetOrCreateLabel(loopExpr.ContinueLabel, il)); - - if (!TryEmit(loopExpr.Body, paramExprs, il, ref closure, parent)) - return false; - - // If loop hasn't exited, jump back to start of its body: - il.Emit(OpCodes.Br, loopBodyLabel); - - if (loopExpr.BreakLabel != null) - il.MarkLabel(closure.GetOrCreateLabel(loopExpr.BreakLabel, il)); - - return true; - } - - private static bool TryEmitIndex(IndexExpression indexExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - if (indexExpr.Object != null && - !TryEmit(indexExpr.Object, paramExprs, il, ref closure, parent)) - return false; - - var indexArgExprs = indexExpr.Arguments; - for (var i = 0; i < indexArgExprs.Count; i++) - if (!TryEmit(indexArgExprs[i], paramExprs, il, ref closure, parent, - indexArgExprs[i].Type.IsByRef ? i : -1)) - return false; - - var indexerProp = indexExpr.Indexer; - if (indexerProp != null) - return EmitMethodCall(il, indexerProp.DeclaringType.FindPropertyGetMethod(indexerProp.Name)); - - if (indexExpr.Arguments.Count == 1) // one dimensional array - return TryEmitArrayIndex(indexExpr.Type, il); - - // multi dimensional array - return EmitMethodCall(il, indexExpr.Object?.Type.FindMethod("Get")); - } - - private static bool TryEmitLabel(LabelExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var index = closure.GetLabelIndex(expr.Target); - if (index == -1) - return false; // should be found in first collecting constants round - - if (closure.IsTryReturnLabel(index)) - return true; // label will be emitted by TryEmitTryCatchFinallyBlock - - // define a new label or use the label provided by the preceding GoTo expression - var label = closure.GetOrCreateLabel(index, il); - - il.MarkLabel(label); - - return expr.DefaultValue == null || TryEmit(expr.DefaultValue, paramExprs, il, ref closure, parent); - } - - private static bool TryEmitGoto(GotoExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var index = closure.GetLabelIndex(expr.Target); - if (index == -1) - { - if ((closure.Status & ClosureStatus.ToBeCollected) == 0) - return false; // if no collection cycle then the labels may be not collected - throw new InvalidOperationException("Cannot jump, no labels found"); - } - - if (expr.Value != null && - !TryEmit(expr.Value, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - - switch (expr.Kind) - { - case GotoExpressionKind.Break: - case GotoExpressionKind.Continue: - // use label defined by Label expression or define its own to use by subsequent Label - il.Emit(OpCodes.Br, closure.GetOrCreateLabel(index, il)); - return true; - - case GotoExpressionKind.Goto: - if (expr.Value != null) - goto case GotoExpressionKind.Return; - - // use label defined by Label expression or define its own to use by subsequent Label - il.Emit(OpCodes.Br, closure.GetOrCreateLabel(index, il)); - return true; - - case GotoExpressionKind.Return: - - // check that we are inside the Try-Catch-Finally block - if ((parent & ParentFlags.TryCatch) != 0) - { - // Can't emit a Return inside a Try/Catch, so leave it to TryEmitTryCatchFinallyBlock - // to emit the Leave instruction, return label and return result - closure.MarkReturnLabelIndex(index); - } - else - { - // use label defined by Label expression or define its own to use by subsequent Label - il.Emit(OpCodes.Ret, closure.GetOrCreateLabel(index, il)); - } - - return true; - - default: - return false; - } - } - - private static bool TryEmitCoalesceOperator(BinaryExpression exprObj, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var labelFalse = il.DefineLabel(); - var labelDone = il.DefineLabel(); - - var left = exprObj.Left; - var right = exprObj.Right; - - if (!TryEmit(left, paramExprs, il, ref closure, parent | ParentFlags.Coalesce)) - return false; - - var leftType = left.Type; - if (leftType.IsValueType()) // Nullable -> It's the only ValueType comparable to null - { - var varIndex = EmitStoreLocalVariableAndLoadItsAddress(il, leftType); - il.Emit(OpCodes.Call, leftType.FindNullableHasValueGetterMethod()); - - il.Emit(OpCodes.Brfalse, labelFalse); - EmitLoadLocalVariableAddress(il, varIndex); - il.Emit(OpCodes.Call, leftType.FindNullableGetValueOrDefaultMethod()); - - il.Emit(OpCodes.Br, labelDone); - il.MarkLabel(labelFalse); - if (!TryEmit(right, paramExprs, il, ref closure, parent | ParentFlags.Coalesce)) - return false; - - il.MarkLabel(labelDone); - return true; - } - - il.Emit(OpCodes.Dup); // duplicate left, if it's not null, after the branch this value will be on the top of the stack - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Brfalse, labelFalse); - - il.Emit(OpCodes.Pop); // left is null, pop its value from the stack - - if (!TryEmit(right, paramExprs, il, ref closure, parent | ParentFlags.Coalesce)) - return false; - - if (right.Type != exprObj.Type) - { - if (right.Type.IsValueType()) - il.Emit(OpCodes.Box, right.Type); - } - - if (left.Type == exprObj.Type) - il.MarkLabel(labelFalse); - else - { - il.Emit(OpCodes.Br, labelDone); - il.MarkLabel(labelFalse); - il.Emit(OpCodes.Castclass, exprObj.Type); - il.MarkLabel(labelDone); - } - - return true; - } - - private static void EmitDefault(Type type, ILGenerator il) - { - if (!type.GetTypeInfo().IsValueType) - { - il.Emit(OpCodes.Ldnull); - } - else if ( - type == typeof(bool) || - type == typeof(byte) || - type == typeof(char) || - type == typeof(sbyte) || - type == typeof(int) || - type == typeof(uint) || - type == typeof(short) || - type == typeof(ushort)) - { - il.Emit(OpCodes.Ldc_I4_0); - } - else if ( - type == typeof(long) || - type == typeof(ulong)) - { - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Conv_I8); - } - else if (type == typeof(float)) - il.Emit(OpCodes.Ldc_R4, default(float)); - else if (type == typeof(double)) - il.Emit(OpCodes.Ldc_R8, default(double)); - else - EmitLoadLocalVariable(il, InitValueTypeVariable(il, type)); - } - - private static bool TryEmitTryCatchFinallyBlock(TryExpression tryExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var containsReturnGotoExpression = closure.TryCatchFinallyContainsReturnGotoExpression(); - il.BeginExceptionBlock(); - - if (!TryEmit(tryExpr.Body, paramExprs, il, ref closure, parent)) - return false; - - var exprType = tryExpr.Type; - var returnsResult = exprType != typeof(void) && (containsReturnGotoExpression || !parent.IgnoresResult()); - var resultVarIndex = -1; - - if (returnsResult) - EmitStoreLocalVariable(il, resultVarIndex = il.GetNextLocalVarIndex(exprType)); - - var catchBlocks = tryExpr.Handlers; - for (var i = 0; i < catchBlocks.Count; i++) - { - var catchBlock = catchBlocks[i]; - if (catchBlock.Filter != null) - return false; // todo: Add support for filters in catch expression - - il.BeginCatchBlock(catchBlock.Test); - - // at the beginning of catch the Exception value is on the stack, - // we will store into local variable. - var exVarExpr = catchBlock.Variable; - if (exVarExpr != null) - { - var exVarIndex = il.GetNextLocalVarIndex(exVarExpr.Type); - closure.PushBlockWithVars(exVarExpr, exVarIndex); - EmitStoreLocalVariable(il, exVarIndex); - } - - if (!TryEmit(catchBlock.Body, paramExprs, il, ref closure, parent)) - return false; - - if (exVarExpr != null) - closure.PopBlock(); - - if (returnsResult) - EmitStoreLocalVariable(il, resultVarIndex); - } - - var finallyExpr = tryExpr.Finally; - if (finallyExpr != null) - { - il.BeginFinallyBlock(); - if (!TryEmit(finallyExpr, paramExprs, il, ref closure, parent)) - return false; - } - - il.EndExceptionBlock(); - - if (returnsResult) - EmitLoadLocalVariable(il, resultVarIndex); - - --closure.CurrentTryCatchFinallyIndex; - return true; - } - - private static bool TryEmitParameter(ParameterExpression paramExpr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent, int byRefIndex = -1) - { - // if parameter is passed through, then just load it on stack - var paramType = paramExpr.Type; - - var paramIndex = paramExprs.Count - 1; - while (paramIndex != -1 && !ReferenceEquals(paramExprs[paramIndex], paramExpr)) - --paramIndex; - if (paramIndex != -1) - { - if ((closure.Status & ClosureStatus.ShouldBeStaticMethod) == 0) - ++paramIndex; // shift parameter index by one, because the first one will be closure - - closure.LastEmitIsAddress = !paramExpr.IsByRef && paramType.IsValueType() && - ((parent & ParentFlags.InstanceCall) == ParentFlags.InstanceCall || - (parent & ParentFlags.MemberAccess) != 0); - - if (closure.LastEmitIsAddress) - il.Emit(OpCodes.Ldarga_S, (byte)paramIndex); - else - { - if (paramIndex == 0) - il.Emit(OpCodes.Ldarg_0); - else if (paramIndex == 1) - il.Emit(OpCodes.Ldarg_1); - else if (paramIndex == 2) - il.Emit(OpCodes.Ldarg_2); - else if (paramIndex == 3) - il.Emit(OpCodes.Ldarg_3); - else - il.Emit(OpCodes.Ldarg_S, (byte)paramIndex); - } - - if (paramExpr.IsByRef) - { - if ((parent & ParentFlags.MemberAccess) != 0 && paramType.IsClass() || - (parent & ParentFlags.Coalesce) != 0) - il.Emit(OpCodes.Ldind_Ref); - else if ((parent & ParentFlags.Arithmetic) != 0) - EmitDereference(il, paramType); - } - - return true; - } - - // If parameter isn't passed, then it is passed into some outer lambda or it is a local variable, - // so it should be loaded from closure or from the locals. Then the closure is null will be an invalid state. - // Parameter may represent a variable, so first look if this is the case - var varIndex = closure.GetDefinedLocalVarOrDefault(paramExpr); - if (varIndex != -1) - { - if (byRefIndex != -1 || - paramType.IsValueType() && (parent & (ParentFlags.MemberAccess | ParentFlags.InstanceAccess)) != 0) - EmitLoadLocalVariableAddress(il, varIndex); - else - EmitLoadLocalVariable(il, varIndex); - return true; - } - - if (paramExpr.IsByRef) - { - EmitLoadLocalVariableAddress(il, byRefIndex); - return true; - } - - // the only possibility that we are here is because we are in the nested lambda, - // and it uses the parameter or variable from the outer lambda - var nonPassedParams = closure.NonPassedParameters; - var nonPassedParamIndex = nonPassedParams.Length - 1; - while (nonPassedParamIndex != -1 && !ReferenceEquals(nonPassedParams[nonPassedParamIndex], paramExpr)) - --nonPassedParamIndex; - if (nonPassedParamIndex == -1) - return false; // what??? no chance - - // Load non-passed argument from Closure - closure object is always a first argument - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, nonPassedParamIndex); - il.Emit(OpCodes.Ldelem_Ref); - - // source type is object, NonPassedParams is object array - if (paramType.IsValueType()) - il.Emit(OpCodes.Unbox_Any, paramType); - - return true; - } - - private static void EmitDereference(ILGenerator il, Type type) - { - if (type == typeof(Int32)) - il.Emit(OpCodes.Ldind_I4); - else if (type == typeof(Int64)) - il.Emit(OpCodes.Ldind_I8); - else if (type == typeof(Int16)) - il.Emit(OpCodes.Ldind_I2); - else if (type == typeof(SByte)) - il.Emit(OpCodes.Ldind_I1); - else if (type == typeof(Single)) - il.Emit(OpCodes.Ldind_R4); - else if (type == typeof(Double)) - il.Emit(OpCodes.Ldind_R8); - else if (type == typeof(IntPtr)) - il.Emit(OpCodes.Ldind_I); - else if (type == typeof(UIntPtr)) - il.Emit(OpCodes.Ldind_I); - else if (type == typeof(Byte)) - il.Emit(OpCodes.Ldind_U1); - else if (type == typeof(UInt16)) - il.Emit(OpCodes.Ldind_U2); - else if (type == typeof(UInt32)) - il.Emit(OpCodes.Ldind_U4); - else - il.Emit(OpCodes.Ldobj, type); - //todo: UInt64 as there is no OpCodes? Ldind_Ref? - } - - private static bool TryEmitSimpleUnaryExpression(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - var exprType = expr.Type; - - // todo: support decimal here - if (exprType == typeof(decimal)) - return false; - - if (!TryEmit(expr.Operand, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - else - { - if (expr.NodeType == ExpressionType.TypeAs) - { - il.Emit(OpCodes.Isinst, exprType); - } - else if (expr.NodeType == ExpressionType.IsFalse) - { - var falseLabel = il.DefineLabel(); - var continueLabel = il.DefineLabel(); - il.Emit(OpCodes.Brfalse, falseLabel); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Br, continueLabel); - il.MarkLabel(falseLabel); - il.Emit(OpCodes.Ldc_I4_1); - il.MarkLabel(continueLabel); - } - else if (expr.NodeType == ExpressionType.Increment) - { - if (!TryEmitNumberOne(il, exprType)) - return false; - il.Emit(OpCodes.Add); - } - else if (expr.NodeType == ExpressionType.Decrement) - { - if (!TryEmitNumberOne(il, exprType)) - return false; - il.Emit(OpCodes.Sub); - } - else if (expr.NodeType == ExpressionType.Negate || expr.NodeType == ExpressionType.NegateChecked) - { - il.Emit(OpCodes.Neg); - } - else if (expr.NodeType == ExpressionType.OnesComplement) - { - il.Emit(OpCodes.Not); - } - else if (expr.NodeType == ExpressionType.Unbox) - { - il.Emit(OpCodes.Unbox_Any, exprType); - } - else if (expr.NodeType == ExpressionType.IsTrue) - { } - else if (expr.NodeType == ExpressionType.UnaryPlus) - { } - } - return true; - } - - private static bool TryEmitTypeIs(TypeBinaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - if (!TryEmit(expr.Expression, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - else - { - il.Emit(OpCodes.Isinst, expr.TypeOperand); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Cgt_Un); - } - - return true; - } - - private static bool TryEmitNot(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - if (!TryEmit(expr.Operand, paramExprs, il, ref closure, parent)) - return false; - if ((parent & ParentFlags.IgnoreResult) > 0) - il.Emit(OpCodes.Pop); - else - { - if (expr.Type == typeof(bool)) - { - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - { - il.Emit(OpCodes.Not); - } - } - return true; - } - - private static bool TryEmitConvert(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var opExpr = expr.Operand; - var method = expr.Method; - if (method != null && method.Name != "op_Implicit" && method.Name != "op_Explicit") - { - if (!TryEmit(opExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult | ParentFlags.InstanceCall, 0)) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - if ((parent & ParentFlags.IgnoreResult) != 0 && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - return true; - } - - var sourceType = opExpr.Type; - var sourceTypeIsNullable = sourceType.IsNullable(); - var underlyingNullableSourceType = Nullable.GetUnderlyingType(sourceType); - var targetType = expr.Type; - - if (sourceTypeIsNullable && targetType == underlyingNullableSourceType) - { - if (!TryEmit(opExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult | ParentFlags.InstanceAccess)) - return false; - - if (!closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - return true; - } - - if (!TryEmit(opExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) - return false; - - var targetTypeIsNullable = targetType.IsNullable(); - var underlyingNullableTargetType = Nullable.GetUnderlyingType(targetType); - if (targetTypeIsNullable && sourceType == underlyingNullableTargetType) - { - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - return true; - } - - if (sourceType == targetType || targetType == typeof(object)) - { - if (targetType == typeof(object) && sourceType.IsValueType()) - il.Emit(OpCodes.Box, sourceType); - if (IgnoresResult(parent)) - il.Emit(OpCodes.Pop); - return true; - } - - // check implicit / explicit conversion operators on source and target types - // for non-primitives and for non-primitive nullable - #73 - if (!sourceTypeIsNullable && !sourceType.IsPrimitive()) - { - var actualTargetType = targetTypeIsNullable ? underlyingNullableTargetType : targetType; - var convertOpMethod = method ?? sourceType.FindConvertOperator(sourceType, actualTargetType); - if (convertOpMethod != null) - { - il.Emit(OpCodes.Call, convertOpMethod); - - if (targetTypeIsNullable) - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - else if (!targetTypeIsNullable) - { - var actualSourceType = sourceTypeIsNullable ? underlyingNullableSourceType : sourceType; - - var convertOpMethod = method ?? actualSourceType.FindConvertOperator(actualSourceType, targetType); - if (convertOpMethod != null) - { - if (sourceTypeIsNullable) - { - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - } - - il.Emit(OpCodes.Call, convertOpMethod); - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - - if (!targetTypeIsNullable && !targetType.IsPrimitive()) - { - var actualSourceType = sourceTypeIsNullable ? underlyingNullableSourceType : sourceType; - - // ReSharper disable once ConstantNullCoalescingCondition - var convertOpMethod = method ?? targetType.FindConvertOperator(actualSourceType, targetType); - if (convertOpMethod != null) - { - if (sourceTypeIsNullable) - { - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - } - - il.Emit(OpCodes.Call, convertOpMethod); - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - else if (!sourceTypeIsNullable) - { - var actualTargetType = targetTypeIsNullable ? underlyingNullableTargetType : targetType; - var convertOpMethod = method ?? actualTargetType.FindConvertOperator(sourceType, actualTargetType); - if (convertOpMethod != null) - { - il.Emit(OpCodes.Call, convertOpMethod); - - if (targetTypeIsNullable) - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - - if (sourceType == typeof(object) && targetType.IsValueType()) - { - il.Emit(OpCodes.Unbox_Any, targetType); - } - else if (targetTypeIsNullable) - { - // Conversion to Nullable: `new Nullable(T val);` - if (!sourceTypeIsNullable) - { - if (!TryEmitValueConvert(underlyingNullableTargetType, il, isChecked: false)) - return false; - - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - } - else - { - var sourceVarIndex = EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindNullableHasValueGetterMethod()); - - var labelSourceHasValue = il.DefineLabel(); - il.Emit(OpCodes.Brtrue_S, labelSourceHasValue); // jump where source has a value - - // otherwise, emit and load a `new Nullable()` struct (that's why a Init instead of New) - EmitLoadLocalVariable(il, InitValueTypeVariable(il, targetType)); - - // jump to completion - var labelDone = il.DefineLabel(); - il.Emit(OpCodes.Br_S, labelDone); - - // if source nullable has a value: - il.MarkLabel(labelSourceHasValue); - EmitLoadLocalVariableAddress(il, sourceVarIndex); - il.Emit(OpCodes.Call, sourceType.FindNullableGetValueOrDefaultMethod()); - - if (!TryEmitValueConvert(underlyingNullableTargetType, il, - expr.NodeType == ExpressionType.ConvertChecked)) - { - var convertOpMethod = method ?? underlyingNullableTargetType.FindConvertOperator(underlyingNullableSourceType, underlyingNullableTargetType); - if (convertOpMethod == null) - return false; // nor conversion nor conversion operator is found - il.Emit(OpCodes.Call, convertOpMethod); - } - - il.Emit(OpCodes.Newobj, targetType.GetTypeInfo().DeclaredConstructors.GetFirst()); - il.MarkLabel(labelDone); - } - } - else - { - if (targetType.GetTypeInfo().IsEnum) - targetType = Enum.GetUnderlyingType(targetType); - - // fixes #159 - if (sourceTypeIsNullable) - { - EmitStoreLocalVariableAndLoadItsAddress(il, sourceType); - il.Emit(OpCodes.Call, sourceType.FindValueGetterMethod()); - } - - // cast as the last resort and let's it fail if unlucky - if (!TryEmitValueConvert(targetType, il, expr.NodeType == ExpressionType.ConvertChecked)) - il.Emit(OpCodes.Castclass, targetType); - } - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - - private static bool TryEmitValueConvert(Type targetType, ILGenerator il, bool isChecked) - { - if (targetType == typeof(int)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I4 : OpCodes.Conv_I4); - else if (targetType == typeof(float)) - il.Emit(OpCodes.Conv_R4); - else if (targetType == typeof(uint)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U4 : OpCodes.Conv_U4); - else if (targetType == typeof(sbyte)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I1 : OpCodes.Conv_I1); - else if (targetType == typeof(byte)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U1 : OpCodes.Conv_U1); - else if (targetType == typeof(short)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I2 : OpCodes.Conv_I2); - else if (targetType == typeof(ushort) || targetType == typeof(char)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U2 : OpCodes.Conv_U2); - else if (targetType == typeof(long)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_I8 : OpCodes.Conv_I8); - else if (targetType == typeof(ulong)) - il.Emit(isChecked ? OpCodes.Conv_Ovf_U8 : OpCodes.Conv_U8); - else if (targetType == typeof(double)) - il.Emit(OpCodes.Conv_R8); - else - return false; - return true; - } - - private static bool TryEmitNotNullConstant( - bool considerClosure, Type exprType, object constantValue, ILGenerator il, ref ClosureInfo closure) - { - var constValueType = constantValue.GetType(); - if (considerClosure && IsClosureBoundConstant(constantValue, constValueType.GetTypeInfo())) - { - var constItems = closure.Constants.Items; - var constIndex = closure.Constants.Count - 1; - while (constIndex != -1 && !ReferenceEquals(constItems[constIndex], constantValue)) - --constIndex; - if (constIndex == -1) - return false; - - var varIndex = closure.ConstantUsage.Items[constIndex] - 1; - if (varIndex > 0) - EmitLoadLocalVariable(il, varIndex); - else - { - il.Emit(OpCodes.Ldloc_0); // load constants array from variable - EmitLoadConstantInt(il, constIndex); - il.Emit(OpCodes.Ldelem_Ref); - if (exprType.IsValueType()) - il.Emit(OpCodes.Unbox_Any, exprType); - } - } - else - { - if (constantValue is string s) - { - il.Emit(OpCodes.Ldstr, s); - return true; - } - - if (constantValue is Type t) - { - il.Emit(OpCodes.Ldtoken, t); - il.Emit(OpCodes.Call, _getTypeFromHandleMethod); - return true; - } - - // get raw enum type to light - if (constValueType.GetTypeInfo().IsEnum) - constValueType = Enum.GetUnderlyingType(constValueType); - - if (!TryEmitNumberConstant(il, constantValue, constValueType)) - return false; - } - - var underlyingNullableType = Nullable.GetUnderlyingType(exprType); - if (underlyingNullableType != null) - il.Emit(OpCodes.Newobj, exprType.GetTypeInfo().DeclaredConstructors.GetFirst()); - - // boxing the value type, otherwise we can get a strange result when 0 is treated as Null. - else if (exprType == typeof(object) && constValueType.IsValueType()) - il.Emit(OpCodes.Box, constantValue.GetType()); // using normal type for Enum instead of underlying type - - return true; - } - - // todo: can we do something about boxing? - private static bool TryEmitNumberConstant(ILGenerator il, object constantValue, Type constValueType) - { - if (constValueType == typeof(int)) - { - EmitLoadConstantInt(il, (int)constantValue); - } - else if (constValueType == typeof(char)) - { - EmitLoadConstantInt(il, (char)constantValue); - } - else if (constValueType == typeof(short)) - { - EmitLoadConstantInt(il, (short)constantValue); - } - else if (constValueType == typeof(byte)) - { - EmitLoadConstantInt(il, (byte)constantValue); - } - else if (constValueType == typeof(ushort)) - { - EmitLoadConstantInt(il, (ushort)constantValue); - } - else if (constValueType == typeof(sbyte)) - { - EmitLoadConstantInt(il, (sbyte)constantValue); - } - else if (constValueType == typeof(uint)) - { - unchecked - { - EmitLoadConstantInt(il, (int)(uint)constantValue); - } - } - else if (constValueType == typeof(long)) - { - il.Emit(OpCodes.Ldc_I8, (long)constantValue); - } - else if (constValueType == typeof(ulong)) - { - unchecked - { - il.Emit(OpCodes.Ldc_I8, (long)(ulong)constantValue); - } - } - else if (constValueType == typeof(float)) - { - il.Emit(OpCodes.Ldc_R4, (float)constantValue); - } - else if (constValueType == typeof(double)) - { - il.Emit(OpCodes.Ldc_R8, (double)constantValue); - } - else if (constValueType == typeof(bool)) - { - il.Emit((bool)constantValue ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - } - else if (constValueType == typeof(IntPtr)) - { - il.Emit(OpCodes.Ldc_I8, ((IntPtr)constantValue).ToInt64()); - } - else if (constValueType == typeof(UIntPtr)) - { - unchecked - { - il.Emit(OpCodes.Ldc_I8, (long)((UIntPtr)constantValue).ToUInt64()); - } - } - else if (constValueType == typeof(decimal)) - { - EmitDecimalConstant((decimal)constantValue, il); - } - else - { - return false; - } - - return true; - } - - internal static bool TryEmitNumberOne(ILGenerator il, Type type) - { - if (type == typeof(int) || type == typeof(char) || type == typeof(short) || - type == typeof(byte) || type == typeof(ushort) || type == typeof(sbyte) || - type == typeof(uint)) - { - il.Emit(OpCodes.Ldc_I4_1); - } - else if (type == typeof(long) || type == typeof(ulong) || - type == typeof(IntPtr) || type == typeof(UIntPtr)) - { - il.Emit(OpCodes.Ldc_I8, (long)1); - } - else if (type == typeof(float)) - { - il.Emit(OpCodes.Ldc_R4, 1f); - } - else if (type == typeof(double)) - { - il.Emit(OpCodes.Ldc_R8, 1d); - } - else - { - return false; - } - - return true; - } - - internal static void EmitLoadConstantsAndNestedLambdasIntoVars(ILGenerator il, ref ClosureInfo closure) - { - // Load constants array field from Closure and store it into the variable - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, ArrayClosureArrayField); - EmitStoreLocalVariable(il, il.GetNextLocalVarIndex(typeof(object[]))); - - var constItems = closure.Constants.Items; - var constCount = closure.Constants.Count; - var constUsage = closure.ConstantUsage.Items; - - int varIndex; - for (var i = 0; i < constCount; i++) - { - if (constUsage[i] > 1) - { - il.Emit(OpCodes.Ldloc_0);// load array field variable on a stack - EmitLoadConstantInt(il, i); - il.Emit(OpCodes.Ldelem_Ref); - - var varType = constItems[i].GetType(); - if (varType.IsValueType()) - il.Emit(OpCodes.Unbox_Any, varType); - - varIndex = il.GetNextLocalVarIndex(varType); - constUsage[i] = varIndex + 1; // to distinguish from the default 1 - EmitStoreLocalVariable(il, varIndex); - } - } - - var nestedLambdas = closure.NestedLambdas; - for (var i = 0; i < nestedLambdas.Length; i++) - { - var nestedLambda = nestedLambdas[i]; - if (nestedLambda.UsageCountOrVarIndex > 1) - { - il.Emit(OpCodes.Ldloc_0);// load array field variable on a stack - EmitLoadConstantInt(il, constCount + i); - il.Emit(OpCodes.Ldelem_Ref); - varIndex = il.GetNextLocalVarIndex(nestedLambda.Lambda.GetType()); - nestedLambda.UsageCountOrVarIndex = varIndex + 1; - EmitStoreLocalVariable(il, varIndex); - } - } - } - - private static void EmitDecimalConstant(decimal value, ILGenerator il) - { - //check if decimal has decimal places, if not use shorter IL code (constructor from int or long) - if (value % 1 == 0) - { - if (value >= int.MinValue && value <= int.MaxValue) - { - EmitLoadConstantInt(il, decimal.ToInt32(value)); - il.Emit(OpCodes.Newobj, typeof(decimal).FindSingleParamConstructor(typeof(int))); - return; - } - - if (value >= long.MinValue && value <= long.MaxValue) - { - il.Emit(OpCodes.Ldc_I8, decimal.ToInt64(value)); - il.Emit(OpCodes.Newobj, typeof(decimal).FindSingleParamConstructor(typeof(long))); - return; - } - } - - if (value == decimal.MinValue) - { - il.Emit(OpCodes.Ldsfld, typeof(decimal).GetTypeInfo().GetDeclaredField(nameof(decimal.MinValue))); - return; - } - - if (value == decimal.MaxValue) - { - il.Emit(OpCodes.Ldsfld, typeof(decimal).GetTypeInfo().GetDeclaredField(nameof(decimal.MaxValue))); - return; - } - - var parts = decimal.GetBits(value); - var sign = (parts[3] & 0x80000000) != 0; - var scale = (byte)((parts[3] >> 16) & 0x7F); - - EmitLoadConstantInt(il, parts[0]); - EmitLoadConstantInt(il, parts[1]); - EmitLoadConstantInt(il, parts[2]); - - il.Emit(sign ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - EmitLoadConstantInt(il, scale); - - il.Emit(OpCodes.Conv_U1); - - il.Emit(OpCodes.Newobj, _decimalCtor.Value); - } - - private static readonly Lazy _decimalCtor = new Lazy(() => - { - foreach (var ctor in typeof(decimal).GetTypeInfo().DeclaredConstructors) - if (ctor.GetParameters().Length == 5) - return ctor; - return null; - }); - - private static int InitValueTypeVariable(ILGenerator il, Type exprType) - { - var locVarIndex = il.GetNextLocalVarIndex(exprType); - EmitLoadLocalVariableAddress(il, locVarIndex); - il.Emit(OpCodes.Initobj, exprType); - return locVarIndex; - } - - private static bool EmitNewArray(NewArrayExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var arrayType = expr.Type; - var elems = expr.Expressions; - var elemType = arrayType.GetElementType(); - if (elemType == null) - return false; - - var rank = arrayType.GetArrayRank(); - if (rank == 1) // one dimensional - { - EmitLoadConstantInt(il, elems.Count); - } - else // multi dimensional - { - for (var i = 0; i < elems.Count; i++) - if (!TryEmit(elems[i], paramExprs, il, ref closure, parent, i)) - return false; - - il.Emit(OpCodes.Newobj, arrayType.GetTypeInfo().DeclaredConstructors.GetFirst()); - return true; - } - - il.Emit(OpCodes.Newarr, elemType); - - var isElemOfValueType = elemType.IsValueType(); - - for (int i = 0, n = elems.Count; i < n; i++) - { - il.Emit(OpCodes.Dup); - EmitLoadConstantInt(il, i); - - // loading element address for later copying of value into it. - if (isElemOfValueType) - il.Emit(OpCodes.Ldelema, elemType); - - if (!TryEmit(elems[i], paramExprs, il, ref closure, parent)) - return false; - - if (isElemOfValueType) - il.Emit(OpCodes.Stobj, elemType); // store element of value type by array element address - else - il.Emit(OpCodes.Stelem_Ref); - } - - return true; - } - - private static bool TryEmitArrayIndex(Type exprType, ILGenerator il) - { - if (exprType.IsValueType()) - il.Emit(OpCodes.Ldelem, exprType); - else - il.Emit(OpCodes.Ldelem_Ref); - return true; - } - - private static bool EmitMemberInit(MemberInitExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var valueVarIndex = -1; - if (expr.Type.IsValueType()) - valueVarIndex = il.GetNextLocalVarIndex(expr.Type); - - var newExpr = expr.NewExpression; - if (newExpr == null) - { - if (!TryEmit(expr.Expression, paramExprs, il, ref closure, parent)) - return false; - } - else - { - var argExprs = newExpr.Arguments; - for (var i = 0; i < argExprs.Count; i++) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent, i)) - return false; - - var ctor = newExpr.Constructor; - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (ctor != null) - il.Emit(OpCodes.Newobj, ctor); - else if (newExpr.Type.IsValueType()) - { - if (valueVarIndex == -1) - valueVarIndex = il.GetNextLocalVarIndex(expr.Type); - EmitLoadLocalVariableAddress(il, valueVarIndex); - il.Emit(OpCodes.Initobj, newExpr.Type); - } - else - return false; // null constructor and not a value type, better to fallback - } - - var bindings = expr.Bindings; - for (var i = 0; i < bindings.Count; i++) - { - var binding = bindings[i]; - if (binding.BindingType != MemberBindingType.Assignment) - return false; - - if (valueVarIndex != -1) // load local value address, to set its members - EmitLoadLocalVariableAddress(il, valueVarIndex); - else - il.Emit(OpCodes.Dup); // duplicate member owner on stack - - if (!TryEmit(((MemberAssignment)binding).Expression, paramExprs, il, ref closure, parent) || - !EmitMemberAssign(il, binding.Member)) - return false; - } - - if (valueVarIndex != -1) - EmitLoadLocalVariable(il, valueVarIndex); - return true; - } - - private static bool EmitMemberAssign(ILGenerator il, MemberInfo member) - { - if (member is PropertyInfo prop) - { - var method = prop.DeclaringType.FindPropertySetMethod(prop.Name); - if (method == null) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - return true; - } - - if (member is FieldInfo field) - { - il.Emit(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field); - return true; - } - - return false; - } - - private static bool TryEmitIncDecAssign(UnaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var operandExpr = expr.Operand; - - MemberExpression memberAccess; - var useLocalVar = false; - int localVarIndex, paramIndex = -1; - - var isParameterOrVariable = operandExpr.NodeType == ExpressionType.Parameter; - var usesResult = (parent & ParentFlags.IgnoreResult) == 0; - if (isParameterOrVariable) - { - localVarIndex = closure.GetDefinedLocalVarOrDefault((ParameterExpression)operandExpr); - if (localVarIndex != -1) - { - EmitLoadLocalVariable(il, localVarIndex); - useLocalVar = true; - } - else - { - paramIndex = paramExprs.Count - 1; - while (paramIndex != -1 && !ReferenceEquals(paramExprs[paramIndex], operandExpr)) - --paramIndex; - if (paramIndex == -1) - return false; - il.Emit(OpCodes.Ldarg, paramIndex + 1); - } - - memberAccess = null; - - } - else if (operandExpr.NodeType == ExpressionType.MemberAccess) - { - memberAccess = (MemberExpression)operandExpr; - - if (!TryEmitMemberAccess(memberAccess, paramExprs, il, ref closure, parent | ParentFlags.DupMemberOwner)) - return false; - - useLocalVar = memberAccess.Expression != null && (usesResult || memberAccess.Member is PropertyInfo); - localVarIndex = useLocalVar ? il.GetNextLocalVarIndex(operandExpr.Type) : -1; - } - else - return false; - - switch (expr.NodeType) - { - case ExpressionType.PreIncrementAssign: - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Add); - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - break; - - case ExpressionType.PostIncrementAssign: - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Add); - break; - - case ExpressionType.PreDecrementAssign: - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Sub); - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - break; - - case ExpressionType.PostDecrementAssign: - StoreIncDecValue(il, usesResult, isParameterOrVariable, localVarIndex); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Sub); - break; - } - - if (isParameterOrVariable && paramIndex != -1) - il.Emit(OpCodes.Starg_S, paramIndex + 1); - else if (isParameterOrVariable || useLocalVar && !usesResult) - EmitStoreLocalVariable(il, localVarIndex); - - if (isParameterOrVariable) - return true; - - if (useLocalVar && !usesResult) - EmitLoadLocalVariable(il, localVarIndex); - - if (!EmitMemberAssign(il, memberAccess.Member)) - return false; - - if (useLocalVar && usesResult) - EmitLoadLocalVariable(il, localVarIndex); - - return true; - } - - private static void StoreIncDecValue(ILGenerator il, bool usesResult, bool isVar, int localVarIndex) - { - if (!usesResult) - return; - - if (isVar || localVarIndex == -1) - il.Emit(OpCodes.Dup); - else - { - EmitStoreLocalVariable(il, localVarIndex); - EmitLoadLocalVariable(il, localVarIndex); - } - } - - private static bool TryEmitAssign(BinaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var left = expr.Left; - var right = expr.Right; - var leftNodeType = expr.Left.NodeType; - var nodeType = expr.NodeType; - - // if this assignment is part of a single body-less expression or the result of a block - // we should put its result to the evaluation stack before the return, otherwise we are - // somewhere inside the block, so we shouldn't return with the result - var flags = parent & ~ParentFlags.IgnoreResult; - switch (leftNodeType) - { - case ExpressionType.Parameter: - var leftParamExpr = (ParameterExpression)left; - - var paramIndex = paramExprs.Count - 1; - while (paramIndex != -1 && !ReferenceEquals(paramExprs[paramIndex], leftParamExpr)) - --paramIndex; - - var arithmeticNodeType = nodeType; - switch (nodeType) - { - case ExpressionType.AddAssign: - arithmeticNodeType = ExpressionType.Add; - break; - case ExpressionType.AddAssignChecked: - arithmeticNodeType = ExpressionType.AddChecked; - break; - case ExpressionType.SubtractAssign: - arithmeticNodeType = ExpressionType.Subtract; - break; - case ExpressionType.SubtractAssignChecked: - arithmeticNodeType = ExpressionType.SubtractChecked; - break; - case ExpressionType.MultiplyAssign: - arithmeticNodeType = ExpressionType.Multiply; - break; - case ExpressionType.MultiplyAssignChecked: - arithmeticNodeType = ExpressionType.MultiplyChecked; - break; - case ExpressionType.DivideAssign: - arithmeticNodeType = ExpressionType.Divide; - break; - case ExpressionType.ModuloAssign: - arithmeticNodeType = ExpressionType.Modulo; - break; - case ExpressionType.PowerAssign: - arithmeticNodeType = ExpressionType.Power; - break; - case ExpressionType.AndAssign: - arithmeticNodeType = ExpressionType.And; - break; - case ExpressionType.OrAssign: - arithmeticNodeType = ExpressionType.Or; - break; - case ExpressionType.ExclusiveOrAssign: - arithmeticNodeType = ExpressionType.ExclusiveOr; - break; - case ExpressionType.LeftShiftAssign: - arithmeticNodeType = ExpressionType.LeftShift; - break; - case ExpressionType.RightShiftAssign: - arithmeticNodeType = ExpressionType.RightShift; - break; - } - - if (paramIndex != -1) - { - // shift parameter index by one, because the first one will be closure - if ((closure.Status & ClosureStatus.ShouldBeStaticMethod) == 0) - ++paramIndex; - - if (leftParamExpr.IsByRef) - { - if (paramIndex == 0) - il.Emit(OpCodes.Ldarg_0); - else if (paramIndex == 1) - il.Emit(OpCodes.Ldarg_1); - else if (paramIndex == 2) - il.Emit(OpCodes.Ldarg_2); - else if (paramIndex == 3) - il.Emit(OpCodes.Ldarg_3); - else - il.Emit(OpCodes.Ldarg_S, (byte)paramIndex); - } - - if (arithmeticNodeType == nodeType) - { - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - } - else if (!TryEmitArithmetic(expr, arithmeticNodeType, paramExprs, il, ref closure, parent)) - return false; - - if ((parent & ParentFlags.IgnoreResult) == 0) - il.Emit(OpCodes.Dup); // duplicate value to assign and return - - if (leftParamExpr.IsByRef) - EmitByRefStore(il, leftParamExpr.Type); - else - il.Emit(OpCodes.Starg_S, paramIndex); - - return true; - } - else if (arithmeticNodeType != nodeType) - { - var localVarIdx = closure.GetDefinedLocalVarOrDefault(leftParamExpr); - if (localVarIdx != -1) - { - if (!TryEmitArithmetic(expr, arithmeticNodeType, paramExprs, il, ref closure, parent)) - return false; - - EmitStoreLocalVariable(il, localVarIdx); - return true; - } - } - - // if parameter isn't passed, then it is passed into some outer lambda or it is a local variable, - // so it should be loaded from closure or from the locals. Then the closure is null will be an invalid state. - // if it's a local variable, then store the right value in it - var localVariableIdx = closure.GetDefinedLocalVarOrDefault(leftParamExpr); - if (localVariableIdx != -1) - { - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - if ((right as ParameterExpression)?.IsByRef == true) - il.Emit(OpCodes.Ldind_I4); - - if ((parent & ParentFlags.IgnoreResult) == 0) // if we have to push the result back, duplicate the right value - il.Emit(OpCodes.Dup); - - EmitStoreLocalVariable(il, localVariableIdx); - return true; - } - - // check that it's a captured parameter by closure - var nonPassedParams = closure.NonPassedParameters; - var nonPassedParamIndex = nonPassedParams.Length - 1; - while (nonPassedParamIndex != -1 && - !ReferenceEquals(nonPassedParams[nonPassedParamIndex], leftParamExpr)) - --nonPassedParamIndex; - if (nonPassedParamIndex == -1) - return false; // what??? no chance - - il.Emit(OpCodes.Ldarg_0); // closure is always a first argument - - if ((parent & ParentFlags.IgnoreResult) == 0) - { - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - var valueVarIndex = il.GetNextLocalVarIndex(expr.Type); // store left value in variable - EmitStoreLocalVariable(il, valueVarIndex); - - // load array field and param item index - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, nonPassedParamIndex); - EmitLoadLocalVariable(il, valueVarIndex); - if (expr.Type.IsValueType()) - il.Emit(OpCodes.Box, expr.Type); - il.Emit(OpCodes.Stelem_Ref); // put the variable into array - EmitLoadLocalVariable(il, valueVarIndex); - } - else - { - // load array field and param item index - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, nonPassedParamIndex); - - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - if (expr.Type.IsValueType()) - il.Emit(OpCodes.Box, expr.Type); - il.Emit(OpCodes.Stelem_Ref); // put the variable into array - } - - return true; - - case ExpressionType.MemberAccess: - var assignFromLocalVar = right.NodeType == ExpressionType.Try; - - var resultLocalVarIndex = -1; - if (assignFromLocalVar) - { - resultLocalVarIndex = il.GetNextLocalVarIndex(right.Type); - - if (!TryEmit(right, paramExprs, il, ref closure, ParentFlags.Empty)) - return false; - - EmitStoreLocalVariable(il, resultLocalVarIndex); - } - - var memberExpr = (MemberExpression)left; - var objExpr = memberExpr.Expression; - if (objExpr != null && - !TryEmit(objExpr, paramExprs, il, ref closure, flags | ParentFlags.MemberAccess | ParentFlags.InstanceAccess)) - return false; - - if (assignFromLocalVar) - EmitLoadLocalVariable(il, resultLocalVarIndex); - else if (!TryEmit(right, paramExprs, il, ref closure, ParentFlags.Empty)) - return false; - - var member = memberExpr.Member; - if ((parent & ParentFlags.IgnoreResult) != 0) - return EmitMemberAssign(il, member); - - il.Emit(OpCodes.Dup); - - var rightVarIndex = il.GetNextLocalVarIndex(expr.Type); // store right value in variable - EmitStoreLocalVariable(il, rightVarIndex); - - if (!EmitMemberAssign(il, member)) - return false; - - EmitLoadLocalVariable(il, rightVarIndex); - return true; - - case ExpressionType.Index: - var indexExpr = (IndexExpression)left; - - var obj = indexExpr.Object; - if (obj != null && !TryEmit(obj, paramExprs, il, ref closure, flags)) - return false; - - var indexArgExprs = indexExpr.Arguments; - for (var i = 0; i < indexArgExprs.Count; i++) - if (!TryEmit(indexArgExprs[i], paramExprs, il, ref closure, flags, i)) - return false; - - if (!TryEmit(right, paramExprs, il, ref closure, flags)) - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - return TryEmitIndexAssign(indexExpr, obj?.Type, expr.Type, il); - - var varIndex = il.GetNextLocalVarIndex(expr.Type); // store value in variable to return - il.Emit(OpCodes.Dup); - EmitStoreLocalVariable(il, varIndex); - - if (!TryEmitIndexAssign(indexExpr, obj?.Type, expr.Type, il)) - return false; - - EmitLoadLocalVariable(il, varIndex); - return true; - - default: // todo: not yet support assignment targets - return false; - } - } - - private static void EmitByRefStore(ILGenerator il, Type type) - { - if (type == typeof(int) || type == typeof(uint)) - il.Emit(OpCodes.Stind_I4); - else if (type == typeof(byte)) - il.Emit(OpCodes.Stind_I1); - else if (type == typeof(short) || type == typeof(ushort)) - il.Emit(OpCodes.Stind_I2); - else if (type == typeof(long) || type == typeof(ulong)) - il.Emit(OpCodes.Stind_I8); - else if (type == typeof(float)) - il.Emit(OpCodes.Stind_R4); - else if (type == typeof(double)) - il.Emit(OpCodes.Stind_R8); - else if (type == typeof(object)) - il.Emit(OpCodes.Stind_Ref); - else if (type == typeof(IntPtr) || type == typeof(UIntPtr)) - il.Emit(OpCodes.Stind_I); - else - il.Emit(OpCodes.Stobj, type); - } - - private static bool TryEmitIndexAssign(IndexExpression indexExpr, Type instType, Type elementType, ILGenerator il) - { - if (indexExpr.Indexer != null) - return EmitMemberAssign(il, indexExpr.Indexer); - - if (indexExpr.Arguments.Count == 1) // one dimensional array - { - if (elementType.IsValueType()) - il.Emit(OpCodes.Stelem, elementType); - else - il.Emit(OpCodes.Stelem_Ref); - return true; - } - - // multi dimensional array - return EmitMethodCall(il, instType?.FindMethod("Set")); - } - - private static bool TryEmitMethodCall(Expression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var flags = parent & ~ParentFlags.IgnoreResult | ParentFlags.Call; - var callExpr = (MethodCallExpression)expr; - var objExpr = callExpr.Object; - var method = callExpr.Method; - var methodParams = method.GetParameters(); - var objIsValueType = false; - if (objExpr != null) - { - if (!TryEmit(objExpr, paramExprs, il, ref closure, flags | ParentFlags.InstanceAccess)) - return false; - - objIsValueType = objExpr.Type.IsValueType(); - if (objIsValueType && objExpr.NodeType != ExpressionType.Parameter && !closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, objExpr.Type); - } - - var fewArgCount = callExpr.FewArgumentCount; - if (fewArgCount >= 0) - { - if (fewArgCount == 1) - { - if (!TryEmit(((OneArgumentMethodCallExpression)callExpr).Argument, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1)) - return false; - } - else if (fewArgCount == 2) - { - var twoArgsExpr = (TwoArgumentsMethodCallExpression)callExpr; - if (!TryEmit(twoArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(twoArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1)) - return false; - } - else if (fewArgCount == 3) - { - var threeArgsExpr = (ThreeArgumentsMethodCallExpression)callExpr; - if (!TryEmit(threeArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(threeArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(threeArgsExpr.Argument2, paramExprs, il, ref closure, flags, methodParams[2].ParameterType.IsByRef ? 2 : -1)) - return false; - } - else if (fewArgCount == 4) - { - var fourArgsExpr = (FourArgumentsMethodCallExpression)callExpr; - if (!TryEmit(fourArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fourArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fourArgsExpr.Argument2, paramExprs, il, ref closure, flags, methodParams[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fourArgsExpr.Argument3, paramExprs, il, ref closure, flags, methodParams[3].ParameterType.IsByRef ? 3 : -1)) - return false; - } - else if (fewArgCount == 5) - { - var fiveArgsExpr = (FiveArgumentsMethodCallExpression)callExpr; - if (!TryEmit(fiveArgsExpr.Argument0, paramExprs, il, ref closure, flags, methodParams[0].ParameterType.IsByRef ? 0 : -1) || - !TryEmit(fiveArgsExpr.Argument1, paramExprs, il, ref closure, flags, methodParams[1].ParameterType.IsByRef ? 1 : -1) || - !TryEmit(fiveArgsExpr.Argument2, paramExprs, il, ref closure, flags, methodParams[2].ParameterType.IsByRef ? 2 : -1) || - !TryEmit(fiveArgsExpr.Argument3, paramExprs, il, ref closure, flags, methodParams[3].ParameterType.IsByRef ? 3 : -1) || - !TryEmit(fiveArgsExpr.Argument4, paramExprs, il, ref closure, flags, methodParams[4].ParameterType.IsByRef ? 4 : -1)) - return false; - } - - if (objIsValueType && method.IsVirtual) - il.Emit(OpCodes.Constrained, objExpr.Type); - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - if (parent.IgnoresResult() && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - closure.LastEmitIsAddress = false; - return true; - } - - var args = callExpr.Arguments; - for (var i = 0; i < methodParams.Length; i++) - if (!TryEmit(args[i], paramExprs, il, ref closure, flags, methodParams[i].ParameterType.IsByRef ? i : -1)) - return false; - - if (objIsValueType && method.IsVirtual) - il.Emit(OpCodes.Constrained, objExpr.Type); - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - if (parent.IgnoresResult() && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - - closure.LastEmitIsAddress = false; - return true; - } - - private static bool TryEmitMemberAccess(MemberExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - if (expr.Member is PropertyInfo prop) - { - var instanceExpr = expr.Expression; - if (instanceExpr != null) - { - if (!TryEmit(instanceExpr, paramExprs, il, ref closure, - ~ParentFlags.IgnoreResult & ~ParentFlags.DupMemberOwner & - (parent | ParentFlags.Call | ParentFlags.MemberAccess | ParentFlags.InstanceAccess))) - return false; - - if ((parent & ParentFlags.DupMemberOwner) != 0) - il.Emit(OpCodes.Dup); - - // Value type special treatment to load address of value instance in order to access a field or call a method. - // Parameter should be excluded because it already loads an address via `LDARGA`, and you don't need to. - // And for field access no need to load address, cause the field stored on stack nearby - if (!closure.LastEmitIsAddress && - instanceExpr.NodeType != ExpressionType.Parameter && instanceExpr.Type.IsValueType()) - EmitStoreLocalVariableAndLoadItsAddress(il, instanceExpr.Type); - } - - closure.LastEmitIsAddress = false; - var propGetter = prop.DeclaringType.FindPropertyGetMethod(prop.Name); - if (propGetter == null) - return false; - - il.Emit(propGetter.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, propGetter); - return true; - } - - if (expr.Member is FieldInfo field) - { - var instanceExpr = expr.Expression; - if (instanceExpr != null) - { - if (!TryEmit(instanceExpr, paramExprs, il, ref closure, - ~ParentFlags.IgnoreResult & ~ParentFlags.DupMemberOwner & - (parent | ParentFlags.MemberAccess | ParentFlags.InstanceAccess))) - return false; - - if ((parent & ParentFlags.DupMemberOwner) != 0) - il.Emit(OpCodes.Dup); - - closure.LastEmitIsAddress = field.FieldType.IsValueType() && (parent & ParentFlags.InstanceAccess) != 0; - il.Emit(closure.LastEmitIsAddress ? OpCodes.Ldflda : OpCodes.Ldfld, field); - } - else if (field.IsLiteral) - { - var fieldValue = field.GetValue(null); - if (fieldValue != null) - return TryEmitNotNullConstant(false, field.FieldType, fieldValue, il, ref closure); - - il.Emit(OpCodes.Ldnull); - } - else - { - il.Emit(OpCodes.Ldsfld, field); - } - - return true; - } - - return false; - } - - // ReSharper disable once FunctionComplexityOverflow - private static bool TryEmitNestedLambda(LambdaExpression lambdaExpr, - IReadOnlyList outerParamExprs, ILGenerator il, ref ClosureInfo closure) - { - // First, find in closed compiled lambdas the one corresponding to the current lambda expression. - // Situation with not found lambda is not possible/exceptional, - // it means that we somehow skipped the lambda expression while collecting closure info. - var outerNestedLambdas = closure.NestedLambdas; - var outerNestedLambdaIndex = outerNestedLambdas.Length - 1; - while (outerNestedLambdaIndex != -1 && - !ReferenceEquals(outerNestedLambdas[outerNestedLambdaIndex].LambdaExpression, lambdaExpr)) - --outerNestedLambdaIndex; - if (outerNestedLambdaIndex == -1) - return false; - - var nestedLambdaInfo = closure.NestedLambdas[outerNestedLambdaIndex]; - var nestedLambda = nestedLambdaInfo.Lambda; - var nestedLambdaInClosureIndex = outerNestedLambdaIndex + closure.Constants.Count; - - var varIndex = nestedLambdaInfo.UsageCountOrVarIndex - 1; - if (varIndex > 0) - EmitLoadLocalVariable(il, varIndex); - else - { - il.Emit(OpCodes.Ldloc_0); - EmitLoadConstantInt(il, nestedLambdaInClosureIndex); - il.Emit(OpCodes.Ldelem_Ref); // load the array item object - } - - // If lambda does not use any outer parameters to be set in closure, then we're done - ref var nestedClosureInfo = ref nestedLambdaInfo.ClosureInfo; - var nestedNonPassedParams = nestedClosureInfo.NonPassedParameters; - if (nestedNonPassedParams.Length == 0) - return true; - - //------------------------------------------------------------------- - // For the lambda with non-passed parameters (or variables) in closure - // we have loaded `NestedLambdaWithConstantsAndNestedLambdas` pair. - if (varIndex > 0) - { - // we are already have variable loaded - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.NestedLambdaField); - EmitLoadLocalVariable(il, varIndex); // load the variable for the second time - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.ConstantsAndNestedLambdasField); - } - else - { - var nestedLambdaAndClosureItemsVarIndex = il.GetNextLocalVarIndex(typeof(NestedLambdaWithConstantsAndNestedLambdas)); - EmitStoreLocalVariable(il, nestedLambdaAndClosureItemsVarIndex); - - // - load the `NestedLambda` field - EmitLoadLocalVariable(il, nestedLambdaAndClosureItemsVarIndex); - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.NestedLambdaField); - - // - load the `ConstantsAndNestedLambdas` field - EmitLoadLocalVariable(il, nestedLambdaAndClosureItemsVarIndex); - il.Emit(OpCodes.Ldfld, NestedLambdaWithConstantsAndNestedLambdas.ConstantsAndNestedLambdasField); - } - - // - create `NonPassedParameters` array - EmitLoadConstantInt(il, nestedNonPassedParams.Length); // size of array - il.Emit(OpCodes.Newarr, typeof(object)); - - // - populate the `NonPassedParameters` array - var outerNonPassedParams = closure.NonPassedParameters; - for (var nestedParamIndex = 0; nestedParamIndex < nestedNonPassedParams.Length; ++nestedParamIndex) - { - var nestedParam = nestedNonPassedParams[nestedParamIndex]; - - // Duplicate nested array on stack to store the item, and load index to where to store - il.Emit(OpCodes.Dup); - EmitLoadConstantInt(il, nestedParamIndex); - - var outerParamIndex = outerParamExprs.Count - 1; - while (outerParamIndex != -1 && !ReferenceEquals(outerParamExprs[outerParamIndex], nestedParam)) - --outerParamIndex; - if (outerParamIndex != -1) // load parameter from input outer params - { - // Add `+1` to index because the `0` index is for the closure argument - if (outerParamIndex == 0) - il.Emit(OpCodes.Ldarg_1); - else if (outerParamIndex == 1) - il.Emit(OpCodes.Ldarg_2); - else if (outerParamIndex == 2) - il.Emit(OpCodes.Ldarg_3); - else - il.Emit(OpCodes.Ldarg_S, (byte)(1 + outerParamIndex)); - - if (nestedParam.Type.IsValueType()) - il.Emit(OpCodes.Box, nestedParam.Type); - } - else // load parameter from outer closure or from the local variables - { - if (outerNonPassedParams.Length == 0) - return false; // impossible, better to throw? - - var variableIdx = closure.GetDefinedLocalVarOrDefault(nestedParam); - if (variableIdx != -1) // it's a local variable - { - EmitLoadLocalVariable(il, variableIdx); - } - else // it's a parameter from the outer closure - { - var outerNonPassedParamIndex = outerNonPassedParams.Length - 1; - while (outerNonPassedParamIndex != -1 && !ReferenceEquals(outerNonPassedParams[outerNonPassedParamIndex], nestedParam)) - --outerNonPassedParamIndex; - if (outerNonPassedParamIndex == -1) - return false; // impossible - - // Load the parameter from outer closure `Items` array - il.Emit(OpCodes.Ldarg_0); // closure is always a first argument - il.Emit(OpCodes.Ldfld, ArrayClosureWithNonPassedParamsField); - EmitLoadConstantInt(il, outerNonPassedParamIndex); - il.Emit(OpCodes.Ldelem_Ref); - } - } - - // Store the item into nested lambda array - il.Emit(OpCodes.Stelem_Ref); - } - - // - create `ArrayClosureWithNonPassedParams` out of the both above - il.Emit(OpCodes.Newobj, ArrayClosureWithNonPassedParamsConstructor); - - // - call `Curry` method with nested lambda and array closure to produce a closed lambda with the expected signature - var lambdaTypeArgs = nestedLambda.GetType().GetTypeInfo().GenericTypeArguments; - - var closureMethod = nestedLambdaInfo.LambdaExpression.ReturnType == typeof(void) - ? CurryClosureActions.Methods[lambdaTypeArgs.Length - 1].MakeGenericMethod(lambdaTypeArgs) - : CurryClosureFuncs.Methods[lambdaTypeArgs.Length - 2].MakeGenericMethod(lambdaTypeArgs); - - il.Emit(OpCodes.Call, closureMethod); - return true; - } - - private static bool TryEmitInvoke(InvocationExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var lambda = expr.Expression; - if (!TryEmit(lambda, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - - var argExprs = expr.Arguments; - for (var i = 0; i < argExprs.Count; i++) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, - parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess, - argExprs[i].Type.IsByRef ? i : -1)) - return false; - - var delegateInvokeMethod = lambda.Type.FindDelegateInvokeMethod(); - il.Emit(OpCodes.Call, delegateInvokeMethod); - - if ((parent & ParentFlags.IgnoreResult) != 0 && delegateInvokeMethod.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - - return true; - } - - private static bool TryEmitSwitch(SwitchExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - // todo: - //- use switch statement for int comparison (if int difference is less or equal 3 -> use IL switch) - //- TryEmitComparison should not emit "CEQ" so we could use Beq_S instead of Brtrue_S (not always possible (nullable)) - //- if switch SwitchValue is a nullable parameter, we should call getValue only once and store the result. - //- use comparison methods (when defined) - - var endLabel = il.DefineLabel(); - var labels = new Label[expr.Cases.Count]; - for (var index = 0; index < expr.Cases.Count; index++) - { - var switchCase = expr.Cases[index]; - labels[index] = il.DefineLabel(); - - foreach (var switchCaseTestValue in switchCase.TestValues) - { - if (!TryEmitComparison(expr.SwitchValue, switchCaseTestValue, ExpressionType.Equal, paramExprs, il, - ref closure, parent)) - return false; - il.Emit(OpCodes.Brtrue, labels[index]); - } - } - - if (expr.DefaultBody != null) - { - if (!TryEmit(expr.DefaultBody, paramExprs, il, ref closure, parent)) - return false; - il.Emit(OpCodes.Br, endLabel); - } - - for (var index = 0; index < expr.Cases.Count; index++) - { - var switchCase = expr.Cases[index]; - il.MarkLabel(labels[index]); - if (!TryEmit(switchCase.Body, paramExprs, il, ref closure, parent)) - return false; - - if (index != expr.Cases.Count - 1) - il.Emit(OpCodes.Br, endLabel); - } - - il.MarkLabel(endLabel); - - return true; - } - - private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, ExpressionType expressionType, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var leftOpType = exprLeft.Type; - var leftIsNullable = leftOpType.IsNullable(); - var rightOpType = exprRight.Type; - if (exprRight is ConstantExpression c && c.Value == null && exprRight.Type == typeof(object)) - rightOpType = leftOpType; - - int lVarIndex = -1, rVarIndex = -1; - if (!TryEmit(exprLeft, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) - return false; - - if (leftIsNullable) - { - lVarIndex = EmitStoreLocalVariableAndLoadItsAddress(il, leftOpType); - il.Emit(OpCodes.Call, leftOpType.FindNullableGetValueOrDefaultMethod()); - leftOpType = Nullable.GetUnderlyingType(leftOpType); - } - - if (!TryEmit(exprRight, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) - return false; - - if (leftOpType != rightOpType) - { - if (leftOpType.IsClass() && rightOpType.IsClass() && - (leftOpType == typeof(object) || rightOpType == typeof(object))) - { - if (expressionType == ExpressionType.Equal) - { - il.Emit(OpCodes.Ceq); - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - } - else if (expressionType == ExpressionType.NotEqual) - { - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - return false; - - if ((parent & ParentFlags.IgnoreResult) != 0) - il.Emit(OpCodes.Pop); - - return true; - } - } - - if (rightOpType.IsNullable()) - { - rVarIndex = EmitStoreLocalVariableAndLoadItsAddress(il, rightOpType); - il.Emit(OpCodes.Call, rightOpType.FindNullableGetValueOrDefaultMethod()); - // ReSharper disable once AssignNullToNotNullAttribute - rightOpType = Nullable.GetUnderlyingType(rightOpType); - } - - var leftOpTypeInfo = leftOpType.GetTypeInfo(); - if (!leftOpTypeInfo.IsPrimitive && !leftOpTypeInfo.IsEnum) - { - var methodName - = expressionType == ExpressionType.Equal ? "op_Equality" - : expressionType == ExpressionType.NotEqual ? "op_Inequality" - : expressionType == ExpressionType.GreaterThan ? "op_GreaterThan" - : expressionType == ExpressionType.GreaterThanOrEqual ? "op_GreaterThanOrEqual" - : expressionType == ExpressionType.LessThan ? "op_LessThan" - : expressionType == ExpressionType.LessThanOrEqual ? "op_LessThanOrEqual" - : null; - - if (methodName == null) - return false; - - // todo: for now handling only parameters of the same type - var methods = leftOpTypeInfo.DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && m.IsStatic && m.Name == methodName && - IsComparisonOperatorSignature(leftOpType, m.GetParameters())) - { - il.Emit(OpCodes.Call, m); - return true; - } - } - - if (expressionType != ExpressionType.Equal && expressionType != ExpressionType.NotEqual) - return false; - - il.Emit(OpCodes.Call, _objectEqualsMethod); - - if (expressionType == ExpressionType.NotEqual) // invert result for not equal - { - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - - if (leftIsNullable) - goto nullCheck; - - if ((parent & ParentFlags.IgnoreResult) > 0) - il.Emit(OpCodes.Pop); - - return true; - } - - // handle primitives comparison - switch (expressionType) - { - case ExpressionType.Equal: - il.Emit(OpCodes.Ceq); - break; - - case ExpressionType.NotEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - - case ExpressionType.LessThan: - il.Emit(OpCodes.Clt); - break; - - case ExpressionType.GreaterThan: - il.Emit(OpCodes.Cgt); - break; - - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LessThanOrEqual: - var ifTrueLabel = il.DefineLabel(); - if (rightOpType == typeof(uint) || rightOpType == typeof(ulong) || - rightOpType == typeof(ushort) || rightOpType == typeof(byte)) - il.Emit(expressionType == ExpressionType.GreaterThanOrEqual ? OpCodes.Bge_Un_S : OpCodes.Ble_Un_S, ifTrueLabel); - else - il.Emit(expressionType == ExpressionType.GreaterThanOrEqual ? OpCodes.Bge_S : OpCodes.Ble_S, ifTrueLabel); - - il.Emit(OpCodes.Ldc_I4_0); - var doneLabel = il.DefineLabel(); - il.Emit(OpCodes.Br_S, doneLabel); - - il.MarkLabel(ifTrueLabel); - il.Emit(OpCodes.Ldc_I4_1); - - il.MarkLabel(doneLabel); - break; - - default: - return false; - } - - nullCheck: - if (leftIsNullable) - { - var leftNullableHasValueGetterMethod = exprLeft.Type.FindNullableHasValueGetterMethod(); - - EmitLoadLocalVariableAddress(il, lVarIndex); - il.Emit(OpCodes.Call, leftNullableHasValueGetterMethod); - - // ReSharper disable once AssignNullToNotNullAttribute - EmitLoadLocalVariableAddress(il, rVarIndex); - il.Emit(OpCodes.Call, leftNullableHasValueGetterMethod); - - switch (expressionType) - { - case ExpressionType.Equal: - il.Emit(OpCodes.Ceq); // compare both HasValue calls - il.Emit(OpCodes.And); // both results need to be true - break; - - case ExpressionType.NotEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Or); - break; - - case ExpressionType.LessThan: - case ExpressionType.GreaterThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.GreaterThanOrEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.And); - break; - - default: - return false; - } - } - - if ((parent & ParentFlags.IgnoreResult) > 0) - il.Emit(OpCodes.Pop); - - return true; - } - - private static bool IsComparisonOperatorSignature(Type t, ParameterInfo[] pars) => - pars.Length == 2 && pars[0].ParameterType == t && pars[1].ParameterType == t; - - private static bool TryEmitArithmetic(BinaryExpression expr, ExpressionType exprNodeType, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ParentFlags parent) - { - var flags = parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceCall | ParentFlags.Arithmetic; - - var leftNoValueLabel = default(Label); - var leftExpr = expr.Left; - var lefType = leftExpr.Type; - var leftIsNullable = lefType.IsNullable(); - if (leftIsNullable) - { - leftNoValueLabel = il.DefineLabel(); - if (!TryEmit(leftExpr, paramExprs, il, ref closure, flags | ParentFlags.InstanceCall)) - return false; - - if (!closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, lefType); - - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Call, lefType.FindNullableHasValueGetterMethod()); - - il.Emit(OpCodes.Brfalse, leftNoValueLabel); - il.Emit(OpCodes.Call, lefType.FindNullableGetValueOrDefaultMethod()); - } - else if (!TryEmit(leftExpr, paramExprs, il, ref closure, flags)) - return false; - - var rightNoValueLabel = default(Label); - var rightExpr = expr.Right; - var rightType = rightExpr.Type; - var rightIsNullable = rightType.IsNullable(); - if (rightIsNullable) - { - rightNoValueLabel = il.DefineLabel(); - if (!TryEmit(rightExpr, paramExprs, il, ref closure, flags | ParentFlags.InstanceCall)) - return false; - - if (!closure.LastEmitIsAddress) - EmitStoreLocalVariableAndLoadItsAddress(il, rightType); - - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Call, rightType.FindNullableHasValueGetterMethod()); - il.Emit(OpCodes.Brfalse, rightNoValueLabel); - il.Emit(OpCodes.Call, rightType.FindNullableGetValueOrDefaultMethod()); - } - else if (!TryEmit(rightExpr, paramExprs, il, ref closure, flags)) - return false; - - var exprType = expr.Type; - if (!TryEmitArithmeticOperation(expr, exprNodeType, exprType, il)) - return false; - - if (leftIsNullable || rightIsNullable) - { - var valueLabel = il.DefineLabel(); - il.Emit(OpCodes.Br, valueLabel); - - if (rightIsNullable) - il.MarkLabel(rightNoValueLabel); - il.Emit(OpCodes.Pop); - - if (leftIsNullable) - il.MarkLabel(leftNoValueLabel); - il.Emit(OpCodes.Pop); - - if (exprType.IsNullable()) - { - var endL = il.DefineLabel(); - var locIndex = InitValueTypeVariable(il, exprType); - EmitLoadLocalVariable(il, locIndex); - il.Emit(OpCodes.Br_S, endL); - il.MarkLabel(valueLabel); - il.Emit(OpCodes.Newobj, exprType.GetTypeInfo().DeclaredConstructors.GetFirst()); - il.MarkLabel(endL); - } - else - { - il.Emit(OpCodes.Ldc_I4_0); - il.MarkLabel(valueLabel); - } - } - - return true; - } - - private static bool TryEmitArithmeticOperation(BinaryExpression expr, - ExpressionType exprNodeType, Type exprType, ILGenerator il) - { - if (!exprType.IsPrimitive()) - { - if (exprType.IsNullable()) - exprType = Nullable.GetUnderlyingType(exprType); - - if (!exprType.IsPrimitive()) - { - MethodInfo method = null; - if (exprType == typeof(string)) - { - var paraType = typeof(string); - if (expr.Left.Type != expr.Right.Type || expr.Left.Type != typeof(string)) - paraType = typeof(object); - - var methods = typeof(string).GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsStatic && m.Name == "Concat" && - m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == paraType) - { - method = m; - break; - } - } - } - else - { - var methodName - = exprNodeType == ExpressionType.Add ? "op_Addition" - : exprNodeType == ExpressionType.AddChecked ? "op_Addition" - : exprNodeType == ExpressionType.Subtract ? "op_Subtraction" - : exprNodeType == ExpressionType.SubtractChecked ? "op_Subtraction" - : exprNodeType == ExpressionType.Multiply ? "op_Multiply" - : exprNodeType == ExpressionType.MultiplyChecked ? "op_Multiply" - : exprNodeType == ExpressionType.Divide ? "op_Division" - : exprNodeType == ExpressionType.Modulo ? "op_Modulus" - : null; - - if (methodName != null) - { - var methods = exprType.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; method == null && i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsSpecialName && m.IsStatic && m.Name == methodName) - method = m; - } - } - } - - if (method == null) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - return true; - } - } - - switch (exprNodeType) - { - case ExpressionType.Add: - case ExpressionType.AddAssign: - il.Emit(OpCodes.Add); - return true; - - case ExpressionType.AddChecked: - case ExpressionType.AddAssignChecked: - il.Emit(exprType.IsUnsigned() ? OpCodes.Add_Ovf_Un : OpCodes.Add_Ovf); - return true; - - case ExpressionType.Subtract: - case ExpressionType.SubtractAssign: - il.Emit(OpCodes.Sub); - return true; - - case ExpressionType.SubtractChecked: - case ExpressionType.SubtractAssignChecked: - il.Emit(exprType.IsUnsigned() ? OpCodes.Sub_Ovf_Un : OpCodes.Sub_Ovf); - return true; - - case ExpressionType.Multiply: - case ExpressionType.MultiplyAssign: - il.Emit(OpCodes.Mul); - return true; - - case ExpressionType.MultiplyChecked: - case ExpressionType.MultiplyAssignChecked: - il.Emit(exprType.IsUnsigned() ? OpCodes.Mul_Ovf_Un : OpCodes.Mul_Ovf); - return true; - - case ExpressionType.Divide: - case ExpressionType.DivideAssign: - il.Emit(OpCodes.Div); - return true; - - case ExpressionType.Modulo: - case ExpressionType.ModuloAssign: - il.Emit(OpCodes.Rem); - return true; - - case ExpressionType.And: - case ExpressionType.AndAssign: - il.Emit(OpCodes.And); - return true; - - case ExpressionType.Or: - case ExpressionType.OrAssign: - il.Emit(OpCodes.Or); - return true; - - case ExpressionType.ExclusiveOr: - case ExpressionType.ExclusiveOrAssign: - il.Emit(OpCodes.Xor); - return true; - - case ExpressionType.LeftShift: - case ExpressionType.LeftShiftAssign: - il.Emit(OpCodes.Shl); - return true; - - case ExpressionType.RightShift: - case ExpressionType.RightShiftAssign: - il.Emit(OpCodes.Shr); - return true; - - case ExpressionType.Power: - il.Emit(OpCodes.Call, typeof(Math).FindMethod("Pow")); - return true; - } - - return false; - } - - private static bool TryEmitLogicalOperator(BinaryExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - if (!TryEmit(expr.Left, paramExprs, il, ref closure, parent)) - return false; - - var labelSkipRight = il.DefineLabel(); - il.Emit(expr.NodeType == ExpressionType.AndAlso ? OpCodes.Brfalse : OpCodes.Brtrue, labelSkipRight); - - if (!TryEmit(expr.Right, paramExprs, il, ref closure, parent)) - return false; - - var labelDone = il.DefineLabel(); - il.Emit(OpCodes.Br, labelDone); - - il.MarkLabel(labelSkipRight); // label the second branch - il.Emit(expr.NodeType == ExpressionType.AndAlso ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1); - il.MarkLabel(labelDone); - - return true; - } - - private static bool TryEmitConditional(ConditionalExpression expr, - IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) - { - var testExpr = TryReduceCondition(expr.Test); - - // detect a special simplistic case of comparison with `null` - var comparedWithNull = false; - if (testExpr is BinaryExpression b) - { - if (b.NodeType == ExpressionType.Equal || b.NodeType == ExpressionType.NotEqual || - !b.Left.Type.IsNullable() && !b.Right.Type.IsNullable()) - { - if (b.Right is ConstantExpression r && r.Value == null) - { - // the null comparison for nullable is actually a `nullable.HasValue` check, - // which implies member access on nullable struct - therefore loading it by address - if (b.Left.Type.IsNullable()) - parent |= ParentFlags.MemberAccess; - comparedWithNull = TryEmit(b.Left, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult); - } - else if (b.Left is ConstantExpression l && l.Value == null) - { - // the null comparison for nullable is actually a `nullable.HasValue` check, - // which implies member access on nullable struct - therefore loading it by address - if (b.Right.Type.IsNullable()) - parent |= ParentFlags.MemberAccess; - comparedWithNull = TryEmit(b.Right, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult); - } - } - } - - if (!comparedWithNull) - { - if (!TryEmit(testExpr, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) - return false; - } - - var labelIfFalse = il.DefineLabel(); - il.Emit(comparedWithNull && testExpr.NodeType == ExpressionType.Equal ? OpCodes.Brtrue : OpCodes.Brfalse, labelIfFalse); - - var ifTrueExpr = expr.IfTrue; - if (!TryEmit(ifTrueExpr, paramExprs, il, ref closure, parent & ParentFlags.IgnoreResult)) - return false; - - var ifFalseExpr = expr.IfFalse; - if (ifFalseExpr.NodeType == ExpressionType.Default && ifFalseExpr.Type == typeof(void)) - { - il.MarkLabel(labelIfFalse); - return true; - } - - var labelDone = il.DefineLabel(); - il.Emit(OpCodes.Br, labelDone); - - il.MarkLabel(labelIfFalse); - if (!TryEmit(ifFalseExpr, paramExprs, il, ref closure, parent & ParentFlags.IgnoreResult)) - return false; - - il.MarkLabel(labelDone); - return true; - } - - private static Expression TryReduceCondition(Expression testExpr) - { - if (testExpr is BinaryExpression b) - { - if (b.NodeType == ExpressionType.OrElse || b.NodeType == ExpressionType.Or) - { - if (b.Left is ConstantExpression l && l.Value is bool lb) - return lb ? b.Left : TryReduceCondition(b.Right); - - if (b.Right is ConstantExpression r && r.Value is bool rb && rb == false) - return TryReduceCondition(b.Left); - } - else if (b.NodeType == ExpressionType.AndAlso || b.NodeType == ExpressionType.And) - { - if (b.Left is ConstantExpression l && l.Value is bool lb) - return !lb ? b.Left : TryReduceCondition(b.Right); - - if (b.Right is ConstantExpression r && r.Value is bool rb && rb) - return TryReduceCondition(b.Left); - } - } - - return testExpr; - } - - private static bool EmitMethodCall(ILGenerator il, MethodInfo method, ParentFlags parent = ParentFlags.Empty) - { - if (method == null) - return false; - - il.Emit(method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, method); - - if ((parent & ParentFlags.IgnoreResult) != 0 && method.ReturnType != typeof(void)) - il.Emit(OpCodes.Pop); - return true; - } - - private static void EmitLoadConstantInt(ILGenerator il, int i) - { - switch (i) - { - case -1: - il.Emit(OpCodes.Ldc_I4_M1); - break; - case 0: - il.Emit(OpCodes.Ldc_I4_0); - break; - case 1: - il.Emit(OpCodes.Ldc_I4_1); - break; - case 2: - il.Emit(OpCodes.Ldc_I4_2); - break; - case 3: - il.Emit(OpCodes.Ldc_I4_3); - break; - case 4: - il.Emit(OpCodes.Ldc_I4_4); - break; - case 5: - il.Emit(OpCodes.Ldc_I4_5); - break; - case 6: - il.Emit(OpCodes.Ldc_I4_6); - break; - case 7: - il.Emit(OpCodes.Ldc_I4_7); - break; - case 8: - il.Emit(OpCodes.Ldc_I4_8); - break; - default: - if (i > -129 && i < 128) - il.Emit(OpCodes.Ldc_I4_S, (sbyte)i); - else - il.Emit(OpCodes.Ldc_I4, i); - break; - } - } - - private static void EmitLoadLocalVariableAddress(ILGenerator il, int location) - { - if (location < 256) - il.Emit(OpCodes.Ldloca_S, (byte)location); - else - il.Emit(OpCodes.Ldloca, location); - } - - private static void EmitLoadLocalVariable(ILGenerator il, int location) - { - if (location == 0) - il.Emit(OpCodes.Ldloc_0); - else if (location == 1) - il.Emit(OpCodes.Ldloc_1); - else if (location == 2) - il.Emit(OpCodes.Ldloc_2); - else if (location == 3) - il.Emit(OpCodes.Ldloc_3); - else if (location < 256) - il.Emit(OpCodes.Ldloc_S, (byte)location); - else - il.Emit(OpCodes.Ldloc, location); - } - - private static void EmitStoreLocalVariable(ILGenerator il, int location) - { - if (location == 0) - il.Emit(OpCodes.Stloc_0); - else if (location == 1) - il.Emit(OpCodes.Stloc_1); - else if (location == 2) - il.Emit(OpCodes.Stloc_2); - else if (location == 3) - il.Emit(OpCodes.Stloc_3); - else if (location < 256) - il.Emit(OpCodes.Stloc_S, (byte)location); - else - il.Emit(OpCodes.Stloc, location); - } - - private static int EmitStoreLocalVariableAndLoadItsAddress(ILGenerator il, Type type) - { - var varIndex = il.GetNextLocalVarIndex(type); - if (varIndex == 0) - { - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloca_S, (byte)0); - } - else if (varIndex == 1) - { - il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Ldloca_S, (byte)1); - } - else if (varIndex == 2) - { - il.Emit(OpCodes.Stloc_2); - il.Emit(OpCodes.Ldloca_S, (byte)2); - } - else if (varIndex == 3) - { - il.Emit(OpCodes.Stloc_3); - il.Emit(OpCodes.Ldloca_S, (byte)3); - } - else if (varIndex < 256) - { - il.Emit(OpCodes.Stloc_S, (byte)varIndex); - il.Emit(OpCodes.Ldloca_S, (byte)varIndex); - } - else - { - il.Emit(OpCodes.Stloc, varIndex); - il.Emit(OpCodes.Ldloca, varIndex); - } - - return varIndex; - } - } - } - - // Helpers targeting the performance. Extensions method names may be a bit funny (non standard), - // in order to prevent conflicts with YOUR helpers with standard names - internal static class Tools - { - internal static bool IsValueType(this Type type) => type.GetTypeInfo().IsValueType; - internal static bool IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive; - internal static bool IsClass(this Type type) => type.GetTypeInfo().IsClass; - - internal static bool IsUnsigned(this Type type) => - type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong); - - internal static bool IsNullable(this Type type) => - type.GetTypeInfo().IsGenericType && type.GetTypeInfo().GetGenericTypeDefinition() == typeof(Nullable<>); - - internal static MethodInfo FindMethod(this Type type, string methodName) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - if (methods[i].Name == methodName) - return methods[i]; - - return type.GetTypeInfo().BaseType?.FindMethod(methodName); - } - - internal static MethodInfo DelegateTargetGetterMethod = typeof(Delegate).FindPropertyGetMethod("Target"); - - internal static MethodInfo FindDelegateInvokeMethod(this Type type) => type.FindMethod("Invoke"); - - internal static MethodInfo FindNullableGetValueOrDefaultMethod(this Type type) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.GetParameters().Length == 0 && m.Name == "GetValueOrDefault") - return m; - } - - return null; - } - - internal static MethodInfo FindValueGetterMethod(this Type type) => - type.FindPropertyGetMethod("Value"); - - internal static MethodInfo FindNullableHasValueGetterMethod(this Type type) => - type.FindPropertyGetMethod("HasValue"); - - internal static MethodInfo FindPropertyGetMethod(this Type propHolderType, string propName) - { - var methods = propHolderType.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var method = methods[i]; - if (method.IsSpecialName) - { - var methodName = method.Name; - if (methodName.Length == propName.Length + 4 && methodName[0] == 'g' && methodName[3] == '_') - { - var j = propName.Length - 1; - while (j != -1 && propName[j] == methodName[j + 4]) --j; - if (j == -1) - return method; - } - } - } - - return propHolderType.GetTypeInfo().BaseType?.FindPropertyGetMethod(propName); - } - - internal static MethodInfo FindPropertySetMethod(this Type propHolderType, string propName) - { - var methods = propHolderType.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var method = methods[i]; - if (method.IsSpecialName) - { - var methodName = method.Name; - if (methodName.Length == propName.Length + 4 && methodName[0] == 's' && methodName[3] == '_') - { - var j = propName.Length - 1; - while (j != -1 && propName[j] == methodName[j + 4]) --j; - if (j == -1) - return method; - } - } - } - - return propHolderType.GetTypeInfo().BaseType?.FindPropertySetMethod(propName); - } - - internal static MethodInfo FindConvertOperator(this Type type, Type sourceType, Type targetType) - { - var methods = type.GetTypeInfo().DeclaredMethods.AsArray(); - for (var i = 0; i < methods.Length; i++) - { - var m = methods[i]; - if (m.IsStatic && m.IsSpecialName && m.ReturnType == targetType) - { - var n = m.Name; - // n == "op_Implicit" || n == "op_Explicit" - if (n.Length == 11 && - n[2] == '_' && n[5] == 'p' && n[6] == 'l' && n[7] == 'i' && n[8] == 'c' && n[9] == 'i' && n[10] == 't' && - m.GetParameters()[0].ParameterType == sourceType) - return m; - } - } - - return null; - } - - internal static ConstructorInfo FindSingleParamConstructor(this Type type, Type paramType) - { - var ctors = type.GetTypeInfo().DeclaredConstructors.AsArray(); - for (var i = 0; i < ctors.Length; i++) - { - var ctor = ctors[i]; - var parameters = ctor.GetParameters(); - if (parameters.Length == 1 && parameters[0].ParameterType == paramType) - return ctor; - } - - return null; - } - - public static T[] AsArray(this IEnumerable xs) - { - if (xs is T[] array) - return array; - return xs == null ? null : xs.ToArray(); - } - - private static class EmptyArray - { - public static readonly T[] Value = new T[0]; - } - - public static T[] Empty() => EmptyArray.Value; - - public static T[] WithLast(this T[] source, T value) - { - if (source == null || source.Length == 0) - return new[] { value }; - if (source.Length == 1) - return new[] { source[0], value }; - if (source.Length == 2) - return new[] { source[0], source[1], value }; - var sourceLength = source.Length; - var result = new T[sourceLength + 1]; - Array.Copy(source, 0, result, 0, sourceLength); - result[sourceLength] = value; - return result; - } - - public static Type[] GetParamTypes(IReadOnlyList paramExprs) - { - if (paramExprs == null || paramExprs.Count == 0) - return Empty(); - - if (paramExprs.Count == 1) - return new[] { paramExprs[0].IsByRef ? paramExprs[0].Type.MakeByRefType() : paramExprs[0].Type }; - - var paramTypes = new Type[paramExprs.Count]; - for (var i = 0; i < paramTypes.Length; i++) - { - var parameterExpr = paramExprs[i]; - paramTypes[i] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type; - } - - return paramTypes; - } - - public static Type GetFuncOrActionType(Type[] paramTypes, Type returnType) - { - if (returnType == typeof(void)) - { - switch (paramTypes.Length) - { - case 0: return typeof(Action); - case 1: return typeof(Action<>).MakeGenericType(paramTypes); - case 2: return typeof(Action<,>).MakeGenericType(paramTypes); - case 3: return typeof(Action<,,>).MakeGenericType(paramTypes); - case 4: return typeof(Action<,,,>).MakeGenericType(paramTypes); - case 5: return typeof(Action<,,,,>).MakeGenericType(paramTypes); - case 6: return typeof(Action<,,,,,>).MakeGenericType(paramTypes); - case 7: return typeof(Action<,,,,,,>).MakeGenericType(paramTypes); - default: - throw new NotSupportedException( - $"Action with so many ({paramTypes.Length}) parameters is not supported!"); - } - } - - switch (paramTypes.Length) - { - case 0: return typeof(Func<>).MakeGenericType(returnType); - case 1: return typeof(Func<,>).MakeGenericType(paramTypes[0], returnType); - case 2: return typeof(Func<,,>).MakeGenericType(paramTypes[0], paramTypes[1], returnType); - case 3: return typeof(Func<,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], returnType); - case 4: return typeof(Func<,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], returnType); - case 5: return typeof(Func<,,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], paramTypes[4], returnType); - case 6: return typeof(Func<,,,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], paramTypes[4], paramTypes[5], returnType); - case 7: return typeof(Func<,,,,,,,>).MakeGenericType(paramTypes[0], paramTypes[1], paramTypes[2], paramTypes[3], paramTypes[4], paramTypes[5], paramTypes[6], returnType); - default: - throw new NotSupportedException( - $"Func with so many ({paramTypes.Length}) parameters is not supported!"); - } - } - - public static T GetFirst(this IEnumerable source) - { - // This is pretty much Linq.FirstOrDefault except it does not need to check - // if source is IPartition (but should it?) - - if (source is IList list) - return list.Count == 0 ? default : list[0]; - using (var items = source.GetEnumerator()) - return items.MoveNext() ? items.Current : default; - } - - public static T GetFirst(this IList source) - { - return source.Count == 0 ? default : source[0]; - } - - public static T GetFirst(this T[] source) - { - return source.Length == 0 ? default : source[0]; - } - } - - /// Hey - public static class ILGeneratorHacks - { - /* - // Original methods from ILGenerator.cs - - public virtual LocalBuilder DeclareLocal(Type localType) - { - return this.DeclareLocal(localType, false); - } - - public virtual LocalBuilder DeclareLocal(Type localType, bool pinned) - { - MethodBuilder methodBuilder = this.m_methodBuilder as MethodBuilder; - if ((MethodInfo)methodBuilder == (MethodInfo)null) - throw new NotSupportedException(); - if (methodBuilder.IsTypeCreated()) - throw new InvalidOperationException(SR.InvalidOperation_TypeHasBeenCreated); - if (localType == (Type)null) - throw new ArgumentNullException(nameof(localType)); - if (methodBuilder.m_bIsBaked) - throw new InvalidOperationException(SR.InvalidOperation_MethodBaked); - this.m_localSignature.AddArgument(localType, pinned); - LocalBuilder localBuilder = new LocalBuilder(this.m_localCount, localType, (MethodInfo)methodBuilder, pinned); - ++this.m_localCount; - return localBuilder; - } - */ - - /// Not allocating the LocalBuilder class - /// emitting this: - /// il.m_localSignature.AddArgument(type); - /// return PostInc(ref il.LocalCount); - public static Func CompileGetNextLocalVarIndex() - { - if (LocalCountField == null || LocalSignatureField == null || AddArgumentMethod == null) - return (i, t) => i.DeclareLocal(t).LocalIndex; - - var method = new DynamicMethod(string.Empty, - typeof(int), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(ILGenerator), typeof(Type) }, - typeof(ExpressionCompiler.ArrayClosure), skipVisibility: true); - - var il = method.GetILGenerator(); - - il.Emit(OpCodes.Ldarg_1); // load `il` argument - il.Emit(OpCodes.Ldfld, LocalSignatureField); - il.Emit(OpCodes.Ldarg_2); // load `type` argument - il.Emit(OpCodes.Ldc_I4_0); // load `pinned: false` argument - il.Emit(OpCodes.Call, AddArgumentMethod); - - il.Emit(OpCodes.Ldarg_1); // load `il` argument - il.Emit(OpCodes.Ldflda, LocalCountField); - il.Emit(OpCodes.Call, PostIncMethod); - - il.Emit(OpCodes.Ret); - - return (Func)method.CreateDelegate(typeof(Func), ExpressionCompiler.EmptyArrayClosure); - } - - internal static int PostInc(ref int i) => i++; - - public static readonly MethodInfo PostIncMethod = typeof(ILGeneratorHacks).GetTypeInfo() - .GetDeclaredMethod(nameof(PostInc)); - - /// Get via reflection - public static readonly FieldInfo LocalSignatureField = typeof(ILGenerator).GetTypeInfo() - .GetDeclaredField("m_localSignature"); - - /// Get via reflection - public static readonly FieldInfo LocalCountField = typeof(ILGenerator).GetTypeInfo() - .GetDeclaredField("m_localCount"); - - /// Get via reflection - public static readonly MethodInfo AddArgumentMethod = typeof(SignatureHelper).GetTypeInfo() - .GetDeclaredMethods(nameof(SignatureHelper.AddArgument)).First(m => m.GetParameters().Length == 2); - - private static readonly Func _getNextLocalVarIndex = CompileGetNextLocalVarIndex(); - - /// Does the job - public static int GetNextLocalVarIndex(this ILGenerator il, Type t) => _getNextLocalVarIndex(il, t); - } - - internal struct LiveCountArray - { - public int Count; - public T[] Items; - - public LiveCountArray(T[] items) - { - Items = items; - Count = items.Length; - } - - public ref T PushSlot() - { - if (++Count > Items.Length) - Items = Expand(Items); - return ref Items[Count - 1]; - } - - public void PushSlot(T item) - { - if (++Count > Items.Length) - Items = Expand(Items); - Items[Count - 1] = item; - } - - public void Pop() => --Count; - - private static T[] Expand(T[] items) - { - if (items.Length == 0) - return new T[4]; - - var count = items.Length; - var newItems = new T[count << 1]; // count x 2 - Array.Copy(items, 0, newItems, 0, count); - return newItems; - } - } -} -#endif diff --git a/BattleFieldSimulator/ConsoleClient/DryIoc/ImTools.cs b/BattleFieldSimulator/ConsoleClient/DryIoc/ImTools.cs deleted file mode 100644 index 0775e3b..0000000 --- a/BattleFieldSimulator/ConsoleClient/DryIoc/ImTools.cs +++ /dev/null @@ -1,6198 +0,0 @@ -// -/* -The MIT License (MIT) - -Copyright (c) 2016-2020 Maksim Volkau - -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 -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. -*/ - -// ReSharper disable once InconsistentNaming - -#if !NET35 && !PCL -#define SUPPORTS_SPIN_WAIT -#endif - -namespace ImTools -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading; - using System.Diagnostics; - using System.Runtime.CompilerServices; // For [MethodImpl(AggressiveInlining)] - - /// Helpers for functional composition - public static class Fun - { - /// - /// Always a true condition. - /// - public static bool Always(T _) => true; - - /// - /// Identity function returning passed argument as result. - /// - public static T Id(T x) => x; - - /// - /// Forward pipe operator (`|>` in F#) - /// - public static R To(this T x, Func map) => map(x); - - /// - /// Forward pipe operator (`|>` in F#) with the additional state A for two arguments function - /// - public static R To(this T x, S state, Func map) => map(x, state); - - /// - /// Cast to the R type with the forward pipe operator (`|>` in F#) - /// - public static R To(this object x) => (R)x; - - /// - /// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value - /// - public static T Do(this T x, Action effect) - { - effect(x); - return x; - } - - /// - /// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value and the state object - /// - public static T Do(this T x, S state, Action effect) - { - effect(x, state); - return x; - } - - /// - /// Lifts argument to Func without allocations ignoring the first argument. - /// For example if you have `Func{T, R} = _ => instance`, - /// you may rewrite it without allocations as `instance.ToFunc{A, R}` - /// - public static R ToFunc(this R result, T ignoredArg) => result; - } - - /// Helpers for lazy instantiations - public static class Lazy - { - /// Provides result type inference for creation of lazy. - public static Lazy Of(Func valueFactory) => new Lazy(valueFactory); - } - - /// Replacement for `Void` type which can be used as a type argument and value. - /// In traditional functional languages this type is a singleton empty record type, - /// e.g. `()` in Haskell https://en.wikipedia.org/wiki/Unit_type - public struct Unit : IEquatable - { - /// Singleton unit value - making it a lower-case so you could import `using static ImTools.Unit;` and write `return unit;` - public static readonly Unit unit = new Unit(); - - /// - public override string ToString() => "(unit)"; - - /// Equals to any other Unit - public bool Equals(Unit other) => true; - - /// - public override bool Equals(object obj) => obj is Unit; - - /// Using type hash code for the value - public override int GetHashCode() => typeof(Unit).GetHashCode(); - } - - /// Simple value provider interface - useful for the type pattern matching via `case I{T} x: ...` - public interface I - { - /// The value in this case ;) - T Value { get; } - } - - /// Helpers for `Is` and `Union` - public static class UnionTools - { - /// Pretty prints the Union using the type information - internal static string ToString(T value, string prefix = "case(", string suffix = ")") - { - if (typeof(TName) == typeof(Unit)) - return prefix + value + suffix; - - var typeName = typeof(TName).Name; - var i = typeName.IndexOf('`'); - var name = i == -1 ? typeName : typeName.Substring(0, i); - return name + prefix + value + suffix; - } - } - - /// Wraps the `T` in a typed `TData` struct value in a one-line declaration, - /// so the ]]> - /// is different from the ]]> - public abstract class Item where TItem : Item - { - /// Creation method for the consistency with other types - public static item Of(T x) => new item(x); - - /// Nested structure that hosts a value. - /// All nested types by convention here are lowercase - public readonly struct item : IEquatable, I - { - /// - public T Value { [MethodImpl((MethodImplOptions)256)] get => Item; } - - /// The value - public readonly T Item; - - /// Constructor - public item(T x) => Item = x; - - /// - public bool Equals(item other) => EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is item c && Equals(c); - - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value); - } - } - - /// Item without the data payload - public abstract class Item where TItem : Item - { - /// Single item value - public static readonly item Single = new item(); - - /// Nested structure that hosts a value. - /// All nested types by convention here are lowercase - public readonly struct item : IEquatable - { - /// - public bool Equals(item other) => true; - - /// - public override bool Equals(object obj) => obj is item; - - /// - public override int GetHashCode() => typeof(TItem).GetHashCode(); - - /// - public override string ToString() => "(" + typeof(TItem).Name + ")"; - } - } - - /// Wraps the `T` in a named `TBox` class in a one-line declaration, - /// so the ]]> - /// is different from the ]]> - public abstract class Box : I, IEquatable> - where TBox : Box, new() - { - /// Wraps the value - public static TBox Of(T x) => new TBox { Value = x }; - - /// - public T Value { get; private set; } - - /// - public bool Equals(Box other) => - other != null && EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is Box c && Equals(c); - - // ReSharper disable once NonReadonlyMemberInGetHashCode - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value, "data("); - } - - /// Unnamed discriminated union (with Empty name), shorter name for simplified inline usage - public class U : Union { } - - /// Discriminated union - public abstract class Union - { - /// To tag the cases with enum value for efficient pattern matching of required - - /// otherwise we need to use `is CaseN` pattern or similar which is less efficient - public enum Tag : byte - { - /// Tags Case1 - Case1, - /// Tags Case2 - Case2 - } - - /// The base interface for the cases to operate. - /// The naming is selected to start from the lower letter, cause we need to use the nested type. - /// It is an unusual case, that's why using the __union__ will be fine to highlight this. - // ReSharper disable once InconsistentNaming - public interface union - { - /// The tag - Tag Tag { get; } - - /// Matches the union cases to the R value - R Match(Func map1, Func map2); - } - - /// Creates the respective case - public static union Of(T1 x) => new case1(x); - - /// Creates the respective case - public static union Of(T2 x) => new case2(x); - - /// Wraps the respective case - public readonly struct case1 : union, IEquatable, I - { - /// Implicit conversion - public static implicit operator case1(T1 x) => new case1(x); - - /// - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - /// - public R Match(Func map1, Func map2) => map1(Case); - - /// - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - /// The case value - public readonly T1 Case; - - /// Wraps the value - public case1(T1 x) => Case = x; - - /// - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is case1 x && Equals(x); - - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value); - } - - /// Wraps the respective case - public readonly struct case2 : union, IEquatable, I - { - /// Conversion - public static implicit operator case2(T2 x) => new case2(x); - - /// - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - - /// - public R Match(Func map1, Func map2) => map2(Value); - - /// - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - /// The case value - public readonly T2 Case; - - /// Wraps the value - public case2(T2 x) => Case = x; - - /// - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Value, other.Value); - - /// - public override bool Equals(object obj) => obj is case2 x && Equals(x); - - /// - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); - - /// - public override string ToString() => UnionTools.ToString(Value); - } - } - -#pragma warning disable 1591 - public class U : Union { } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public class U : Union { } - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - public static union Of(T8 x) => new case8(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case8 : union, IEquatable, I - { - public static implicit operator case8(T8 x) => new case8(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map8(Case); - - public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T8 Case; - public case8(T8 x) => Case = x; - - public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case8 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8, Case9 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - public static union Of(T8 x) => new case8(x); - public static union Of(T9 x) => new case9(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case8 : union, IEquatable, I - { - public static implicit operator case8(T8 x) => new case8(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map8(Case); - - public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T8 Case; - public case8(T8 x) => Case = x; - - public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case8 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case9 : union, IEquatable, I - { - public static implicit operator case9(T9 x) => new case9(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case9; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9) => map9(Case); - - public T9 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T9 Case; - public case9(T9 x) => Case = x; - - public bool Equals(case9 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case9 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - - public abstract class Union - { - public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8, Case9, Case10 } - - public interface union - { - Tag Tag { get; } - R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10); - } - - public static union Of(T1 x) => new case1(x); - public static union Of(T2 x) => new case2(x); - public static union Of(T3 x) => new case3(x); - public static union Of(T4 x) => new case4(x); - public static union Of(T5 x) => new case5(x); - public static union Of(T6 x) => new case6(x); - public static union Of(T7 x) => new case7(x); - public static union Of(T8 x) => new case8(x); - public static union Of(T9 x) => new case9(x); - public static union Of(T10 x) => new case10(x); - - public struct case1 : union, IEquatable, I - { - public static implicit operator case1(T1 x) => new case1(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } - - [MethodImpl((MethodImplOptions)256)] - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map1(Case); - - public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T1 Case; - public case1(T1 x) => Case = x; - - public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case1 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case2 : union, IEquatable, I - { - public static implicit operator case2(T2 x) => new case2(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map2(Case); - - public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T2 Case; - public case2(T2 x) => Case = x; - - public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case2 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case3 : union, IEquatable, I - { - public static implicit operator case3(T3 x) => new case3(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map3(Case); - - public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T3 Case; - public case3(T3 x) => Case = x; - - public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case3 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case4 : union, IEquatable, I - { - public static implicit operator case4(T4 x) => new case4(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map4(Case); - - public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T4 Case; - public case4(T4 x) => Case = x; - - public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case4 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case5 : union, IEquatable, I - { - public static implicit operator case5(T5 x) => new case5(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map5(Case); - - public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T5 Case; - public case5(T5 x) => Case = x; - - public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case5 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case6 : union, IEquatable, I - { - public static implicit operator case6(T6 x) => new case6(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map6(Case); - - public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T6 Case; - public case6(T6 x) => Case = x; - - public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case6 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case7 : union, IEquatable, I - { - public static implicit operator case7(T7 x) => new case7(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map7(Case); - - public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T7 Case; - public case7(T7 x) => Case = x; - - public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case7 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case8 : union, IEquatable, I - { - public static implicit operator case8(T8 x) => new case8(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map8(Case); - - public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T8 Case; - public case8(T8 x) => Case = x; - - public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case8 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case9 : union, IEquatable, I - { - public static implicit operator case9(T9 x) => new case9(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case9; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map9(Case); - - public T9 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T9 Case; - public case9(T9 x) => Case = x; - - public bool Equals(case9 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case9 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - - public struct case10 : union, IEquatable, I - { - public static implicit operator case10(T10 x) => new case10(x); - - public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case10; } - public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, - Func map6, Func map7, Func map8, Func map9, Func map10) => map10(Case); - - public T10 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } - - public readonly T10 Case; - public case10(T10 x) => Case = x; - - public bool Equals(case10 other) => EqualityComparer.Default.Equals(Case, other.Case); - public override bool Equals(object obj) => obj is case10 x && Equals(x); - public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); - public override string ToString() => UnionTools.ToString(Case); - } - } - -#pragma warning restore 1591 - - /// Methods to work with immutable arrays and some sugar. - public static class ArrayTools - { - private static class EmptyArray - { - public static readonly T[] Value = new T[0]; - } - - /// Returns singleton empty array of provided type. - /// Array item type. Empty array. - public static T[] Empty() => EmptyArray.Value; - - /// Wraps item in array. - public static T[] One(this T one) => new[] { one }; - - /// Returns true if array is null or have no items. Type of array item. - /// Source array to check. True if null or has no items, false otherwise. - public static bool IsNullOrEmpty(this T[] source) => source == null || source.Length == 0; - - /// Returns empty array instead of null, or source array otherwise. Type of array item. - public static T[] EmptyIfNull(this T[] source) => source ?? Empty(); - - /// Returns source enumerable if it is array, otherwise converts source to array or an empty array if null. - public static T[] ToArrayOrSelf(this IEnumerable source) => - source == null ? Empty() : (source as T[] ?? source.ToArray()); - - /// Returns source enumerable if it is list, otherwise converts source to IList or an empty array if null. - public static IList ToListOrSelf(this IEnumerable source) => - source == null ? Empty() : source as IList ?? source.ToList(); - - /// - /// Array copy - /// - public static T[] Copy(this T[] items) - { - if (items == null) - return null; - var copy = new T[items.Length]; - for (var i = 0; i < copy.Length; i++) - copy[i] = items[i]; - return copy; - } - - /// Returns new array consisting from all items from source array then all items from added array. - /// If source is null or empty, then added array will be returned. - /// If added is null or empty, then source will be returned. - /// Array item type. - /// Array with leading items. - /// Array with following items. - /// New array with items of source and added arrays. - public static T[] Append(this T[] source, params T[] added) - { - if (added == null || added.Length == 0) - return source; - if (source == null || source.Length == 0) - return added; - - var result = new T[source.Length + added.Length]; - Array.Copy(source, 0, result, 0, source.Length); - if (added.Length == 1) - result[source.Length] = added[0]; - else - Array.Copy(added, 0, result, source.Length, added.Length); - return result; - } - - /// Performant concat of enumerables in case of arrays. - /// But performance will degrade if you use Concat().Where(). - /// Type of item. - /// goes first. - /// appended to source. - /// empty array or concat of source and other. - public static T[] Append(this IEnumerable source, IEnumerable other) => - source.ToArrayOrSelf().Append(other.ToArrayOrSelf()); - - /// Returns new array with appended, - /// or at , if specified. - /// If source array could be null or empty, then single value item array will be created despite any index. - /// Array item type. - /// Array to append value to. - /// Value to append. - /// (optional) Index of value to update. - /// New array with appended or updated value. - public static T[] AppendOrUpdate(this T[] source, T value, int index = -1) - { - if (source == null || source.Length == 0) - return new[] { value }; - var sourceLength = source.Length; - index = index < 0 ? sourceLength : index; - var result = new T[index < sourceLength ? sourceLength : sourceLength + 1]; - Array.Copy(source, result, sourceLength); - result[index] = value; - return result; - } - - /// Calls predicate on each item in array until predicate returns true, - /// then method will return this item index, or if predicate returns false for each item, method will return -1. - /// Type of array items. - /// Source array: if null or empty, then method will return -1. - /// Delegate to evaluate on each array item until delegate returns true. - /// Index of item for which predicate returns true, or -1 otherwise. - public static int IndexOf(this T[] source, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (predicate(source[i])) - return i; - return -1; - } - - /// Minimizes the allocations for closure in predicate lambda with the provided - public static int IndexOf(this T[] source, S state, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (predicate(state, source[i])) - return i; - return -1; - } - - /// Looks up for item in source array equal to provided value, and returns its index, or -1 if not found. - /// Type of array items. - /// Source array: if null or empty, then method will return -1. - /// Value to look up. - /// Index of item equal to value, or -1 item is not found. - public static int IndexOf(this T[] source, T value) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (Equals(source[i], value)) - return i; - - return -1; - } - - /// The same as `IndexOf` but searching the item by reference - public static int IndexOfReference(this T[] source, T reference) where T : class - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - if (ReferenceEquals(source[i], reference)) - return i; - - return -1; - } - - /// Produces new array without item at specified . - /// Will return array if index is out of bounds, or source is null/empty. - /// Type of array item. - /// Input array. Index if item to remove. - /// New array with removed item at index, or input source array if index is not in array. - public static T[] RemoveAt(this T[] source, int index) - { - if (source == null || source.Length == 0 || index < 0 || index >= source.Length) - return source; - if (index == 0 && source.Length == 1) - return new T[0]; - var result = new T[source.Length - 1]; - if (index != 0) - Array.Copy(source, 0, result, 0, index); - if (index != result.Length) - Array.Copy(source, index + 1, result, index, result.Length - index); - return result; - } - - /// Looks for item in array using equality comparison, and returns new array with found item remove, or original array if not item found. - /// Type of array item. - /// Input array. Value to find and remove. - /// New array with value removed or original array if value is not found. - public static T[] Remove(this T[] source, T value) => - source.RemoveAt(source.IndexOf(value)); - - /// Returns first item matching the , or default item value. - /// item type - /// items collection to search - /// condition to evaluate for each item. - /// First item matching condition or default value. - public static T FindFirst(this T[] source, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - { - var item = source[i]; - if (predicate(item)) - return item; - } - - return default(T); - } - - /// Version of FindFirst with the fixed state used by predicate to prevent allocations by predicate lambda closure - public static T FindFirst(this T[] source, S state, Func predicate) - { - if (source != null && source.Length != 0) - for (var i = 0; i < source.Length; ++i) - { - var item = source[i]; - if (predicate(state, item)) - return item; - } - - return default(T); - } - - /// Returns first item matching the , or default item value. - /// item type - /// items collection to search - /// condition to evaluate for each item. - /// First item matching condition or default value. - public static T FindFirst(this IEnumerable source, Func predicate) => - source is T[] sourceArr ? sourceArr.FindFirst(predicate) : source.FirstOrDefault(predicate); - - /// Returns element if collection consist on single element, otherwise returns default value. - /// It does not throw for collection with many elements - public static T SingleOrDefaultIfMany(this IEnumerable source) - { - if (source is IList list) - return list.Count == 1 ? list[0] : default(T); - - if (source == null) - return default(T); - - using (var e = source.GetEnumerator()) - { - if (!e.MoveNext()) - return default(T); - var it = e.Current; - return !e.MoveNext() ? it : default(T); - } - } - - /// Does for each item - public static void ForEach(this T[] source, Action action) - { - if (!source.IsNullOrEmpty()) - for (var i = 0; i < source.Length; i++) - action(source[i]); - } - - /// Appends source to results - public static T[] AppendTo(T[] source, int sourcePos, int count, T[] results = null) - { - if (results == null) - { - var newResults = new T[count]; - if (count == 1) - newResults[0] = source[sourcePos]; - else - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = source[j]; - return newResults; - } - - var matchCount = results.Length; - var appendedResults = new T[matchCount + count]; - if (matchCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, matchCount); - - if (count == 1) - appendedResults[matchCount] = source[sourcePos]; - else - Array.Copy(source, sourcePos, appendedResults, matchCount, count); - - return appendedResults; - } - - private static R[] AppendTo(T[] source, int sourcePos, int count, Func map, R[] results = null) - { - if (results == null || results.Length == 0) - { - var newResults = new R[count]; - if (count == 1) - newResults[0] = map(source[sourcePos]); - else - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = map(source[j]); - return newResults; - } - - var oldResultsCount = results.Length; - var appendedResults = new R[oldResultsCount + count]; - if (oldResultsCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, oldResultsCount); - - if (count == 1) - appendedResults[oldResultsCount] = map(source[sourcePos]); - else - { - for (int i = oldResultsCount, j = sourcePos; i < appendedResults.Length; ++i, ++j) - appendedResults[i] = map(source[j]); - } - - return appendedResults; - } - - private static R[] AppendTo(T[] source, S state, int sourcePos, int count, Func map, R[] results = null) - { - if (results == null || results.Length == 0) - { - var newResults = new R[count]; - if (count == 1) - newResults[0] = map(state, source[sourcePos]); - else - for (int i = 0, j = sourcePos; i < count; ++i, ++j) - newResults[i] = map(state, source[j]); - return newResults; - } - - var oldResultsCount = results.Length; - var appendedResults = new R[oldResultsCount + count]; - if (oldResultsCount == 1) - appendedResults[0] = results[0]; - else - Array.Copy(results, 0, appendedResults, 0, oldResultsCount); - - if (count == 1) - appendedResults[oldResultsCount] = map(state, source[sourcePos]); - else - { - for (int i = oldResultsCount, j = sourcePos; i < appendedResults.Length; ++i, ++j) - appendedResults[i] = map(state, source[j]); - } - - return appendedResults; - } - - /// Where method similar to Enumerable.Where but more performant and non necessary allocating. - /// It returns source array and does Not create new one if all items match the condition. - /// Type of source items. - /// If null, the null will be returned. - /// Condition to keep items. - /// New array if some items are filter out. Empty array if all items are filtered out. Original array otherwise. - public static T[] Match(this T[] source, Func condition) - { - if (source == null || source.Length == 0) - return source; - - if (source.Length == 1) - return condition(source[0]) ? source : Empty(); - - if (source.Length == 2) - { - var condition0 = condition(source[0]); - var condition1 = condition(source[1]); - return condition0 && condition1 ? new[] { source[0], source[1] } - : condition0 ? new[] { source[0] } - : condition1 ? new[] { source[1] } - : Empty(); - } - - var matchStart = 0; - T[] matches = null; - var matchFound = false; - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, matches); - - return matches ?? (matchStart != 0 ? Empty() : source); - } - - /// The same as `Match` but assumes that is not null and not empty - public static T[] MatchUnsafe(this T[] source, Func condition) - { - var matchStart = 0; - T[] matches = null; - var matchFound = false; - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, matches); - - return matches ?? (matchStart != 0 ? Empty() : source); - } - - /// Match with the additional state to use in to minimize the allocations in lambda closure - public static T[] Match(this T[] source, S state, Func condition) - { - if (source == null || source.Length == 0) - return source; - - if (source.Length == 1) - return condition(state, source[0]) ? source : Empty(); - - if (source.Length == 2) - { - var condition0 = condition(state, source[0]); - var condition1 = condition(state, source[1]); - return condition0 && condition1 ? new[] { source[0], source[1] } - : condition0 ? new[] { source[0] } - : condition1 ? new[] { source[1] } - : Empty(); - } - - var matchStart = 0; - T[] matches = null; - var matchFound = false; - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(state, source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, matches); - - return matches ?? (matchStart != 0 ? Empty() : source); - } - - /// Where method similar to Enumerable.Where but more performant and non necessary allocating. - /// It returns source array and does Not create new one if all items match the condition. - /// Type of source items. Type of result items. - /// If null, the null will be returned. - /// Condition to keep items. Converter from source to result item. - /// New array of result items. - public static R[] Match(this T[] source, Func condition, Func map) - { - if (source == null) - return null; - - if (source.Length == 0) - return Empty(); - - if (source.Length == 1) - { - var item = source[0]; - return condition(item) ? new[] { map(item) } : Empty(); - } - - if (source.Length == 2) - { - var condition0 = condition(source[0]); - var condition1 = condition(source[1]); - return condition0 && condition1 ? new[] { map(source[0]), map(source[1]) } - : condition0 ? new[] { map(source[0]) } - : condition1 ? new[] { map(source[1]) } - : Empty(); - } - - var matchStart = 0; - R[] matches = null; - var matchFound = false; - - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, matchStart, i - matchStart, map, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, matchStart, i - matchStart, map, matches); - - return matches ?? (matchStart == 0 ? AppendTo(source, 0, source.Length, map) : Empty()); - } - - /// Match with the additional state to use in and to minimize the allocations in lambda closure - public static R[] Match(this T[] source, S state, Func condition, Func map) - { - if (source == null) - return null; - - if (source.Length == 0) - return Empty(); - - if (source.Length == 1) - { - var item = source[0]; - return condition(state, item) ? new[] { map(state, item) } : Empty(); - } - - if (source.Length == 2) - { - var condition0 = condition(state, source[0]); - var condition1 = condition(state, source[1]); - return condition0 && condition1 ? new[] { map(state, source[0]), map(state, source[1]) } - : condition0 ? new[] { map(state, source[0]) } - : condition1 ? new[] { map(state, source[1]) } - : Empty(); - } - - var matchStart = 0; - R[] matches = null; - var matchFound = false; - - var i = 0; - for (; i < source.Length; ++i) - if (!(matchFound = condition(state, source[i]))) - { - // for accumulated matched items - if (i != 0 && i > matchStart) - matches = AppendTo(source, state, matchStart, i - matchStart, map, matches); - matchStart = i + 1; // guess the next match start will be after the non-matched item - } - - // when last match was found but not all items are matched (hence matchStart != 0) - if (matchFound && matchStart != 0) - return AppendTo(source, state, matchStart, i - matchStart, map, matches); - - return matches ?? (matchStart == 0 ? AppendTo(source, state, 0, source.Length, map) : Empty()); - } - - /// Maps all items from source to result array. - /// Source item type Result item type - /// Source items Function to convert item from source to result. - /// Converted items - public static R[] Map(this T[] source, Func map) - { - if (source == null) - return null; - - var sourceCount = source.Length; - if (sourceCount == 0) - return Empty(); - - if (sourceCount == 1) - return new[] { map(source[0]) }; - - if (sourceCount == 2) - return new[] { map(source[0]), map(source[1]) }; - - var results = new R[sourceCount]; - for (var i = 0; i < source.Length; i++) - results[i] = map(source[i]); - return results; - } - - /// Map with additional state to use in to minimize allocations in lambda closure - public static R[] Map(this T[] source, S state, Func map) - { - if (source == null) - return null; - - var sourceCount = source.Length; - if (sourceCount == 0) - return Empty(); - - if (sourceCount == 1) - return new[] { map(state, source[0]) }; - - if (sourceCount == 2) - return new[] { map(state, source[0]), map(state, source[1]) }; - - var results = new R[sourceCount]; - for (var i = 0; i < source.Length; i++) - results[i] = map(state, source[i]); - return results; - } - - /// Maps all items from source to result collection. - /// If possible uses fast array Map otherwise Enumerable.Select. - /// Source item type Result item type - /// Source items Function to convert item from source to result. - /// Converted items - public static IEnumerable Map(this IEnumerable source, Func map) => - source is T[] arr ? arr.Map(map) : source?.Select(map); - - /// If is array uses more effective Match for array, otherwise just calls Where - /// Type of source items. - /// If null, the null will be returned. - /// Condition to keep items. - /// Result items, may be an array. - public static IEnumerable Match(this IEnumerable source, Func condition) => - source is T[] arr ? arr.Match(condition) : source?.Where(condition); - - /// If is array uses more effective Match for array, - /// otherwise just calls Where, Select - /// Type of source items. Type of result items. - /// If null, the null will be returned. - /// Condition to keep items. Converter from source to result item. - /// Result items, may be an array. - public static IEnumerable Match(this IEnumerable source, Func condition, Func map) => - source is T[] arr ? arr.Match(condition, map) : source?.Where(condition).Select(map); - } - - /// Wrapper that provides optimistic-concurrency Swap operation implemented using . - /// Type of object to wrap. - public sealed class Ref where T : class - { - /// Gets the wrapped value. - public T Value => _value; - private T _value; - - /// Creates ref to object, optionally with initial value provided. - /// (optional) Initial value. - public Ref(T initialValue = default) => _value = initialValue; - - /// Exchanges currently hold object with - see for details. - /// Delegate to produce new object value from current one passed as parameter. - /// Returns old object value the same way as - /// Important: May be called multiple times to retry update with value concurrently changed by other code. - public T Swap(Func getNewValue) => - Ref.Swap(ref _value, getNewValue); - - // todo: A good candidate to implement - // The same as `Swap` but instead of old known value it returns the new one - //public T SwapAndGetNewValue(Func getNewValue) => - // Ref.Swap(ref _value, getNewValue); - - /// Option without allocation for capturing `a` in closure of `getNewValue` - public T Swap(A a, Func getNewValue) => Ref.Swap(ref _value, a, getNewValue); - - /// Option without allocation for capturing `a` and `b` in closure of `getNewValue` - public T Swap(A a, B b, Func getNewValue) => Ref.Swap(ref _value, a, b, getNewValue); - - /// Just sets new value ignoring any intermingled changes and returns the original value - /// old value - public T Swap(T newValue) => Interlocked.Exchange(ref _value, newValue); - - /// Directly sets the value and returns the new value - public T SetNonAtomic(T newValue) => _value = newValue; - - /// Compares current Referred value with and if equal replaces current with - /// - /// True if current value was replaced with new value, and false if current value is outdated (already changed by other party). - /// [!CDATA[ - /// var value = SomeRef.Value; - /// if (!SomeRef.TrySwapIfStillCurrent(value, Update(value)) - /// SomeRef.Swap(v => Update(v)); // fallback to normal Swap with delegate allocation - /// ]] - public bool TrySwapIfStillCurrent(T currentValue, T newValue) => - Interlocked.CompareExchange(ref _value, newValue, currentValue) == currentValue; - } - - /// Provides optimistic-concurrency consistent operation. - public static class Ref - { - /// The default max retry count - can be overriden by `Swap` optional parameter - public const int RETRY_COUNT_UNTIL_THROW = 50; - - /// Factory for with type of value inference. - /// Type of value to wrap. - /// Initial value to wrap. - /// New ref. - public static Ref Of(T value) where T : class => new Ref(value); - - /// Creates new ref to the value of original ref. Ref value type. - /// Original ref. New ref to original value. - public static Ref NewRef(this Ref original) where T : class => Of(original.Value); - - /// First, it evaluates new value using function. - /// Second, it checks that original value is not changed. - /// If it is changed it will retry first step, otherwise it assigns new value and returns original (the one used for ). - /// Type of value to swap. - /// Reference to change to new value - /// Delegate to get value from old one. - /// (optional) - /// Old/original value. By analogy with . - /// Important: May be called multiple times to retry update with value concurrently changed by other code. - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - private static void ThrowRetryCountExceeded(int retryCountExceeded) => - throw new InvalidOperationException( - $"Ref retried to Update for {retryCountExceeded} times But there is always someone else intervened."); - - /// Swap with the additional state required for the delegate . - /// May prevent closure creation for the delegate - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, A a, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue, a); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - /// Swap with the additional state , required for the delegate . - /// May prevent closure creation for the delegate - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, A a, B b, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue, a, b); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); - -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - /// Swap with the additional state , , required for the delegate . - /// May prevent closure creation for the delegate - [MethodImpl((MethodImplOptions)256)] - public static T Swap(ref T value, A a, B b, C c, Func getNewValue, - int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - where T : class - { -#if SUPPORTS_SPIN_WAIT - var spinWait = new SpinWait(); -#endif - var retryCount = 0; - while (true) - { - var oldValue = value; - var newValue = getNewValue(oldValue, a, b, c); - if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - return oldValue; - - if (++retryCount > retryCountUntilThrow) - ThrowRetryCountExceeded(retryCountUntilThrow); - -#if SUPPORTS_SPIN_WAIT - spinWait.SpinOnce(); -#endif - } - } - - // todo: Func of 5 args is not available on all plats - // /// Option without allocation for capturing `a`, `b`, `c`, `d` in closure of `getNewValue` - // [MethodImpl((MethodImplOptions)256)] - // public static T Swap(ref T value, A a, B b, C c, D d, Func getNewValue, - // int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) - // where T : class - // { - //#if SUPPORTS_SPIN_WAIT - // var spinWait = new SpinWait(); - //#endif - // var retryCount = 0; - // while (true) - // { - // var oldValue = value; - // var newValue = getNewValue(oldValue, a, b, c, d); - // if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) - // return oldValue; - - // if (++retryCount > retryCountUntilThrow) - // ThrowRetryCountExceeded(retryCountUntilThrow); - - //#if SUPPORTS_SPIN_WAIT - // spinWait.SpinOnce(); - //#endif - // } - // } - } - - /// Printable thing via provided printer - public interface IPrintable - { - /// Print to the provided string builder via the provided printer. - StringBuilder Print(StringBuilder s, Func printer); - } - - /// Produces good enough hash codes for the fields - public static class Hasher - { - /// Combines hashes of two fields - public static int Combine(T1 a, T2 b) => - Combine(a?.GetHashCode() ?? 0, b?.GetHashCode() ?? 0); - - /// Inspired by System.Tuple.CombineHashCodes - public static int Combine(int h1, int h2) - { - if (h1 == 0) return h2; - unchecked - { - return (h1 << 5) + h1 ^ h2; - } - } - } - - /// Simple unbounded object pool - public sealed class StackPool where T : class - { - /// Give me an object - [MethodImpl((MethodImplOptions)256)] - public T RentOrDefault() => - Interlocked.Exchange(ref _s, _s?.Tail)?.Head; - - /// Give it back - [MethodImpl((MethodImplOptions)256)] - public void Return(T x) => - Interlocked.Exchange(ref _s, new Stack(x, _s)); - - private Stack _s; - - private sealed class Stack - { - public readonly T Head; - public readonly Stack Tail; - public Stack(T h, Stack t) - { - Head = h; - Tail = t; - } - } - } - - /// Immutable Key-Value pair. It is reference type (could be check for null), - /// which is different from System value type . - /// In addition provides and implementations. - /// Type of Key.Type of Value. - public class KV : IPrintable - { - /// Key. - public readonly K Key; - - /// Value. - public readonly V Value; - - /// Creates Key-Value object by providing key and value. Does Not check either one for null. - /// key.value. - public KV(K key, V value) - { - Key = key; - Value = value; - } - - /// - public StringBuilder Print(StringBuilder s, Func printer) => - s.Append("(").To(b => Key == null ? b : printer(b, Key)) - .Append(", ").To(b => Value == null ? b : printer(b, Value)) - .Append(')'); - - /// Creates nice string view.String representation. - public override string ToString() => - Print(new StringBuilder(), (s, x) => s.Append(x)).ToString(); - - /// Returns true if both key and value are equal to corresponding key-value of other object. - public override bool Equals(object obj) - { - var other = obj as KV; - return other != null - && (ReferenceEquals(other.Key, Key) || Equals(other.Key, Key)) - && (ReferenceEquals(other.Value, Value) || Equals(other.Value, Value)); - } - - /// Combines key and value hash code - public override int GetHashCode() => Hasher.Combine(Key, Value); - } - - /// Helpers for . - public static class KV - { - /// Creates the key value pair. - public static KV Of(K key, V value) => new KV(key, value); - - /// Creates the pair with the new value - public static KV WithValue(this KV kv, V value) => new KV(kv.Key, value); - } - - /// Simple helper for creation of the pair of two parts. - public static class KeyValuePair - { - /// Pairs key with value. - public static KeyValuePair Pair(this K key, V value) => new KeyValuePair(key, value); - } - - /// Helper structure which allows to distinguish null value from the default value for optional parameter. - public struct Opt - { - /// Allows to transparently convert parameter argument to opt structure. - public static implicit operator Opt(T value) => new Opt(value); - - /// Argument value. - public readonly T Value; - - /// Indicates that value is provided. - public readonly bool HasValue; - - /// Wraps passed value in structure. Sets the flag that value is present. - public Opt(T value) - { - HasValue = true; - Value = value; - } - - /// Helper to get value or default value if value is not present. - public T OrDefault(T defaultValue = default) => HasValue ? Value : defaultValue; - } - - /// Ever growing list - public struct GrowingList - { - /// Default initial capacity - public const int DefaultInitialCapacity = 2; - - /// The items array - public T[] Items; - - /// The count - public int Count; - - /// Constructs the thing - public GrowingList(T[] items, int count = 0) - { - Items = items; - Count = count; - } - - /// Push the new slot and return the ref to it - public ref T PushSlot() - { - if (Items == null) - Items = new T[DefaultInitialCapacity]; - else if (Count >= Items.Length) - Items = Expand(Items); - return ref Items[Count++]; - } - - /// Adds the new item possibly extending the item collection - public void PushSlot(T item) - { - if (Items == null) - Items = new T[DefaultInitialCapacity]; - else if (Count >= Items.Length) - Items = Expand(Items); - Items[Count++] = item; - } - - /// Pops the item - just moving the counter back - public void Pop() => --Count; - - /// Expands the items starting with 2 - private static T[] Expand(T[] items) - { - var count = items.Length; - var newItems = new T[count << 1]; // count x 2 - Array.Copy(items, 0, newItems, 0, count); - return newItems; - } - - /// - public override string ToString() => - $"Count {Count} of {(Count == 0 || Items == null || Items.Length == 0 ? "empty" : "first (" + Items[0] + ") and last (" + Items[Count - 1] + ")")}"; - } - - /// Immutable list - simplest linked list with the Head and the Tail. - public sealed class ImList - { - /// Empty list to Push to. - public static readonly ImList Empty = new ImList(); - - /// True for empty list. - public bool IsEmpty => Tail == null; - - /// First value in a list. - public readonly T Head; - - /// The rest of values or Empty if list has a single value. - public readonly ImList Tail; - - /// Prepends new value and returns new list. - public ImList Push(T head) => new ImList(head, this); - - /// Enumerates the list. - public IEnumerable Enumerate() - { - if (IsEmpty) - yield break; - for (var list = this; !list.IsEmpty; list = list.Tail) - yield return list.Head; - } - - /// String representation for debugging purposes - public override string ToString() => IsEmpty - ? "[]" : Tail.IsEmpty - ? "[" + Head + "]" : Tail.Tail.IsEmpty - ? "[" + Head + "," + Tail.Head + "]" : Tail.Tail.Tail.IsEmpty - ? "[" + Head + "," + Tail.Head + "," + Tail.Tail.Head + "]" - : "[" + Head + "," + Tail.Head + "," + Tail.Tail.Head + ", ...]"; - - private ImList() { } - - private ImList(T head, ImList tail) - { - Head = head; - Tail = tail; - } - } - - /// Extension methods providing basic operations on a list. - public static class ImList - { - /// Split list into (Head, Tail, IsEmpty) tuple - public static void Deconstruct(this ImList list, out T head, out ImList tail, out bool isEmpty) - { - head = list.Head; - tail = list.Tail; - isEmpty = list.IsEmpty; - } - - /// - /// Constructs the reversed list from the parameter array of items - /// - public static ImList List(params T[] items) - { - var l = ImList.Empty; - if (items != null) - for (var i = items.Length - 1; i >= 0; --i) - l = l.Push(items[i]); - return l; - } - - /// - /// Constructs the list as the reversed input list - /// - public static ImList ToImList(this IList source) - { - var l = ImList.Empty; - if (source != null) - for (var i = source.Count - 1; i >= 0; --i) - l = l.Push(source[i]); - return l; - } - - /// - /// Constructs the list as the reversed enumerable - /// - public static ImList ToImList(this IEnumerable source) - { - if (source is IList list) - return list.ToImList(); - var l = ImList.Empty; - - if (source != null) - foreach (var item in source) - l = l.Push(item); - return l.Reverse(); - } - - /// Constructs list of one element - public static ImList List(this T head) => ImList.Empty.Push(head); - - /// Constructs list from head and tail - public static ImList List(this T head, ImList tail) => tail.Push(head); - - /// Apples some effect action to each element - public static void ForEach(this ImList list, Action effect) - { - for (; !list.IsEmpty; list = list.Tail) - effect(list.Head); - } - - /// Fold list to a single value. The respective name for it in LINQ is Aggregate - public static S Fold(this ImList list, S state, Func reduce) - { - if (list.IsEmpty) - return state; - var result = state; - for (; !list.IsEmpty; list = list.Tail) - result = reduce(list.Head, result); - return result; - } - - /// Fold list to a single value with index of item. The respective name for it in LINQ is Aggregate. - public static S Fold(this ImList list, S state, Func reduce) - { - if (list.IsEmpty) - return state; - var result = state; - for (var i = 0; !list.IsEmpty; list = list.Tail, ++i) - result = reduce(list.Head, i, result); - return result; - } - - /// Returns new list in reverse order. - public static ImList Reverse(this ImList list) - { - if (list.IsEmpty || list.Tail.IsEmpty) - return list; - var reversed = ImList.Empty; - for (; !list.IsEmpty; list = list.Tail) - reversed = reversed.Push(list.Head); - return reversed; - } - - /// Maps the items from the first list to the result list. - public static ImList Map(this ImList list, Func map) => - list.Fold(ImList.Empty, (x, r) => List(map(x), r)).Reverse(); - - /// Maps with index - public static ImList Map(this ImList list, Func map) => - list.Fold(ImList.Empty, (x, i, r) => List(map(x, i), r)).Reverse(); - - /// Copies list to array. - public static T[] ToArray(this ImList source) => - source.IsEmpty ? ArrayTools.Empty() - : source.Tail.IsEmpty ? new[] { source.Head } : source.Enumerate().ToArray(); - } - - /// Zipper is an immutable persistent data structure, to represent collection with single focused (selected, active) element. - /// Consist of REVERSED `Left` immutable list, `Focus` element, and the `Right` immutable list. That's why a Zipper name, - /// where left and right part are joined / zipped in focus item. - public sealed class ImZipper - { - /// Empty singleton instance to start building your zipper - public static readonly ImZipper Empty = new ImZipper(); - - /// True is zipper does not contain items - public bool IsEmpty => Count == 0; - - /// Index of Focus item, from `0` to `Count-1` - public readonly int Index; - - /// Number of items - public readonly int Count; - - /// Left REVERSED list, so the Head of the list is just prior the Focus item - public readonly ImList Left; - - /// Right list, where Head is just after the Focus item - public readonly ImList Right; - - /// Single focus item - public readonly T Focus; - - /// - public override string ToString() => - IsEmpty ? "[||]" : Count + ":" + Left.Reverse() + "|" + Index + ":" + Focus + "|" + Right; - - /// Sets a new focus and pushes the old focus to the Left list. - public ImZipper Append(T focus) => PushLeft(focus); - - /// Sets a new focus and pushes the old focus to the Left list. - public ImZipper PushLeft(T focus) => - IsEmpty ? new ImZipper(ImList.Empty, focus, 0, ImList.Empty, 1) - : new ImZipper(Left.Push(Focus), focus, Index + 1, Right, Count + 1); - - /// Sets a new focus and pushes the old focus to the right list. - public ImZipper Insert(T focus) => PushRight(focus); - - /// Sets a new focus and pushes the old focus to the right list. - public ImZipper PushRight(T focus) => - IsEmpty ? new ImZipper(ImList.Empty, focus, 0, ImList.Empty, 1) - : new ImZipper(Left, focus, Index, Right.Push(Focus), Count + 1); - - /// Removes a focus, filling the hole with the item from the left list, or from the right if the left is empty - public ImZipper PopLeft() => - IsEmpty ? this - : Left.IsEmpty && Right.IsEmpty ? Empty - : !Left.IsEmpty ? new ImZipper(Left.Tail, Left.Head, Index - 1, Right, Count - 1) - : new ImZipper(Left, Right.Head, Index, Right.Tail, Count - 1); - - /// Removes a focus, filling the hole with the item from the right list, or from the left if the right is empty - public ImZipper PopRight() => - IsEmpty ? this - : Left.IsEmpty && Right.IsEmpty ? Empty - : !Right.IsEmpty ? new ImZipper(Left, Right.Head, Index, Right.Tail, Count - 1) - : new ImZipper(Left.Tail, Left.Head, Index - 1, Right, Count - 1); - - /// Shifts focus one element to the left (decrementing its Index). - public ImZipper ShiftLeft() => - IsEmpty || Left.IsEmpty ? this - : new ImZipper(Left.Tail, Left.Head, Index - 1, Right.Push(Focus), Count); - - /// Shifts focus one element to the right (incrementing its Index). - public ImZipper ShiftRight() => - IsEmpty || Right.IsEmpty ? this - : new ImZipper(Left.Push(Focus), Right.Head, Index + 1, Right.Tail, Count); - - /// Sets a new focus and returns a new zipper with the left and right lists unchanged - public ImZipper WithFocus(T focus) => - IsEmpty ? this : new ImZipper(Left, focus, Index, Right, Count); - - /// Maps over the zipper items producing a new zipper - public ImZipper Map(Func map) => - IsEmpty ? ImZipper.Empty - : new ImZipper(Left.Reverse().Fold(ImList.Empty, (x, r) => r.Push(map(x))), - map(Focus), Index, Right.Map(map), Count); - - /// Maps over the zipper items with item index, producing a new zipper - public ImZipper Map(Func map) => - IsEmpty ? ImZipper.Empty - : new ImZipper( - Left.Reverse().Fold(ImList.Empty, (x, i, r) => r.Push(map(x, i))), - map(Focus, Index), Index, Right.Map((x, i) => map(x, Index + 1 + i)), Count); - - private ImZipper() => Index = -1; - - private ImZipper(ImList left, T focus, int index, ImList right, int count) - { - Left = left; - Focus = focus; - Index = index; - Right = right; - Count = count; - } - } - - /// Other ImZipper methods - public static class ImZipper - { - /// Appends array items to zipper - public static ImZipper Zip(params T[] items) - { - if (items.IsNullOrEmpty()) - return ImZipper.Empty; - var z = ImZipper.Empty; - for (var i = 0; i < items.Length; ++i) - z = z.PushLeft(items[i]); - return z; - } - - /// Converts to array. - public static T[] ToArray(this ImZipper z) - { - if (z.IsEmpty) - return ArrayTools.Empty(); - var a = new T[z.Count]; - z.Fold(a, (x, i, xs) => - { - xs[i] = x; - return xs; - }); - return a; - } - - /// Shifts focus to a specified index, e.g. a random access - public static ImZipper ShiftTo(this ImZipper z, int i) - { - if (i < 0 || i >= z.Count || i == z.Index) - return z; - while (i < z.Index) - z = z.ShiftLeft(); - while (i > z.Index) - z = z.ShiftRight(); - return z; - } - - /// Updates a focus element if it is present, otherwise does nothing. - /// If the focus item is the equal one, then returns the same zipper back. - public static ImZipper Update(this ImZipper z, Func update) - { - if (z.IsEmpty) - return z; - var result = update(z.Focus); - if (ReferenceEquals(z.Focus, result) || result != null && result.Equals(z.Focus)) - return z; - return z.WithFocus(result); - } - - /// Update the item at random index, by shifting and updating it - public static ImZipper UpdateAt(this ImZipper z, int i, Func update) => - i < 0 || i >= z.Count ? z : z.ShiftTo(i).Update(update); - - /// Update the item at random index, by shifting and updating it - public static ImZipper RemoveAt(this ImZipper z, int i) => - i < 0 || i >= z.Count ? z : z.ShiftTo(i).PopLeft(); - - /// Folds zipper to a single value - public static S Fold(this ImZipper z, S state, Func reduce) => - z.IsEmpty ? state : - z.Right.Fold(reduce(z.Focus, z.Left.Reverse().Fold(state, reduce)), reduce); - - /// Folds zipper to a single value by using an item index - public static S Fold(this ImZipper z, S state, Func reduce) - { - if (z.IsEmpty) - return state; - var focusIndex = z.Index; - var reducedLeft = z.Left.Reverse().Fold(state, reduce); - return z.Right.Fold(reduce(z.Focus, focusIndex, reducedLeft), - (x, i, r) => reduce(x, focusIndex + i + 1, r)); - } - - /// Apply some effect action on each element - public static void ForEach(this ImZipper z, Action effect) - { - if (!z.IsEmpty) - { - if (!z.Left.IsEmpty) - z.Left.Reverse().ForEach(effect); - effect(z.Focus); - if (!z.Right.IsEmpty) - z.Right.ForEach(effect); - } - } - } - - /// Given the old value should and the new value should return result updated value. - public delegate V Update(V oldValue, V newValue); - - /// Update handler including the key - public delegate V Update(K key, V oldValue, V newValue); - - /// - /// Fold reducer. Designed as a alternative to `Func{V, S, S}` but with possibility of inlining on the call side. - /// Note: To get the advantage of inlining the can the interface should be implemented and passed as a NON-GENERIC STRUCT - /// - public interface IFoldReducer - { - /// Reduce method - S Reduce(T x, S state); - } - - /// - /// Immutable http://en.wikipedia.org/wiki/AVL_tree with integer keys and values. - /// - public class ImMap - { - /// Empty tree to start with. - public static readonly ImMap Empty = new ImMap(); - - /// Returns true if tree is empty. - public bool IsEmpty => this == Empty; - - /// Prevents multiple creation of an empty tree - protected ImMap() { } - - /// Height of the longest sub-tree/branch - 0 for the empty tree - public virtual int Height => 0; - - /// Prints "empty" - public override string ToString() => "empty"; - } - - /// Wraps the stored data with "fixed" reference semantics - when added to the tree it did not change or reconstructed in memory - public sealed class ImMapEntry : ImMap - { - /// - public override int Height => 1; - - /// The Key is basically the hash, or the Height for ImMapTree - public readonly int Key; - - /// The value - may be modified if you need a Ref{V} semantics - public V Value; - - /// Constructs the entry with the default value - public ImMapEntry(int key) => Key = key; - - /// Constructs the entry - public ImMapEntry(int key, V value) - { - Key = key; - Value = value; - } - - /// Prints the key value pair - public override string ToString() => Key + ":" + Value; - } - - /// - /// The two level - two node tree with either left or right - /// - public sealed class ImMapBranch : ImMap - { - /// Always two - public override int Height => 2; - - /// Contains the once created data node - public readonly ImMapEntry Entry; - - /// Left sub-tree/branch, or empty. - public ImMapEntry RightEntry; - - /// Constructor - public ImMapBranch(ImMapEntry entry, ImMapEntry rightEntry) - { - Entry = entry; - RightEntry = rightEntry; - } - - /// Prints the key value pair - public override string ToString() => "h2:" + Entry + "->" + RightEntry; - } - - /// - /// The tree always contains Left and Right node, for the missing leaf we have - /// - public sealed class ImMapTree : ImMap - { - /// Starts from 2 - public override int Height => TreeHeight; - - /// Starts from 2 - allows to access the field directly when you know it is a Tree - public int TreeHeight; - - /// Contains the once created data node - public readonly ImMapEntry Entry; - - /// Left sub-tree/branch, or empty. - public ImMap Left; - - /// Right sub-tree/branch, or empty.md - public ImMap Right; - - internal ImMapTree(ImMapEntry entry, ImMap left, ImMap right, int height) - { - Entry = entry; - Left = left; - Right = right; - TreeHeight = height; - } - - internal ImMapTree(ImMapEntry entry, ImMapEntry leftEntry, ImMapEntry rightEntry) - { - Entry = entry; - Left = leftEntry; - Right = rightEntry; - TreeHeight = 2; - } - - /// Outputs the brief tree info - mostly for debugging purposes - public override string ToString() => - "h" + Height + ":" + Entry - + "->(" + (Left is ImMapTree leftTree ? "h" + leftTree.TreeHeight + ":" + leftTree.Entry : "" + Left) - + ", " + (Right is ImMapTree rightTree ? "h" + rightTree.TreeHeight + ":" + rightTree.Entry : "" + Right) - + ")"; - - /// Adds or updates the left or right branch - public ImMapTree AddOrUpdateLeftOrRightEntry(int key, ImMapEntry entry) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(entry, leftTree.Left, leftTree.Right, leftTree.TreeHeight), - Right, TreeHeight); - - var newLeftTree = leftTree.AddOrUpdateLeftOrRightEntry(key, entry); - return newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), - Right, TreeHeight); - - if (key > leftBranch.Entry.Key) - { - var newLeft = - // 5 5 - // 2 ? => 3 ? - // 3 2 4 - // 4 - key > leftBranch.RightEntry.Key ? new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry) - // 5 5 - // 2 ? => 2.5 ? - // 3 2 3 - // 2.5 - : key < leftBranch.RightEntry.Key ? new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry) - : (ImMap)new ImMapBranch(leftBranch.Entry, entry); - - return new ImMapTree(Entry, newLeft, Right, TreeHeight); - } - - return new ImMapTree(Entry, - new ImMapBranch(entry, leftBranch.RightEntry), Right, TreeHeight); - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) - : key < leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3) - : new ImMapTree(Entry, entry, Right, TreeHeight); - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(entry, rightTree.Left, rightTree.Right, rightTree.TreeHeight), - TreeHeight); - - var newRightTree = rightTree.AddOrUpdateLeftOrRightEntry(key, entry); - return newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.Entry.Key) - { - var newRight = - // 5 5 - // ? 6 => ? 8 - // 8 6 ! - // ! - key > rightBranch.RightEntry.Key ? new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry) - // 5 5 - // ? 6 => ? 7 - // 8 6 8 - // 7 - : key < rightBranch.RightEntry.Key ? new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry) - : (ImMap)new ImMapBranch(rightBranch.Entry, entry); - - return new ImMapTree(Entry, Left, newRight, TreeHeight); - } - - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), - TreeHeight); - - return new ImMapTree(Entry, Left, new ImMapBranch(entry, rightBranch.RightEntry), TreeHeight); - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3) - : new ImMapTree(Entry, Left, entry, TreeHeight); - } - } - - /// Adds the left or right branch - public ImMapTree AddUnsafeLeftOrRightEntry(int key, ImMapEntry entry) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - var newLeftTree = leftTree.AddUnsafeLeftOrRightEntry(key, entry); - return newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), - Right, TreeHeight); - - var newLeft = key > leftBranch.RightEntry.Key - ? new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry) - : new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry); - - return new ImMapTree(Entry, newLeft, Right, TreeHeight); - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) - : new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3); - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - var newRightTree = rightTree.AddUnsafeLeftOrRightEntry(key, entry); - return newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.Entry.Key) - { - var newRight = key > rightBranch.RightEntry.Key - ? new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry) - : new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry); - - return new ImMapTree(Entry, Left, newRight, TreeHeight); - } - - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), - TreeHeight); - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) - : new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3); - } - } - - /// Adds to the left or right branch, or keeps the un-modified map - public ImMapTree AddOrKeepLeftOrRight(int key, V value) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return this; - - var newLeftTree = leftTree.AddOrKeepLeftOrRight(key, value); - return newLeftTree == leftTree ? this - : newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, new ImMapEntry(key, value), leftBranch.RightEntry), - Right, TreeHeight); - if (key > leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, new ImMapEntry(key, value)), - Right, TreeHeight); - if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(new ImMapEntry(key, value), leftBranch.Entry, leftBranch.RightEntry), - Right, TreeHeight); - return this; - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, new ImMapEntry(key, value)), Right, 3) - : key < leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(new ImMapEntry(key, value), leftLeaf), Right, 3) - : this; - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return this; - - var newRightTree = rightTree.AddOrKeepLeftOrRight(key, value); - return newRightTree == rightTree ? this - : newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, new ImMapEntry(key, value)), - TreeHeight); - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, new ImMapEntry(key, value), rightBranch.RightEntry), - TreeHeight); - if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(new ImMapEntry(key, value), rightBranch.Entry, rightBranch.RightEntry), - TreeHeight); - return this; - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, new ImMapEntry(key, value)), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(new ImMapEntry(key, value), rightLeaf), 3) - : this; - } - } - - /// Adds to the left or right branch, or keeps the un-modified map - public ImMapTree AddOrKeepLeftOrRight(int key) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return this; - - var newLeftTree = leftTree.AddOrKeepLeftOrRight(key); - return newLeftTree == leftTree ? this - : newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, new ImMapEntry(key), leftBranch.RightEntry), - Right, TreeHeight); - if (key > leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, new ImMapEntry(key)), - Right, TreeHeight); - if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(new ImMapEntry(key), leftBranch.Entry, leftBranch.RightEntry), - Right, TreeHeight); - return this; - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, new ImMapEntry(key)), Right, 3) - : key < leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(new ImMapEntry(key), leftLeaf), Right, 3) - : this; - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return this; - - // note: tree always contains left and right (for the missing leaf we have a Branch) - var newRightTree = rightTree.AddOrKeepLeftOrRight(key); - return newRightTree == rightTree ? this - : newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, new ImMapEntry(key)), - TreeHeight); - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, new ImMapEntry(key), rightBranch.RightEntry), - TreeHeight); - if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(new ImMapEntry(key), rightBranch.Entry, rightBranch.RightEntry), - TreeHeight); - return this; - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, new ImMapEntry(key)), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(new ImMapEntry(key), rightLeaf), 3) - : this; - } - } - - /// Adds to the left or right branch, or keeps the un-modified map - public ImMapTree AddOrKeepLeftOrRightEntry(int key, ImMapEntry entry) - { - if (key < Entry.Key) - { - var left = Left; - if (left is ImMapTree leftTree) - { - if (key == leftTree.Entry.Key) - return this; - - var newLeftTree = leftTree.AddOrKeepLeftOrRightEntry(key, entry); - return newLeftTree == leftTree ? this - : newLeftTree.TreeHeight == leftTree.TreeHeight - ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) - : BalanceNewLeftTree(newLeftTree); - } - - if (left is ImMapBranch leftBranch) - { - if (key < leftBranch.Entry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), - Right, TreeHeight); - if (key > leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry), - Right, TreeHeight); - if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) - return new ImMapTree(Entry, - new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry), - Right, TreeHeight); - return this; - } - - var leftLeaf = (ImMapEntry)left; - return key > leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) - : key < leftLeaf.Key - ? new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3) - : this; - } - else - { - var right = Right; - if (right is ImMapTree rightTree) - { - if (key == rightTree.Entry.Key) - return this; - - // note: tree always contains left and right (for the missing leaf we have a Branch) - var newRightTree = rightTree.AddOrKeepLeftOrRightEntry(key, entry); - return newRightTree == rightTree ? this - : newRightTree.TreeHeight == rightTree.TreeHeight - ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) - : BalanceNewRightTree(newRightTree); - } - - if (right is ImMapBranch rightBranch) - { - if (key > rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry), - TreeHeight); - if (key < rightBranch.Entry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), - TreeHeight); - if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) - return new ImMapTree(Entry, Left, - new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry), - TreeHeight); - return this; - } - - var rightLeaf = (ImMapEntry)right; - return key > rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) - : key < rightLeaf.Key - ? new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3) - : this; - } - } - - private ImMapTree BalanceNewLeftTree(ImMapTree newLeftTree) - { - var rightHeight = Right.Height; - var delta = newLeftTree.TreeHeight - rightHeight; - if (delta <= 0) - return new ImMapTree(Entry, newLeftTree, Right, TreeHeight); - - if (delta == 1) - return new ImMapTree(Entry, newLeftTree, Right, newLeftTree.TreeHeight + 1); - - // here is the balancing art comes into place - if (rightHeight == 1) - { - if (newLeftTree.Left is ImMapEntry leftLeftLeaf) - { - // 30 15 - // 10 40 => 10 20 - // 5 15 5 12 30 40 - // 12 20 - if (newLeftTree.Right is ImMapTree leftRightTree) - { - newLeftTree.Right = leftRightTree.Left; - newLeftTree.TreeHeight = 2; - return new ImMapTree(leftRightTree.Entry, - newLeftTree, - new ImMapTree(Entry, leftRightTree.Right, Right, 2), - 3); - } - - // we cannot reuse the new left tree here because it is reduced into the branch - // 30 15 - // 10 40 => 5 20 - // 5 15 10 30 40 - // 20 - var leftRightBranch = (ImMapBranch)newLeftTree.Right; - return new ImMapTree(leftRightBranch.Entry, - new ImMapBranch(leftLeftLeaf, newLeftTree.Entry), - new ImMapTree(Entry, leftRightBranch.RightEntry, Right, 2), - 3); - } - - newLeftTree.Right = new ImMapTree(Entry, newLeftTree.Right, Right, 2); - newLeftTree.TreeHeight = 3; - return newLeftTree; - } - - var leftLeftHeight = (newLeftTree.Left as ImMapTree)?.TreeHeight ?? 2; - var leftRightHeight = (newLeftTree.Right as ImMapTree)?.TreeHeight ?? 2; - if (leftLeftHeight < leftRightHeight) - { - var leftRightTree = (ImMapTree)newLeftTree.Right; - - newLeftTree.Right = leftRightTree.Left; - newLeftTree.TreeHeight = leftLeftHeight + 1; - return new ImMapTree(leftRightTree.Entry, - newLeftTree, - new ImMapTree(Entry, leftRightTree.Right, Right, rightHeight + 1), - leftLeftHeight + 2); - - //return new ImMapTree(leftRightTree.Entry, - // new ImMapTree(newLeftTree.Entry, leftLeftHeight, newLeftTree.Left, leftRightTree.Left), - // new ImMapTree(Entry, leftRightTree.Right, rightHeight, Right)); - } - - newLeftTree.Right = new ImMapTree(Entry, newLeftTree.Right, Right, leftRightHeight + 1); - newLeftTree.TreeHeight = leftRightHeight + 2; - return newLeftTree; - } - - private ImMapTree BalanceNewRightTree(ImMapTree newRightTree) - { - var leftHeight = Left.Height; - var delta = newRightTree.Height - leftHeight; - if (delta <= 0) - return new ImMapTree(Entry, Left, newRightTree, TreeHeight); - if (delta == 1) - return new ImMapTree(Entry, Left, newRightTree, newRightTree.TreeHeight + 1); - - if (leftHeight == 1) - { - // here we need to re-balance by default, because the new right tree is at least 3 level (actually exactly 3 or it would be too unbalanced) - // double rotation needed if only the right-right is a leaf - if (newRightTree.Right is ImMapEntry == false) - { - newRightTree.Left = new ImMapTree(Entry, Left, newRightTree.Left, 2); - newRightTree.TreeHeight = 3; - return newRightTree; - } - - // 20 30 - // 10 40 => 20 40 - // 30 50 10 25 35 50 - // 25 35 - if (newRightTree.Left is ImMapTree rightLeftTree) - { - newRightTree.Left = rightLeftTree.Right; - newRightTree.TreeHeight = 2; - return new ImMapTree(rightLeftTree.Entry, - new ImMapTree(Entry, Left, rightLeftTree.Left, 2), - newRightTree, 3); - } - - // 20 30 - // 10 40 => 10 40 - // 30 50 20 35 50 - // 35 - var rightLeftBranch = (ImMapBranch)newRightTree.Left; - newRightTree.Left = rightLeftBranch.RightEntry; - newRightTree.TreeHeight = 2; - return new ImMapTree(rightLeftBranch.Entry, - new ImMapBranch((ImMapEntry)Left, Entry), - newRightTree, 3); - } - - var rightRightHeight = (newRightTree.Right as ImMapTree)?.TreeHeight ?? 2; - var rightLeftHeight = (newRightTree.Left as ImMapTree)?.TreeHeight ?? 2; - if (rightRightHeight < rightLeftHeight) - { - var rightLeftTree = (ImMapTree)newRightTree.Left; - newRightTree.Left = rightLeftTree.Right; - // the height now should be defined by rr - because left now is shorter by 1 - newRightTree.TreeHeight = rightRightHeight + 1; - // the whole height consequentially can be defined by `newRightTree` (rr+1) because left is consist of short Left and -2 rl.Left - return new ImMapTree(rightLeftTree.Entry, - // Left should be >= rightLeft.Left because it maybe rightLeft.Right which defines rl height - new ImMapTree(Entry, Left, rightLeftTree.Left, leftHeight + 1), - newRightTree, - rightRightHeight + 2); - - //return new ImMapTree(rightLeftTree.Entry, - // new ImMapTree(Entry, leftHeight, Left, rightLeftTree.Left), - // new ImMapTree(newRightTree.Entry, rightLeftTree.Right, rightRightHeight, newRightTree.Right)); - } - - Debug.Assert(rightLeftHeight >= leftHeight, "The whole rightHeight > leftHeight by 2, and rightRight >= leftHeight but not more than by 2"); - - // we may decide on the height because the Left smaller by 2 - newRightTree.Left = new ImMapTree(Entry, Left, newRightTree.Left, rightLeftHeight + 1); - // if rr was > rl by 1 than new rl+1 should be equal height to rr now, if rr was == rl than new rl wins anyway - newRightTree.TreeHeight = rightLeftHeight + 2; - return newRightTree; - } - } - - /// ImMap methods - public static class ImMap - { - /// Adds or updates the value by key in the map, always returns a modified map - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrUpdateEntry(this ImMap map, ImMapEntry entry) - { - if (map == ImMap.Empty) - return entry; - - var key = entry.Key; - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, entry) - : key < leaf.Key ? new ImMapBranch(entry, leaf) - : (ImMap)entry; - - if (map is ImMapBranch branch) - { - if (key > branch.Entry.Key) - // 5 10 - // 10 => 5 11 - // 11 - return key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, entry) - // 5 7 - // 10 => 5 10 - // 7 - : key < branch.RightEntry.Key // rotate if right - ? new ImMapTree(entry, branch.Entry, branch.RightEntry) - : (ImMap)new ImMapBranch(branch.Entry, entry); - - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, entry, branch.RightEntry) - : (ImMap)new ImMapBranch(entry, branch.RightEntry); - } - - var tree = (ImMapTree)map; - return key == tree.Entry.Key - ? new ImMapTree(entry, tree.Left, tree.Right, tree.TreeHeight) - : tree.AddOrUpdateLeftOrRightEntry(key, entry); - } - - /// Adds or updates the value by key in the map, always returns a modified map - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrUpdate(this ImMap map, int key, V value) => - map.AddOrUpdateEntry(new ImMapEntry(key, value)); - - /// Adds the value by key in the map - ASSUMES that the key is not in the map, always returns a modified map - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddEntryUnsafe(this ImMap map, ImMapEntry entry) - { - if (map == ImMap.Empty) - return entry; - - var key = entry.Key; - if (map is ImMapEntry leaf) - return key > leaf.Key - ? new ImMapBranch(leaf, entry) - : new ImMapBranch(entry, leaf); - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, entry, branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, entry) - : new ImMapTree(entry, branch.Entry, branch.RightEntry); - - return ((ImMapTree)map).AddUnsafeLeftOrRightEntry(key, entry); - } - - /// Adds the value for the key or returns the un-modified map if key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrKeep(this ImMap map, int key, V value) - { - if (map == ImMap.Empty) - return new ImMapEntry(key, value); - - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, new ImMapEntry(key, value)) - : key < leaf.Key ? new ImMapBranch(new ImMapEntry(key, value), leaf) - : map; - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, new ImMapEntry(key, value), branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, new ImMapEntry(key, value)) - : key > branch.Entry.Key && key < branch.RightEntry.Key - ? new ImMapTree(new ImMapEntry(key, value), branch.Entry, branch.RightEntry) - : map; - - var tree = (ImMapTree)map; - return key != tree.Entry.Key - ? tree.AddOrKeepLeftOrRight(key, value) - : map; - } - - /// Adds the entry with default value for the key or returns the un-modified map if key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrKeep(this ImMap map, int key) - { - if (map == ImMap.Empty) - return new ImMapEntry(key); - - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, new ImMapEntry(key)) - : key < leaf.Key ? new ImMapBranch(new ImMapEntry(key), leaf) - : map; - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, new ImMapEntry(key), branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, new ImMapEntry(key)) - : key > branch.Entry.Key && key < branch.RightEntry.Key - ? new ImMapTree(new ImMapEntry(key), branch.Entry, branch.RightEntry) - : map; - - var tree = (ImMapTree)map; - return key != tree.Entry.Key ? tree.AddOrKeepLeftOrRight(key) : map; - } - - /// Adds the entry for the key or returns the un-modified map if key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap AddOrKeepEntry(this ImMap map, ImMapEntry entry) - { - if (map == ImMap.Empty) - return entry; - - var key = entry.Key; - if (map is ImMapEntry leaf) - return key > leaf.Key ? new ImMapBranch(leaf, entry) - : key < leaf.Key ? new ImMapBranch(entry, leaf) - : map; - - if (map is ImMapBranch branch) - return key < branch.Entry.Key - ? new ImMapTree(branch.Entry, entry, branch.RightEntry) - : key > branch.RightEntry.Key - ? new ImMapTree(branch.RightEntry, branch.Entry, entry) - : key > branch.Entry.Key && key < branch.RightEntry.Key - ? new ImMapTree(entry, branch.Entry, branch.RightEntry) - : map; - - var tree = (ImMapTree)map; - return key != tree.Entry.Key ? tree.AddOrKeepLeftOrRightEntry(key, entry) : map; - } - - ///Returns the new map with the updated value for the key, or the same map if the key was not found. - [MethodImpl((MethodImplOptions)256)] - public static ImMap Update(this ImMap map, int key, V value) => - map.Contains(key) ? map.UpdateImpl(key, new ImMapEntry(key, value)) : map; - - ///Returns the new map with the updated value for the key, ASSUMES that the key is not in the map. - [MethodImpl((MethodImplOptions)256)] - public static ImMap UpdateEntryUnsafe(this ImMap map, ImMapEntry entry) => - map.UpdateImpl(entry.Key, entry); - - internal static ImMap UpdateImpl(this ImMap map, int key, ImMapEntry entry) - { - if (map is ImMapTree tree) - return key > tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left, tree.Right.UpdateImpl(key, entry), tree.TreeHeight) - : key < tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left.UpdateImpl(key, entry), tree.Right, tree.TreeHeight) - : new ImMapTree(entry, tree.Left, tree.Right, tree.TreeHeight); - - // the key was found - so it should be either entry or right entry - if (map is ImMapBranch branch) - return key == branch.Entry.Key - ? new ImMapBranch(entry, branch.RightEntry) - : new ImMapBranch(branch.Entry, entry); - - return entry; - } - - ///Returns the new map with the value set to default, or the same map if the key was not found. - [MethodImpl((MethodImplOptions)256)] - public static ImMap UpdateToDefault(this ImMap map, int key) => - map.Contains(key) ? map.UpdateToDefaultImpl(key) : map; - - internal static ImMap UpdateToDefaultImpl(this ImMap map, int key) - { - if (map is ImMapTree tree) - return key > tree.Entry.Key - ? new ImMapTree(tree.Entry, tree.Left, tree.Right.UpdateToDefaultImpl(key), tree.TreeHeight) - : key < tree.Entry.Key - ? new ImMapTree(tree.Entry, tree.Left.UpdateToDefaultImpl(key), tree.Right, tree.TreeHeight) - : new ImMapTree(new ImMapEntry(key), tree.Left, tree.Right, tree.TreeHeight); - - // the key was found - so it should be either entry or right entry - if (map is ImMapBranch branch) - return key == branch.Entry.Key - ? new ImMapBranch(new ImMapEntry(key), branch.RightEntry) - : new ImMapBranch(branch.Entry, new ImMapEntry(key)); - - return new ImMapEntry(key); - } - - /// Returns `true` if key is found or `false` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static bool Contains(this ImMap map, int key) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - return true; - } - - if (map is ImMapBranch branch) - return branch.Entry.Key == key || branch.RightEntry.Key == key; - - entry = map as ImMapEntry; - return entry != null && entry.Key == key; - } - - /// Returns the entry if key is found or null otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry GetEntryOrDefault(this ImMap map, int key) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - return entry; - } - - if (map is ImMapBranch branch) - return branch.Entry.Key == key ? branch.Entry - : branch.RightEntry.Key == key ? branch.RightEntry - : null; - - entry = map as ImMapEntry; - return entry != null && entry.Key == key ? entry : null; - } - - /// Returns the value if key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImMap map, int key) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - return entry.Value; - } - - if (map is ImMapBranch branch) - return branch.Entry.Key == key ? branch.Entry.Value - : branch.RightEntry.Key == key ? branch.RightEntry.Value - : default; - - entry = map as ImMapEntry; - if (entry != null && entry.Key == key) - return entry.Value; - - return default; - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImMap map, int key, out V value) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - { - value = entry.Value; - return true; - } - } - - if (map is ImMapBranch branch) - { - if (branch.Entry.Key == key) - { - value = branch.Entry.Value; - return true; - } - - if (branch.RightEntry.Key == key) - { - value = branch.RightEntry.Value; - return true; - } - - value = default; - return false; - } - - entry = map as ImMapEntry; - if (entry != null && entry.Key == key) - { - value = entry.Value; - return true; - } - - value = default; - return false; - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFindEntry(this ImMap map, int key, out ImMapEntry result) - { - ImMapEntry entry; - while (map is ImMapTree tree) - { - entry = tree.Entry; - if (key > entry.Key) - map = tree.Right; - else if (key < entry.Key) - map = tree.Left; - else - { - result = entry; - return true; - } - } - - if (map is ImMapBranch branch) - { - if (branch.Entry.Key == key) - { - result = branch.Entry; - return true; - } - - if (branch.RightEntry.Key == key) - { - result = branch.RightEntry; - return true; - } - - result = null; - return false; - } - - entry = map as ImMapEntry; - if (entry != null && entry.Key == key) - { - result = entry; - return true; - } - - result = null; - return false; - } - - /// - /// Enumerates all the map nodes from the left to the right and from the bottom to top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, - /// the content of the stack is not important and could be erased. - /// - public static IEnumerable> Enumerate(this ImMap map, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - yield break; - - if (map is ImMapEntry leaf) - yield return leaf; - else if (map is ImMapBranch branch) - { - yield return branch.Entry; - yield return branch.RightEntry; - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - yield return (ImMapEntry)tree.Left; - yield return tree.Entry; - yield return (ImMapEntry)tree.Right; - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - yield return (ImMapEntry)tree.Left; - yield return tree.Entry; - yield return (ImMapEntry)tree.Right; - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - yield return tree.Entry; - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - yield return branch.Entry; - yield return branch.RightEntry; - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - yield return tree.Entry; - map = tree.Right; - } - else - { - yield return (ImMapEntry)map; - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - yield return tree.Entry; - map = tree.Right; - } - } - } - } - } - - /// - /// Folds all the map nodes with the state from left to right and from the bottom to top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, - /// the content of the stack is not important and could be erased. - /// - public static S Fold(this ImMap map, S state, Func, S, S> reduce, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - return state; - - if (map is ImMapEntry leaf) - state = reduce(leaf, state); - else if (map is ImMapBranch branch) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - state = reduce((ImMapEntry)map, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - } - } - } - - return state; - } - - /// - /// Folds all the map nodes with the state from left to right and from the bottom to top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, - /// the content of the stack is not important and could be erased. - /// - public static S Fold(this ImMap map, S state, A a, Func, S, A, S> reduce, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - return state; - - if (map is ImMapEntry leaf) - state = reduce(leaf, state, a); - else if (map is ImMapBranch branch) - { - state = reduce(branch.Entry, state, a); - state = reduce(branch.RightEntry, state, a); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state, a); - state = reduce(tree.Entry, state, a); - state = reduce((ImMapEntry)tree.Right, state, a); - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state, a); - state = reduce(tree.Entry, state, a); - state = reduce((ImMapEntry)tree.Right, state, a); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state, a); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - state = reduce(branch.Entry, state, a); - state = reduce(branch.RightEntry, state, a); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state, a); - map = tree.Right; - } - else - { - state = reduce((ImMapEntry)map, state, a); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state, a); - map = tree.Right; - } - } - } - } - - return state; - } - - /// - /// Visits all the map nodes with from the left to the right and from the bottom to the top - /// You may pass `parentStacks` to reuse the array memory. - /// NOTE: the length of `parentStack` should be at least of map height, content is not important and could be erased. - /// - public static void Visit(this ImMap map, Action> visit, ImMapTree[] parentStack = null) - { - if (map == ImMap.Empty) - return; - - if (map is ImMapEntry leaf) - visit(leaf); - else if (map is ImMapBranch branch) - { - visit(branch.Entry); - visit(branch.RightEntry); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - visit((ImMapEntry)tree.Left); - visit(tree.Entry); - visit((ImMapEntry)tree.Right); - } - else - { - parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - visit((ImMapEntry)tree.Left); - visit(tree.Entry); - visit((ImMapEntry)tree.Right); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - visit(tree.Entry); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - visit(branch.Entry); - visit(branch.RightEntry); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - visit(tree.Entry); - map = tree.Right; - } - else - { - visit((ImMapEntry)map); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - visit(tree.Entry); - map = tree.Right; - } - } - } - } - } - - /// Wraps Key and Value payload to store inside ImMapEntry - public struct KValue - { - /// The key - public K Key; - /// The value - public object Value; - - /// Constructs a pair - public KValue(K key, object value) - { - Key = key; - Value = value; - } - } - - /// Uses the user provided hash and adds or updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrUpdate(this ImMap> map, int hash, K key, object value, Update update) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(CreateKValueEntry(hash, key, value)) - : UpdateEntryOrAddOrUpdateConflict(map, hash, oldEntry, key, value, update); - } - - private static ImMap> UpdateEntryOrAddOrUpdateConflict(ImMap> map, int hash, - ImMapEntry> oldEntry, K key, object value, Update update = null) - { - if (key.Equals(oldEntry.Value.Key)) - { - value = update == null ? value : update(key, oldEntry.Value.Value, value); - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key, value)); - } - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex != -1) - { - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - value = update == null ? value : update(key, conflicts[conflictIndex].Value.Value, value); - newConflicts[conflictIndex] = CreateKValueEntry(hash, key, value); - } - else - { - // add the new conflicting value - newConflicts = new ImMapEntry>[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = CreateKValueEntry(hash, key, value); - } - } - else - { - newConflicts = new[] { oldEntry, CreateKValueEntry(hash, key, value) }; - } - - var conflictsEntry = new ImMapEntry>(hash); - conflictsEntry.Value.Value = newConflicts; - return map.UpdateEntryUnsafe(conflictsEntry); - } - - /// Efficiently creates the new entry - [MethodImpl((MethodImplOptions)256)] - private static ImMapEntry> CreateKValueEntry(int hash, K key, object value) - { - var newEntry = new ImMapEntry>(hash); - newEntry.Value.Key = key; - newEntry.Value.Value = value; - return newEntry; - } - - /// Efficiently creates the new entry - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry> CreateKValueEntry(int hash, K key) - { - var newEntry = new ImMapEntry>(hash); - newEntry.Value.Key = key; - return newEntry; - } - - /// Uses the user provided hash and adds or updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrUpdate(this ImMap> map, int hash, K key, object value) => - map.AddOrUpdate(hash, CreateKValueEntry(hash, key, value)); - - /// Uses the provided hash and adds or updates the tree with the passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrUpdate(this ImMap> map, int hash, ImMapEntry> entry) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(entry) - : UpdateEntryOrAddOrUpdateConflict(map, hash, oldEntry, entry); - } - - private static ImMap> UpdateEntryOrAddOrUpdateConflict(ImMap> map, int hash, - ImMapEntry> oldEntry, ImMapEntry> newEntry) - { - if (newEntry.Value.Key.Equals(oldEntry.Value.Key)) - return map.UpdateEntryUnsafe(newEntry); - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var key = newEntry.Value.Key; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex != -1) - { - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictIndex] = newEntry; - } - else - { - // add the new conflicting value - newConflicts = new ImMapEntry>[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = newEntry; - } - } - else - { - newConflicts = new[] { oldEntry, newEntry }; - } - - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); - } - - /// Adds the new entry or keeps the current map if entry key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrKeep(this ImMap> map, int hash, K key) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(CreateKValueEntry(hash, key)) - : AddOrKeepConflict(map, hash, oldEntry, key); - } - - /// Adds the new entry or keeps the current map if entry key is already present - [MethodImpl((MethodImplOptions)256)] - public static ImMap> AddOrKeep(this ImMap> map, int hash, K key, object value) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null - ? map.AddEntryUnsafe(CreateKValueEntry(hash, key, value)) - : AddOrKeepConflict(map, hash, oldEntry, key, value); - } - - private static ImMap> AddOrKeepConflict(ImMap> map, int hash, - ImMapEntry> oldEntry, K key, object value = null) - { - if (key.Equals(oldEntry.Value.Key)) - return map; - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex != -1) - return map; - - // add the new conflicting value - newConflicts = new ImMapEntry>[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = CreateKValueEntry(hash, key, value); - } - else - { - newConflicts = new[] { oldEntry, CreateKValueEntry(hash, key, value) }; - } - - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); - } - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - public static ImMap> Update(this ImMap> map, int hash, K key, object value, Update update = null) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null ? map : UpdateEntryOrReturnSelf(map, hash, oldEntry, key, value, update); - } - - private static ImMap> UpdateEntryOrReturnSelf(ImMap> map, - int hash, ImMapEntry> oldEntry, K key, object value, Update update = null) - { - if (key.Equals(oldEntry.Value.Key)) - { - value = update == null ? value : update(key, oldEntry.Value.Value, value); - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key, value)); - } - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex == -1) - return map; - - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - value = update == null ? value : update(key, conflicts[conflictIndex].Value.Value, value); - newConflicts[conflictIndex] = CreateKValueEntry(hash, key, value); - } - else - { - return map; - } - - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); - } - - /// Updates the map with the default value if the key is found, otherwise returns the same unchanged map. - public static ImMap> UpdateToDefault(this ImMap> map, int hash, K key) - { - var oldEntry = map.GetEntryOrDefault(hash); - return oldEntry == null ? map : UpdateEntryOrReturnSelf(map, hash, oldEntry, key); - } - - private static ImMap> UpdateEntryOrReturnSelf(ImMap> map, - int hash, ImMapEntry> oldEntry, K key) - { - if (key.Equals(oldEntry.Value.Key)) - return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key)); - - // add a new conflicting key value - ImMapEntry>[] newConflicts; - if (oldEntry.Value.Value is ImMapEntry>[] conflicts) - { - // entry is already containing the conflicted entries - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) - --conflictIndex; - - if (conflictIndex == -1) - return map; - - // update the existing conflict - newConflicts = new ImMapEntry>[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictIndex] = CreateKValueEntry(hash, key); - } - else - { - return map; - } - - var conflictsEntry = new ImMapEntry>(hash); - conflictsEntry.Value.Value = newConflicts; - return map.UpdateEntryUnsafe(conflictsEntry); - } - - /// Returns the entry if key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry> GetEntryOrDefault(this ImMap> map, int hash, K key) - { - var entry = map.GetEntryOrDefault(hash); - return entry != null - ? key.Equals(entry.Value.Key) ? entry : GetConflictedEntryOrDefault(entry, key) - : null; - } - - /// Returns the value if key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static object GetValueOrDefault(this ImMap> map, int hash, K key) => - map.GetEntryOrDefault(hash, key)?.Value.Value; - - /// Sets the value if key is found or returns false otherwise. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImMap> map, int hash, K key, out object value) - { - var entry = map.GetEntryOrDefault(hash, key); - if (entry != null) - { - value = entry.Value.Value; - return true; - } - - value = null; - return false; - } - - /// Returns the entry if key is found or `null` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImMapEntry> GetEntryOrDefault(this ImMap> map, int hash, Type type) - { - var entry = map.GetEntryOrDefault(hash); - return entry != null - ? entry.Value.Key == type ? entry : GetConflictedEntryOrDefault(entry, type) - : null; - } - - /// Returns the value if the Type key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static object GetValueOrDefault(this ImMap> map, int hash, Type typeKey) => - map.GetEntryOrDefault(hash, typeKey)?.Value.Value; - - /// Returns the value if the Type key is found or default value otherwise. - [MethodImpl((MethodImplOptions)256)] - public static object GetValueOrDefault(this ImMap> map, Type typeKey) => - map.GetEntryOrDefault(RuntimeHelpers.GetHashCode(typeKey), typeKey)?.Value.Value; - - internal static ImMapEntry> GetConflictedEntryOrDefault(ImMapEntry> entry, K key) - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Value.Key)) - return conflicts[i]; - return null; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// - public static IEnumerable>> Enumerate(this ImMap> map) - { - foreach (var entry in map.Enumerate(null)) - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - yield return conflicts[i]; - else - yield return entry; - } - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public static S Fold(this ImMap> map, - S state, Func>, S, S> reduce, ImMapTree>[] parentsStack = null) => - map.Fold(state, reduce, (entry, s, r) => - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - s = r(conflicts[i], s); - else - s = r(entry, s); - return s; - }, - parentsStack); - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public static S Visit(this ImMap> map, - S state, Action>, S> effect, ImMapTree>[] parentsStack = null) => - map.Fold(state, effect, (entry, s, eff) => - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - eff(conflicts[i], s); - else - eff(entry, s); - return s; - }, - parentsStack); - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public static void Visit(this ImMap> map, - Action>> effect, ImMapTree>[] parentsStack = null) => - map.Fold(false, effect, (entry, s, eff) => - { - if (entry.Value.Value is ImMapEntry>[] conflicts) - for (var i = 0; i < conflicts.Length; i++) - eff(conflicts[i]); - else - eff(entry); - return false; - }, - parentsStack); - } - - /// - /// The array of ImMap slots where the key first bits are used for FAST slot location - /// and the slot is the reference to ImMap that can be swapped with its updated value - /// - public static class ImMapSlots - { - /// Default number of slots - public const int SLOT_COUNT_POWER_OF_TWO = 32; - - /// The default mask to partition the key to the target slot - public const int KEY_MASK_TO_FIND_SLOT = SLOT_COUNT_POWER_OF_TWO - 1; - - /// Creates the array with the empty slots - [MethodImpl((MethodImplOptions)256)] - public static ImMap[] CreateWithEmpty(int slotCountPowerOfTwo = SLOT_COUNT_POWER_OF_TWO) - { - var slots = new ImMap[slotCountPowerOfTwo]; - for (var i = 0; i < slots.Length; ++i) - slots[i] = ImMap.Empty; - return slots; - } - - /// Returns a new tree with added or updated value for specified key. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImMap[] slots, int key, V value, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[key & keyMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(key, value), copy) != copy) - RefAddOrUpdateSlot(ref slot, key, value); - } - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrUpdateSlot(ref ImMap slot, int key, V value) => - Ref.Swap(ref slot, key, value, (x, k, v) => x.AddOrUpdate(k, v)); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImMap[] slots, int key, V value, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[key & keyMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(key, value), copy) != copy) - RefAddOrKeepSlot(ref slot, key, value); - } - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrKeepSlot(ref ImMap slot, int key, V value) => - Ref.Swap(ref slot, key, value, (s, k, v) => s.AddOrKeep(k, v)); - - /// Adds a default value entry for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImMap[] slots, int key, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[key & keyMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(key), copy) != copy) - RefAddOrKeepSlot(ref slot, key); - } - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrKeepSlot(ref ImMap slot, int key) => - Ref.Swap(ref slot, key, (s, k) => s.AddOrKeep(k)); - - /// Folds all map nodes without the order - public static S Fold(this ImMap[] slots, S state, Func, S, S> reduce) - { - var parentStack = ArrayTools.Empty>(); - for (var i = 0; i < slots.Length; ++i) - { - var map = slots[i]; - if (map == ImMap.Empty) - continue; - - if (map is ImMapEntry leaf) - state = reduce(leaf, state); - else if (map is ImMapBranch branch) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - } - else if (map is ImMapTree tree) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - } - else - { - if (parentStack.Length < tree.TreeHeight - 2) - parentStack = new ImMapTree[tree.TreeHeight - 2]; - var parentIndex = -1; - while (true) - { - if ((tree = map as ImMapTree) != null) - { - if (tree.TreeHeight == 2) - { - state = reduce((ImMapEntry)tree.Left, state); - state = reduce(tree.Entry, state); - state = reduce((ImMapEntry)tree.Right, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - parentStack[++parentIndex] = tree; - map = tree.Left; - } - } - else if ((branch = map as ImMapBranch) != null) - { - state = reduce(branch.Entry, state); - state = reduce(branch.RightEntry, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - else - { - state = reduce((ImMapEntry)map, state); - if (parentIndex == -1) - break; - tree = parentStack[parentIndex--]; - state = reduce(tree.Entry, state); - map = tree.Right; - } - } - } - } - } - - return state; - } - } - - /// Wraps the stored data with "fixed" reference semantics - when added to the tree it did not change or reconstructed in memory - public class ImHashMapEntry - { - /// Empty thingy - public static readonly ImHashMapEntry Empty = new ImHashMapEntry(); - - /// Key hash - public readonly int Hash; - - /// The key - public readonly K Key; - - /// The value - may be mutated implementing the Ref CAS semantics if needed - public V Value; - - private ImHashMapEntry() { } - - /// Constructs the data - public ImHashMapEntry(int hash, K key, V value) - { - Hash = hash; - Key = key; - Value = value; - } - - /// Constructs the data with the default value - public ImHashMapEntry(int hash, K key) - { - Hash = hash; - Key = key; - } - - /// Outputs the brief tree info - mostly for debugging purposes - public override string ToString() => Key + ": " + Value; - } - - /// Stores ALL the data in `Conflicts` array, the fields except the `hash` are just fillers. - /// This way we preserve the once created `ImHashMapData` so that client can hold the reference to it and update the Value if needed. - public sealed class ImHashMapConflicts : ImHashMapEntry - { - /// Conflicted data - public readonly ImHashMapEntry[] Conflicts; - - /// - public ImHashMapConflicts(int hash, params ImHashMapEntry[] conflicts) : base(hash, default, default) => - Conflicts = conflicts; - } - - /// Immutable http://en.wikipedia.org/wiki/AVL_tree - /// where node key is the hash code of - public sealed class ImHashMap - { - /// Empty map to start with. - public static readonly ImHashMap Empty = new ImHashMap(); - - /// Calculated key hash. - public int Hash - { - [MethodImpl((MethodImplOptions)256)] - get => Entry.Hash; - } - - /// Key of type K that should support and . - public K Key - { - [MethodImpl((MethodImplOptions)256)] - get => Entry.Key; - } - - /// Value of any type V. - public V Value - { - [MethodImpl((MethodImplOptions)256)] - get => Entry.Value; - } - - /// In case of conflicts for different keys contains conflicted keys with their values. - public ImHashMapEntry[] Conflicts - { - [MethodImpl((MethodImplOptions)256)] - get => (Entry as ImHashMapConflicts)?.Conflicts; - } - - /// Left sub-tree/branch, or empty. - public ImHashMap Left; - - /// Right sub-tree/branch, or empty. - public ImHashMap Right; - - /// Height of longest sub-tree/branch plus 1. It is 0 for empty tree, and 1 for single node tree. - public int Height; - - /// Returns true if tree is empty. - public bool IsEmpty => Height == 0; - - /// The entry which is allocated once and can be used as a "fixed" reference to the Key and Value - public readonly ImHashMapEntry Entry; - - internal ImHashMap() => Entry = ImHashMapEntry.Empty; - - /// Creates leaf node - public ImHashMap(int hash, K key, V value) - { - Entry = new ImHashMapEntry(hash, key, value); - Left = Empty; - Right = Empty; - Height = 1; - } - - /// Creates a leaf node with default value - public ImHashMap(int hash, K key) - { - Entry = new ImHashMapEntry(hash, key); - Left = Empty; - Right = Empty; - Height = 1; - } - - /// Creates a leaf node - public ImHashMap(ImHashMapEntry entry) - { - Entry = entry; - Left = Empty; - Right = Empty; - Height = 1; - } - - /// Creates the tree and calculates the height for you - public ImHashMap(ImHashMapEntry entry, ImHashMap left, ImHashMap right) - { - Entry = entry; - Left = left; - Right = right; - Height = 1 + (left.Height > right.Height ? left.Height : right.Height); - } - - /// Creates the tree with the known height - public ImHashMap(ImHashMapEntry entry, ImHashMap left, ImHashMap right, int height) - { - Entry = entry; - Left = left; - Right = right; - Height = height; - } - - /// Outputs the brief tree info - mostly for debugging purposes - public override string ToString() => Height == 0 ? "empty" - : "(" + Entry - + ") -> (" + (Left.Height == 0 ? "empty" : Left.Entry + " of height " + Left.Height) - + ", " + (Right.Height == 0 ? "empty" : Right.Entry + " of height " + Right.Height) - + ")"; - - /// Uses the user provided hash and adds and updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(int hash, K key, V value) => - Height == 0 ? new ImHashMap(hash, key, value) - : hash == Hash ? UpdateValueOrAddOrUpdateConflict(hash, key, value) - : AddOrUpdateLeftOrRight(hash, key, value); - - /// Adds and updates the tree with passed key-value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(K key, V value) => - AddOrUpdate(key.GetHashCode(), key, value); - - private ImHashMap UpdateValueOrAddOrUpdateConflict(int hash, K key, V value) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(key, Key) || key.Equals(Key)) - ? new ImHashMap(new ImHashMapEntry(hash, key, value), Left, Right, Height) - : AddOrUpdateConflict(conflictsData, hash, key, value); - } - - internal enum DoAddOrUpdateConflicts { AddOrUpdate, AddOrKeep, Update } - - private ImHashMap AddOrUpdateConflict(ImHashMapConflicts conflictsData, int hash, K key, V value, - Update update = null, DoAddOrUpdateConflicts doWhat = DoAddOrUpdateConflicts.AddOrUpdate) - { - if (conflictsData == null) - return doWhat == DoAddOrUpdateConflicts.Update - ? this - : new ImHashMap( - new ImHashMapConflicts(hash, Entry, new ImHashMapEntry(hash, key, value)), - Left, Right, Height); - - var conflicts = conflictsData.Conflicts; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) - --conflictIndex; - - ImHashMapEntry[] newConflicts; - if (conflictIndex != -1) - { - if (doWhat == DoAddOrUpdateConflicts.AddOrKeep) - return this; - - // update the existing conflicted value - newConflicts = new ImHashMapEntry[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - var newValue = update == null ? value : update(key, conflicts[conflictIndex].Value, value); - newConflicts[conflictIndex] = new ImHashMapEntry(hash, key, newValue); - } - else - { - if (doWhat == DoAddOrUpdateConflicts.Update) - return this; - - // add the new conflicting value - newConflicts = new ImHashMapEntry[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = new ImHashMapEntry(hash, key, value); - } - - return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); - } - - private ImHashMap AddOrUpdateLeftOrRight(int hash, K key, V value) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); - - if (Left.Hash == hash) - return new ImHashMap(Entry, Left.UpdateValueOrAddOrUpdateConflict(hash, key, value), Right, Height); - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, - new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrUpdateLeftOrRight(hash, key, value); - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); - - if (Right.Hash == hash) - return new ImHashMap(Entry, Left, Right.UpdateValueOrAddOrUpdateConflict(hash, key, value), Height); - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); - } - - var right = Right.AddOrUpdateLeftOrRight(hash, key, value); - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - private ImHashMap BalanceNewLeftTree(ImHashMap newLeftTree) - { - var leftLeft = newLeftTree.Left; - var leftLeftHeight = leftLeft.Height; - - var leftRight = newLeftTree.Right; - var leftRightHeight = leftRight.Height; - - if (leftRightHeight > leftLeftHeight) - { - newLeftTree.Right = leftRight.Left; - newLeftTree.Height = leftLeftHeight + 1; - return new ImHashMap(leftRight.Entry, - newLeftTree, - new ImHashMap(Entry, leftRight.Right, Right, Right.Height + 1), - leftLeftHeight + 2); - - //return new ImHashMap(leftRight.Entry, - // new ImHashMap(newLeftTree.Entry, leftLeft, leftRight.Left), - // new ImHashMap(Entry, leftRight.Right, Right)); - } - - newLeftTree.Right = new ImHashMap(Entry, leftRight, Right, leftRightHeight + 1); - newLeftTree.Height = leftRightHeight + 2; - return newLeftTree; - - //return new ImHashMap(newLeftTree.Entry, - // leftLeft, new ImHashMap(Entry, leftRight, Right)); - } - - // Note that Left is by 2 less deep than `newRightTree` - means that at `newRightTree.Left/Right` is at least of Left height or deeper - private ImHashMap BalanceNewRightTree(ImHashMap newRightTree) - { - var rightLeft = newRightTree.Left; - var rightLeftHeight = rightLeft.Height; - - var rightRight = newRightTree.Right; - var rightRightHeight = rightRight.Height; - - if (rightLeftHeight > rightRightHeight) // 1 greater - not 2 greater because it would be too unbalanced - { - newRightTree.Left = rightLeft.Right; - // the height now should be defined by rr - because left now is shorter by 1 - newRightTree.Height = rightRightHeight + 1; - // the whole height consequentially can be defined by `newRightTree` (rr+1) because left is consist of short Left and -2 rl.Left - return new ImHashMap(rightLeft.Entry, - // Left should be >= rightLeft.Left because it maybe rightLeft.Right which defines rl height - new ImHashMap(Entry, Left, rightLeft.Left, height: Left.Height + 1), - newRightTree, rightRightHeight + 2); - - //return new ImHashMap(rightLeft.Entry, - // new ImHashMap(Entry, Left, rightLeft.Left), - // new ImHashMap(newRightTree.Entry, rightLeft.Right, rightRight)); - } - - // we may decide on the height because the Left smaller by 2 - newRightTree.Left = new ImHashMap(Entry, Left, rightLeft, rightLeftHeight + 1); - // if rr was > rl by 1 than new rl+1 should be equal height to rr now, if rr was == rl than new rl wins anyway - newRightTree.Height = rightLeftHeight + 2; - return newRightTree; - - //return new ImHashMap(newRightTree.Entry, new ImHashMap(Entry, Left, rightLeft), rightRight); - } - - /// Uses the user provided hash and adds and updates the tree with passed key-value and the update function for the existing value. Returns a new tree. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(int hash, K key, V value, Update update) => - Height == 0 ? new ImHashMap(hash, key, value) - : hash == Hash ? UpdateValueOrAddOrUpdateConflict(hash, key, value, update) - : AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); - - private ImHashMap UpdateValueOrAddOrUpdateConflict(int hash, K key, V value, Update update) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) - ? new ImHashMap(new ImHashMapEntry(hash, key, update(key, Value, value)), Left, Right, Height) - : AddOrUpdateConflict(conflictsData, hash, key, value, update); - } - - private ImHashMap AddOrUpdateLeftOrRightWithUpdate(int hash, K key, V value, Update update) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); - - if (Left.Hash == hash) - return new ImHashMap(Entry, Left.UpdateValueOrAddOrUpdateConflict(hash, key, value, update), Right, Height); - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); - - if (Right.Hash == hash) - return new ImHashMap(Entry, Left, Right.UpdateValueOrAddOrUpdateConflict(hash, key, value, update), Height); - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); - } - - var right = Right.AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - /// Returns a new tree with added or updated key-value. Uses the provided for updating the existing value. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(K key, V value, Update update) => - AddOrUpdate(key.GetHashCode(), key, value, update); - - /// Returns a new tree with added or updated key-value. Uses the provided for updating the existing value. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrUpdate(K key, V value, Update update) => - AddOrUpdate(key.GetHashCode(), key, value, update.IgnoreKey); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(int hash, K key, V value) => - Height == 0 ? new ImHashMap(hash, key, value) - : hash == Hash ? KeepValueOrAddConflict(hash, key, value) - : AddOrKeepLeftOrRight(hash, key, value); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(K key, V value) => - AddOrKeep(key.GetHashCode(), key, value); - - private ImHashMap KeepValueOrAddConflict(int hash, K key, V value) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) ? this - : AddOrUpdateConflict(conflictsData, hash, key, value, null, DoAddOrUpdateConflicts.AddOrKeep); - } - private ImHashMap AddOrKeepLeftOrRight(int hash, K key, V value) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); - - if (Left.Hash == hash) - { - var leftWithNewConflict = Left.KeepValueOrAddConflict(hash, key, value); - return ReferenceEquals(leftWithNewConflict, Left) ? this - : new ImHashMap(Entry, leftWithNewConflict, Right, Height); - } - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, - new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrKeepLeftOrRight(hash, key, value); - if (ReferenceEquals(left, Left)) - return this; - - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); - - if (Right.Hash == hash) - { - var rightWithNewConflict = Right.KeepValueOrAddConflict(hash, key, value); - return ReferenceEquals(rightWithNewConflict, Right) ? this - : new ImHashMap(Entry, Left, rightWithNewConflict, Height); - } - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key, value), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); - } - - var right = Right.AddOrKeepLeftOrRight(hash, key, value); - if (ReferenceEquals(right, Right)) - return this; - - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(int hash, K key) => - Height == 0 ? new ImHashMap(hash, key) - : hash == Hash ? KeepValueOrAddConflict(hash, key) - : AddOrKeepLeftOrRight(hash, key); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap AddOrKeep(K key) => - AddOrKeep(key.GetHashCode(), key); - - private ImHashMap KeepValueOrAddConflict(int hash, K key) - { - var conflictsData = Entry as ImHashMapConflicts; - return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) - ? this : AddOrKeepConflict(conflictsData, hash, key); - } - - private ImHashMap AddOrKeepConflict(ImHashMapConflicts conflictsData, int hash, K key) - { - if (conflictsData == null) - return new ImHashMap( - new ImHashMapConflicts(hash, Entry, new ImHashMapEntry(hash, key)), - Left, Right, Height); - - var conflicts = conflictsData.Conflicts; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) - --conflictIndex; - - if (conflictIndex != -1) - return this; - - // add the new conflicting value - var newConflicts = new ImHashMapEntry[conflictCount + 1]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictCount] = new ImHashMapEntry(hash, key); - - return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); - } - - private ImHashMap AddOrKeepLeftOrRight(int hash, K key) - { - if (hash < Hash) - { - if (Left.Height == 0) - return new ImHashMap(Entry, new ImHashMap(hash, key), Right, 2); - - if (Left.Hash == hash) - { - var leftWithNewConflict = Left.KeepValueOrAddConflict(hash, key); - return ReferenceEquals(leftWithNewConflict, Left) ? this - : new ImHashMap(Entry, leftWithNewConflict, Right, Height); - } - - if (Right.Height == 0) - { - if (hash < Left.Hash) - return new ImHashMap(Left.Entry, - new ImHashMap(hash, key), new ImHashMap(Entry), 2); - - return new ImHashMap(new ImHashMapEntry(hash, key), - new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); - } - - var left = Left.AddOrKeepLeftOrRight(hash, key); - if (ReferenceEquals(left, Left)) - return this; - - return left.Height > Right.Height + 1 - ? BalanceNewLeftTree(left) - : new ImHashMap(Entry, left, Right); - } - else - { - if (Right.Height == 0) - return new ImHashMap(Entry, Left, new ImHashMap(hash, key), 2); - - if (Right.Hash == hash) - { - var rightWithNewConflict = Right.KeepValueOrAddConflict(hash, key); - return ReferenceEquals(rightWithNewConflict, Right) ? this - : new ImHashMap(Entry, Left, rightWithNewConflict, Height); - } - - if (Left.Height == 0) - { - if (hash < Right.Hash) - return new ImHashMap(new ImHashMapEntry(hash, key), - new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); - - return new ImHashMap(Right.Entry, - new ImHashMap(Entry), new ImHashMap(hash, key), 2); - } - - var right = Right.AddOrKeepLeftOrRight(hash, key); - if (ReferenceEquals(right, Right)) - return this; - - return right.Height > Left.Height + 1 - ? BalanceNewRightTree(right) - : new ImHashMap(Entry, Left, right); - } - } - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - public ImHashMap Update(int hash, K key, V value, Update update = null) - { - if (Height == 0) - return this; - - // No need to balance cause we not adding or removing nodes - if (hash < Hash) - { - var left = Left.Update(hash, key, value, update); - return ReferenceEquals(left, Left) ? this : new ImHashMap(Entry, left, Right, Height); - } - - if (hash > Hash) - { - var right = Right.Update(hash, key, value, update); - return ReferenceEquals(right, Right) ? this : new ImHashMap(Entry, Left, right, Height); - } - - var conflictsData = Entry as ImHashMapConflicts; - if (conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key))) - return new ImHashMap( - new ImHashMapEntry(hash, key, update == null ? value : update(key, Value, value)), - Left, Right, Height); - - return AddOrUpdateConflict(conflictsData, hash, key, value, update, DoAddOrUpdateConflicts.Update); - } - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap Update(K key, V value) => - Update(key.GetHashCode(), key, value); - - /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap Update(K key, V value, Update update) => - Update(key.GetHashCode(), key, value, update.IgnoreKey); - - /// Updates the map with the Default (null for reference types) value if key is found, otherwise returns the same unchanged map. - [MethodImpl((MethodImplOptions)256)] - public ImHashMap UpdateToDefault(int hash, K key) - { - if (Height == 0) - return this; - - // No need to balance cause we not adding or removing nodes - if (hash < Hash) - { - var left = Left.UpdateToDefault(hash, key); - return left == Left ? this : new ImHashMap(Entry, left, Right, Height); - } - - if (hash > Hash) - { - var right = Right.UpdateToDefault(hash, key); - return right == Right ? this : new ImHashMap(Entry, Left, right, Height); - } - - var conflictsData = Entry as ImHashMapConflicts; - if (conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key))) - return new ImHashMap(new ImHashMapEntry(hash, key), Left, Right, Height); - - return UpdateConflictToDefault(conflictsData, hash, key); - } - - private ImHashMap UpdateConflictToDefault(ImHashMapConflicts conflictsData, int hash, K key) - { - if (conflictsData == null) - return this; - - var conflicts = conflictsData.Conflicts; - var conflictCount = conflicts.Length; - var conflictIndex = conflictCount - 1; - while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) - --conflictIndex; - - if (conflictIndex == -1) - return this; - - // update the existing conflicted value - var newConflicts = new ImHashMapEntry[conflictCount]; - Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); - newConflicts[conflictIndex] = new ImHashMapEntry(hash, key); - return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// - public IEnumerable> Enumerate() - { - if (Height != 0) - { - var parents = new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parents[++parentCount] = node; - node = node.Left; - } - else - { - node = parents[parentCount--]; - if (node.Entry is ImHashMapConflicts conflictsData) - { - var conflicts = conflictsData.Conflicts; - for (var i = 0; i < conflicts.Length; i++) - yield return conflicts[i]; - } - else - { - yield return node.Entry; - } - - node = node.Right; - } - } - } - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public S Fold(S state, Func, S, S> reduce, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - return reduce(Entry, state); - - if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - state = reduce(node.Entry, state); - else - { - var conflict = conflicts.Conflicts; - for (var i = 0; i < conflict.Length; i++) - state = reduce(conflict[i], state); - } - - node = node.Right; - } - } - } - - return state; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public S Fold(S state, Func, int, S, S> reduce, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - return reduce(Entry, 0, state); - - if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var index = 0; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - state = reduce(node.Entry, index++, state); - else - { - var conflictData = conflicts.Conflicts; - for (var i = 0; i < conflictData.Length; i++) - state = reduce(conflictData[i], index++, state); - } - - node = node.Right; - } - } - } - - return state; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public S Visit(S state, Action, S> effect, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - { - effect(Entry, state); - } - else if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - effect(node.Entry, state); - else - { - var conflict = conflicts.Conflicts; - for (var i = 0; i < conflict.Length; i++) - effect(conflict[i], state); - } - - node = node.Right; - } - } - } - - return state; - } - - /// - /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal - /// The only difference is using fixed size array instead of stack for speed-up. - /// Note: By passing you may reuse the stack array between different method calls, - /// but it should be at least length. The contents of array are not important. - /// - public void Visit(Action> effect, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - effect(Entry); - else if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - effect(node.Entry); - else - { - var conflict = conflicts.Conflicts; - for (var i = 0; i < conflict.Length; i++) - effect(conflict[i]); - } - - node = node.Right; - } - } - } - } - - /// Finds the first entry matching the condition, returns `null` if not found - public ImHashMapEntry FindFirstOrDefault(Func, bool> condition, ImHashMap[] parentsStack = null) - { - if (Height == 1 && Entry is ImHashMapConflicts == false) - { - if (condition(Entry)) - return Entry; - } - else if (Height != 0) - { - parentsStack = parentsStack ?? new ImHashMap[Height]; - var node = this; - var parentCount = -1; - while (node.Height != 0 || parentCount != -1) - { - if (node.Height != 0) - { - parentsStack[++parentCount] = node; - node = node.Left; - } - else - { - node = parentsStack[parentCount--]; - - if (!(node.Entry is ImHashMapConflicts conflicts)) - { - if (condition(node.Entry)) - return node.Entry; - } - else - { - var conflictedEntries = conflicts.Conflicts; - for (var i = 0; i < conflictedEntries.Length; i++) - if (condition(conflictedEntries[i])) - return conflictedEntries[i]; - } - - node = node.Right; - } - } - } - - return null; - } - - /// Removes or updates value for specified key, or does nothing if the key is not found (returns the unchanged map) - /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx - public ImHashMap Remove(int hash, K key) => - RemoveImpl(hash, key); - - /// Removes or updates value for specified key, or does nothing if the key is not found (returns the unchanged map) - /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx - [MethodImpl((MethodImplOptions)256)] - public ImHashMap Remove(K key) => - RemoveImpl(key.GetHashCode(), key); - - private ImHashMap RemoveImpl(int hash, K key, bool ignoreKey = false) - { - if (Height == 0) - return this; - - ImHashMap result; - if (hash == Hash) // found node - { - if (ignoreKey || Equals(Key, key)) - { - if (Height == 1) // remove node - return Empty; - - if (Right.IsEmpty) - result = Left; - else if (Left.IsEmpty) - result = Right; - else - { - // we have two children, so remove the next highest node and replace this node with it. - var next = Right; - while (!next.Left.IsEmpty) - next = next.Left; - result = new ImHashMap(next.Entry, Left, Right.RemoveImpl(next.Hash, default, ignoreKey: true)); - } - } - else if (Entry is ImHashMapConflicts conflictsData) - return TryRemoveConflicted(conflictsData, hash, key); - else - return this; // if key is not matching and no conflicts to lookup - just return - } - else - result = hash < Hash - ? Balance(Entry, Left.RemoveImpl(hash, key, ignoreKey), Right) - : Balance(Entry, Left, Right.RemoveImpl(hash, key, ignoreKey)); - - return result; - } - - /// Searches for the key in the conflicts and returns true if found - public bool ContainsConflictedData(K key) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Key)) - return true; - } - return false; - } - - /// Searches for the key in the node conflicts - public ImHashMapEntry GetConflictedEntryOrDefault(K key) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Key)) - return conflicts[i]; - } - return null; - } - - /// Searches for the key in the node conflicts - public V GetConflictedValueOrDefault(K key, V defaultValue) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (key.Equals(conflicts[i].Key)) - return conflicts[i].Value; - } - return defaultValue; - } - - /// Searches for the key in the node conflicts - public bool TryFindConflictedValue(K key, out V value) - { - if (Conflicts != null) - { - var conflicts = Conflicts; - for (var i = 0; i < conflicts.Length; ++i) - if (Equals(conflicts[i].Key, key)) - { - value = conflicts[i].Value; - return true; - } - } - value = default; - return false; - } - - // todo: implement in terms of BalanceNewLeftTree | BalanceNewRightTree - private static ImHashMap Balance(ImHashMapEntry entry, ImHashMap left, ImHashMap right) - { - var delta = left.Height - right.Height; - if (delta > 1) // left is longer by 2, rotate left - { - var leftLeft = left.Left; - var leftRight = left.Right; - if (leftRight.Height > leftLeft.Height) - { - // double rotation: - // 5 => 5 => 4 - // 2 6 4 6 2 5 - // 1 4 2 3 1 3 6 - // 3 1 - return new ImHashMap(leftRight.Entry, - new ImHashMap(left.Entry, leftLeft, leftRight.Left), - new ImHashMap(entry, leftRight.Right, right)); - } - - // one rotation: - // 5 => 2 - // 2 6 1 5 - // 1 4 4 6 - return new ImHashMap(left.Entry, - leftLeft, new ImHashMap(entry, leftRight, right)); - } - - if (delta < -1) - { - var rightLeft = right.Left; - var rightRight = right.Right; - return rightLeft.Height > rightRight.Height - ? new ImHashMap(rightLeft.Entry, - new ImHashMap(entry, left, rightLeft.Left), - new ImHashMap(right.Entry, rightLeft.Right, rightRight)) - : new ImHashMap(right.Entry, new ImHashMap(entry, left, rightLeft), rightRight); - } - - return new ImHashMap(entry, left, right); - } - - private ImHashMap TryRemoveConflicted(ImHashMapConflicts conflictsData, int hash, K key) - { - var conflicts = conflictsData.Conflicts; - var index = conflicts.Length - 1; - while (index != -1 && !conflicts[index].Key.Equals(key)) --index; - if (index == -1) // key is not found in conflicts - just return - return this; - - // we removing the one from the 2 items, so we can reference the remaining item directly from the map node - if (conflicts.Length == 2) - return new ImHashMap(index == 0 ? conflicts[1] : conflicts[0], Left, Right, Height); - - // copy all except the `index`ed data into shrinked conflicts - var shrinkedConflicts = new ImHashMapEntry[conflicts.Length - 1]; - var newIndex = 0; - for (var i = 0; i < conflicts.Length; ++i) - if (i != index) - shrinkedConflicts[newIndex++] = conflicts[i]; - return new ImHashMap(new ImHashMapConflicts(hash, shrinkedConflicts), Left, Right, Height); - } - } - - /// ImHashMap methods for faster performance - public static class ImHashMap - { - internal static V IgnoreKey(this Update update, K _, V oldValue, V newValue) => update(oldValue, newValue); - - /// Looks for key in a tree and returns `true` if found. - [MethodImpl((MethodImplOptions)256)] - public static bool Contains(this ImHashMap map, int hash, K key) - { - while (map.Height != 0 && map.Hash != hash) - map = hash < map.Hash ? map.Left : map.Right; - return map.Height != 0 && (key.Equals(map.Key) || map.ContainsConflictedData(key)); - } - - /// Looks for key in a tree and returns `true` if found. - [MethodImpl((MethodImplOptions)256)] - public static bool Contains(this ImHashMap map, K key) => - map.Height != 0 && map.Contains(key.GetHashCode(), key); - - /// Looks for key in a tree and returns the Data object if found or `null` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImHashMapEntry GetEntryOrDefault(this ImHashMap map, int hash, K key) - { - while (map.Height != 0 && map.Hash != hash) - map = hash < map.Hash ? map.Left : map.Right; - - return map.Height == 0 ? null : - key.Equals(map.Key) ? map.Entry : - map.GetConflictedEntryOrDefault(key); - } - - /// Looks for key in a tree and returns the Data object if found or `null` otherwise. - [MethodImpl((MethodImplOptions)256)] - public static ImHashMapEntry GetEntryOrDefault(this ImHashMap map, K key) - { - if (map.Height == 0) - return null; - - var hash = key.GetHashCode(); - - while (map.Hash != hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return null; - } - - return key.Equals(map.Key) ? map.Entry : map.GetConflictedEntryOrDefault(key); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, K key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - var hash = key.GetHashCode(); - - while (map.Hash != hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - return key.Equals(map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, int hash, K key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - while (map.Hash != hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - return key.Equals(map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, Type key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - var hash = RuntimeHelpers.GetHashCode(key); - while (hash != map.Hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - // we don't need to check `Height != 0` again cause in that case `key` will be `null` and `ReferenceEquals` will fail - return ReferenceEquals(key, map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Looks for key in a tree and returns the key value if found, or otherwise. - [MethodImpl((MethodImplOptions)256)] - public static V GetValueOrDefault(this ImHashMap map, int hash, Type key, V defaultValue = default) - { - if (map.Height == 0) - return defaultValue; - - while (hash != map.Hash) - { - map = hash < map.Hash ? map.Left : map.Right; - if (map.Height == 0) - return defaultValue; - } - - // we don't need to check `Height != 0` again cause in that case `key` will be `null` and `ReferenceEquals` will fail - return ReferenceEquals(key, map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, K key, out V value) - { - if (map.Height != 0) - { - var hash = key.GetHashCode(); - - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - if (key.Equals(map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Returns true if key is found and sets the value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, int hash, K key, out V value) - { - if (map.Height != 0) - { - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - if (key.Equals(map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Returns true if key is found and the result value. - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, Type key, out V value) - { - if (map.Height != 0) - { - var hash = RuntimeHelpers.GetHashCode(key); - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - // assign to `var data = ...` - if (ReferenceEquals(key, map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Returns true if hash and key are found and the result value, or the false otherwise - [MethodImpl((MethodImplOptions)256)] - public static bool TryFind(this ImHashMap map, int hash, Type key, out V value) - { - if (map.Height != 0) - { - while (hash != map.Hash && map.Height != 0) - map = hash < map.Hash ? map.Left : map.Right; - - if (map.Height != 0) - { - if (ReferenceEquals(key, map.Key)) - { - value = map.Value; - return true; - } - - return map.TryFindConflictedValue(key, out value); - } - } - - value = default; - return false; - } - - /// Uses `RuntimeHelpers.GetHashCode()` - public static ImHashMap AddOrUpdate(this ImHashMap map, Type key, V value) => - map.AddOrUpdate(RuntimeHelpers.GetHashCode(key), key, value); - } - - /// The array of ImHashMap slots where the key first bits are used for FAST slot location - /// and the slot is the reference to ImHashMap that can be swapped with its updated value - public static class ImHashMapSlots - { - /// Default number of slots - public const int SLOT_COUNT_POWER_OF_TWO = 32; - - /// The default mask to partition the key to the target slot - public const int HASH_MASK_TO_FIND_SLOT = SLOT_COUNT_POWER_OF_TWO - 1; - - /// Creates the array with the empty slots - [MethodImpl((MethodImplOptions)256)] - public static ImHashMap[] CreateWithEmpty(int slotCountPowerOfTwo = SLOT_COUNT_POWER_OF_TWO) - { - var slots = new ImHashMap[slotCountPowerOfTwo]; - for (var i = 0; i < slots.Length; ++i) - slots[i] = ImHashMap.Empty; - return slots; - } - - /// Returns a new tree with added or updated value for specified key. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(hash, key, value), copy) != copy) - RefAddOrUpdateSlot(ref slot, hash, key, value); - } - - /// Returns a new tree with added or updated value for specified key. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.AddOrUpdate(key.GetHashCode(), key, value, hashMaskToFindSlot); - - /// Updates the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrUpdateSlot(ref ImHashMap slot, int hash, K key, V value) => - Ref.Swap(ref slot, hash, key, value, (x, h, k, v) => x.AddOrUpdate(h, k, v)); - - /// Updates the value with help of `updateValue` function - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, int hash, K key, V value, Update update, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(hash, key, value, update), copy) != copy) - RefAddOrUpdateSlot(ref slot, hash, key, value, update); - } - - /// Updates the value with help of `updateValue` function - [MethodImpl((MethodImplOptions)256)] - public static void AddOrUpdate(this ImHashMap[] slots, K key, V value, Update updateValue, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.AddOrUpdate(key.GetHashCode(), key, value, updateValue, hashMaskToFindSlot); - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrUpdateSlot(ref ImHashMap slot, int hash, K key, V value, Update update) => - Ref.Swap(ref slot, hash, key, value, (x, h, k, v) => x.AddOrUpdate(h, k, v, update)); - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(hash, key, value), copy) != copy) - RefAddOrKeepSlot(ref slot, hash, key, value); - } - - /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. - [MethodImpl((MethodImplOptions)256)] - public static void AddOrKeep(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.AddOrKeep(key.GetHashCode(), key, value, hashMaskToFindSlot); - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefAddOrKeepSlot(ref ImHashMap slot, int hash, K key, V value) => - Ref.Swap(ref slot, hash, key, value, (s, h, k, v) => s.AddOrKeep(h, k, v)); - - /// Updates the specified slot or does not change it - [MethodImpl((MethodImplOptions)256)] - public static void Update(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) - { - ref var slot = ref slots[hash & hashMaskToFindSlot]; - var copy = slot; - if (Interlocked.CompareExchange(ref slot, copy.Update(hash, key, value), copy) != copy) - RefUpdateSlot(ref slot, hash, key, value); - } - - /// Updates the specified slot or does not change it - [MethodImpl((MethodImplOptions)256)] - public static void Update(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => - slots.Update(key.GetHashCode(), key, value, hashMaskToFindSlot); - - /// Update the ref to the slot with the new version - retry if the someone changed the slot in between - public static void RefUpdateSlot(ref ImHashMap slot, int hash, K key, V value) => - Ref.Swap(ref slot, key, value, (s, k, v) => s.Update(k, v)); - - /// Returns all map tree nodes without the order - public static S Fold(this ImHashMap[] slots, S state, Func, S, S> reduce) - { - var parentStack = ArrayTools.Empty>(); - for (var s = 0; s < slots.Length; s++) - { - var map = slots[s]; - var height = map.Height; - if (height != 0) - { - if (height > 1 && parentStack.Length < height) - parentStack = new ImHashMap[height]; - state = map.Fold(state, reduce, parentStack); - } - } - - return state; - } - } -} diff --git a/BattleFieldSimulator/ConsoleClient/Program.cs b/BattleFieldSimulator/ConsoleClient/Program.cs deleted file mode 100644 index 32b9984..0000000 --- a/BattleFieldSimulator/ConsoleClient/Program.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ConsoleClient -{ - internal class Program - { - public static void Main(string[] args) - { - } - } -} \ No newline at end of file diff --git a/BattleFieldSimulator/ConsoleClient/Properties/AssemblyInfo.cs b/BattleFieldSimulator/ConsoleClient/Properties/AssemblyInfo.cs deleted file mode 100644 index c9ac263..0000000 --- a/BattleFieldSimulator/ConsoleClient/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ConsoleClient")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ConsoleClient")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("35646D97-C2E5-4869-8995-2CB075E0631F")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/BattleFieldSimulator/ConsoleClient/packages.config b/BattleFieldSimulator/ConsoleClient/packages.config deleted file mode 100644 index 1aff88b..0000000 --- a/BattleFieldSimulator/ConsoleClient/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file