< Summary

Information
Class: ImpromptuInterface.Optimization.DynamicConstructor
Assembly: ImpromptuInterface
File(s): /home/runner/work/impromptu-interface/impromptu-interface/ImpromptuInterface/src/Optimization/DynamicConstructor.cs
Line coverage
100%
Covered lines: 26
Uncovered lines: 0
Coverable lines: 26
Total lines: 90
Line coverage: 100%
Branch coverage
85%
Covered branches: 12
Total branches: 14
Branch coverage: 85.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
Invoke(...)75%88100%
Compile(...)100%66100%

File(s)

/home/runner/work/impromptu-interface/impromptu-interface/ImpromptuInterface/src/Optimization/DynamicConstructor.cs

#LineLine coverage
 1//
 2//  Copyright 2011 Ekon Benefits
 3//
 4//    Licensed under the Apache License, Version 2.0 (the "License");
 5//    you may not use this file except in compliance with the License.
 6//    You may obtain a copy of the License at
 7//
 8//        http://www.apache.org/licenses/LICENSE-2.0
 9//
 10//    Unless required by applicable law or agreed to in writing, software
 11//    distributed under the License is distributed on an "AS IS" BASIS,
 12//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13//    See the License for the specific language governing permissions and
 14//    limitations under the License.
 15
 16using System;
 17using System.Collections.Concurrent;
 18using System.Collections.Generic;
 19using System.Linq;
 20using System.Linq.Expressions;
 21using Microsoft.CSharp.RuntimeBinder;
 22using Binder = Microsoft.CSharp.RuntimeBinder.Binder;
 23
 24namespace ImpromptuInterface.Optimization
 25{
 26    /// <summary>
 27    /// `new T(args)` with the constructor chosen by the arguments' runtime types, as C#'s
 28    /// `new T(dynamicArg)` does — rather than by <see cref="Activator"/>, whose overload
 29    /// resolution is not the same.
 30    /// </summary>
 31    /// <remarks>
 32    /// The call site is built once per (type, argument count) and cached: the shape is what a
 33    /// site is compiled for, and the arguments' runtime types are what it dispatches on.
 34    /// </remarks>
 35    internal static class DynamicConstructor
 36    {
 137        private static readonly ConcurrentDictionary<Tuple<Type, int>, Func<object[], object>> _sites =
 138            new ConcurrentDictionary<Tuple<Type, int>, Func<object[], object>>();
 39
 40        /// <summary>Constructs <paramref name="type"/> with <paramref name="args"/>.</summary>
 41        internal static object Invoke(Type type, params object[] args)
 42        {
 1443            if (type == null) throw new ArgumentNullException(nameof(type));
 1444            args = args ?? new object[0];
 45
 46            // A dynamic invocation does not see a value type's parameterless constructor.
 1447            if (args.Length == 0 && type.IsValueType)
 148                return Activator.CreateInstance(type);
 49
 1350            var site = _sites.GetOrAdd(Tuple.Create(type, args.Length), key => Compile(key.Item1, key.Item2));
 1351            return site(args);
 52        }
 53
 54        private static Func<object[], object> Compile(Type type, int count)
 55        {
 456            var argsParameter = Expression.Parameter(typeof(object[]), "args");
 57
 58            // The type is the site's first argument, as a static type; each real argument
 59            // arrives as object, so the binder dispatches on its runtime type.
 460            var infos = new List<CSharpArgumentInfo>
 461            {
 462                CSharpArgumentInfo.Create(
 463                    CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null)
 464            };
 465            var arguments = new List<Expression> { Expression.Constant(type, typeof(Type)) };
 66
 1667            for (var i = 0; i < count; i++)
 68            {
 469                infos.Add(CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null));
 470                arguments.Add(Expression.ArrayIndex(argsParameter, Expression.Constant(i)));
 71            }
 72
 73            // The site's result type has to be one the binder can produce. For a value type the
 74            // binder produces the value type itself, and asking for object instead fails with
 75            // "The result type ... is not compatible with the result type ... expected by the
 76            // call site" - so ask for the type and box afterwards.
 477            var resultType = type.IsValueType ? type : typeof(object);
 78
 479            Expression call = Expression.Dynamic(
 480                Binder.InvokeConstructor(CSharpBinderFlags.None, type, infos),
 481                resultType,
 482                arguments);
 83
 484            if (resultType != typeof(object))
 185                call = Expression.Convert(call, typeof(object));
 86
 487            return Expression.Lambda<Func<object[], object>>(call, argsParameter).Compile();
 88        }
 89    }
 90}