File: DynamicAccessorImpl.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\Extensions\PresentationFramework-SystemCore\PresentationFramework-SystemCore.csproj (PresentationFramework-SystemCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// Description: Implementation of DynamicAccessor derived types
 
using System;
using System.Dynamic;                   // GetMemberBinder, etc.
using System.Reflection;                // PropertyInfo, etc.
using System.Linq.Expressions;          // Expression
using System.Runtime.CompilerServices;  // CallSite
using MS.Internal.Data;                 // DynamicPropertyAccessor
 
namespace MS.Internal
{
    #region DynamicPropertyAccessorImpl
 
    internal class DynamicPropertyAccessorImpl : DynamicPropertyAccessor
    {
        internal DynamicPropertyAccessorImpl(Type ownerType, string propertyName)
            : base (ownerType, propertyName)
        {
        }
 
        public override object GetValue(object component)
        {
            if (_getter == null)
            {
                var binder = new TrivialGetMemberBinder(PropertyName);
                _getter = CallSite<Func<CallSite, object, object>>.Create(binder);
            }
 
            return _getter.Target(_getter, component);
        }
 
        public override void SetValue(object component, object value)
        {
            if (_setter == null)
            {
                var binder = new TrivialSetMemberBinder(PropertyName);
                _setter = CallSite<Action<CallSite, object, object>>.Create(binder);
            }
 
            _setter.Target(_setter, component, value);
        }
 
        CallSite<Func<CallSite, object, object>> _getter;
        CallSite<Action<CallSite, object, object>> _setter;
    }
 
    #endregion DynamicPropertyAccessorImpl
 
    #region DynamicIndexerAccessorImpl
 
    internal class DynamicIndexerAccessorImpl : DynamicIndexerAccessor
    {
        private DynamicIndexerAccessorImpl(int rank)
            : base(typeof(IDynamicMetaObjectProvider), "Items")
        {
            var getBinder = new TrivialGetIndexBinder(rank);
            var setBinder = new TrivialSetIndexBinder(rank);
 
            Type delegateType, callsiteType;
            MethodInfo createMethod;
            FieldInfo targetField;
            Type[] typeArgs;
            int i;
 
            // getter delegate type:  Func<CallSite, object, ..., object>
            typeArgs = new Type[rank+3];
            typeArgs[0] = typeof(CallSite);
            for (i=1; i<=rank+2; ++i)
            {
                typeArgs[i] = typeof(object);
            }
            delegateType = Expression.GetDelegateType(typeArgs);
 
            // getter CallSite:  CallSite<Func<CallSite, object, ..., object>>.Create(getBinder)
            callsiteType = typeof(CallSite<>).MakeGenericType(new Type[]{ delegateType });
            createMethod = callsiteType.GetMethod("Create", new Type[]{ typeof(CallSiteBinder) });
            _getterCallSite = (CallSite)createMethod.Invoke(null, new object[]{ getBinder });
 
            // getter delegate:  _getterCallSite.Target
            targetField = callsiteType.GetField("Target");
            _getterDelegate = (MulticastDelegate)targetField.GetValue(_getterCallSite);
 
 
            // setter delegate type:  Action<CallSite, object, ..., object>
            typeArgs = new Type[rank+4];
            typeArgs[0] = typeof(CallSite);
            typeArgs[rank+3] = typeof(void);
            for (i=1; i<=rank+2; ++i)
            {
                typeArgs[i] = typeof(object);
            }
            delegateType = Expression.GetDelegateType(typeArgs);
 
            // setter CallSite:  CallSite<Func<CallSite, object, ..., object>>.Create(setBinder)
            callsiteType = typeof(CallSite<>).MakeGenericType(new Type[]{ delegateType });
            createMethod = callsiteType.GetMethod("Create", new Type[]{ typeof(CallSiteBinder) });
            _setterCallSite = (CallSite)createMethod.Invoke(null, new object[]{ setBinder });
 
            // setter delegate:  _setterCallSite.Target
            targetField = callsiteType.GetField("Target");
            _setterDelegate = (MulticastDelegate)targetField.GetValue(_setterCallSite);
        }
 
        public override object GetValue(object component, object[] args)
        {
            int rank = args.Length;
            object[] delegateArgs = new object[rank + 2];
            delegateArgs[0] = _getterCallSite;
            delegateArgs[1] = component;
            Array.Copy(args, 0, delegateArgs, 2, rank);
 
            return _getterDelegate.DynamicInvoke(delegateArgs);
        }
 
        public override void SetValue(object component, object[] args, object value)
        {
            int rank = args.Length;
            object[] delegateArgs = new object[rank + 3];
            delegateArgs[0] = _setterCallSite;
            delegateArgs[1] = component;
            Array.Copy(args, 0, delegateArgs, 2, rank);
            delegateArgs[rank + 2] = value;
 
            _setterDelegate.DynamicInvoke(delegateArgs);
        }
 
        // ensure only one accessor for each rank
        public static DynamicIndexerAccessor GetIndexerAccessor(int rank)
        {
            if (_accessors.Length < rank || _accessors[rank-1] == null)
            {
                lock(_lock)
                {
                    if (_accessors.Length < rank)
                    {
                        DynamicIndexerAccessor[] newAccessors = new DynamicIndexerAccessor[rank];
                        Array.Copy(_accessors, 0, newAccessors, 0, _accessors.Length);
                        _accessors = newAccessors;
                    }
 
                    if (_accessors[rank-1] == null)
                    {
                        _accessors[rank-1] = new DynamicIndexerAccessorImpl(rank);
                    }
                }
            }
 
            return _accessors[rank-1];
        }
 
        CallSite            _getterCallSite, _setterCallSite;
        MulticastDelegate   _getterDelegate, _setterDelegate;
 
        static DynamicIndexerAccessor[] _accessors = new DynamicIndexerAccessor[1];
        static readonly object _lock = new object();
    }
 
    #endregion DynamicIndexerAccessor
 
    #region Trivial binders
 
    internal class TrivialGetMemberBinder : GetMemberBinder
    {
        public TrivialGetMemberBinder(string propertyName)
            : base(propertyName, false /*ignoreCase*/)
        {
        }
 
        public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target,
                                                            DynamicMetaObject errorSuggestion)
        {
            return errorSuggestion ??
                TrivialBinderHelper.ThrowExpression(DynamicObjectAccessor.MissingMemberErrorString(target, Name), ReturnType);
        }
    }
 
    internal class TrivialSetMemberBinder : SetMemberBinder
    {
        public TrivialSetMemberBinder(string propertyName)
            : base(propertyName, false /*ignoreCase*/)
        {
        }
 
        public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target,
                                                            DynamicMetaObject value,
                                                            DynamicMetaObject errorSuggestion)
        {
            return errorSuggestion ??
                TrivialBinderHelper.ThrowExpression(DynamicObjectAccessor.MissingMemberErrorString(target, Name), ReturnType);
        }
    }
 
    internal class TrivialGetIndexBinder : GetIndexBinder
    {
        public TrivialGetIndexBinder(int rank)
            : base(new CallInfo(rank))
        {
        }
 
        public override DynamicMetaObject FallbackGetIndex(DynamicMetaObject target,
                                                            DynamicMetaObject[] indexes,
                                                            DynamicMetaObject errorSuggestion)
        {
            return errorSuggestion ??
                TrivialBinderHelper.ThrowExpression(DynamicObjectAccessor.MissingMemberErrorString(target, "Items"), ReturnType);
        }
    }
 
    internal class TrivialSetIndexBinder : SetIndexBinder
    {
        public TrivialSetIndexBinder(int rank)
            : base(new CallInfo(rank))
        {
        }
 
        public override DynamicMetaObject FallbackSetIndex(DynamicMetaObject target,
                                                            DynamicMetaObject[] indexes,
                                                            DynamicMetaObject value,
                                                            DynamicMetaObject errorSuggestion)
        {
            return errorSuggestion ??
                TrivialBinderHelper.ThrowExpression(DynamicObjectAccessor.MissingMemberErrorString(target, "Items"), ReturnType);
        }
    }
 
    internal static class TrivialBinderHelper
    {
        public static DynamicMetaObject ThrowExpression(string message, Type returnType)
        {
            return new DynamicMetaObject(
                        Expression.Throw(
                            Expression.New(
                                typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) }),
                                Expression.Constant(message)
                            ),
                            returnType
                        ),
                        BindingRestrictions.Empty
                    );
        }
    }
 
    #endregion Trivial binders
}