Exemple #1
0
        private static ITypeReadAccessor Create(Type type, object @object, AccessorMemberTypes types,
                                                AccessorMemberScope scope, out AccessorMembers members)
        {
            lock (Sync)
            {
                var key = KeyForType(type, types, scope);

                if (AccessorCache.TryGetValue(key, out var accessor))
                {
                    members = type.IsAnonymous()
                        ? CreateAnonymousReadAccessorMembers(type)
                        : CreateReadAccessorMembers(type, types, scope);
                    return(accessor);
                }

                var anonymous = type.IsAnonymous();

                accessor = anonymous
                    ? CreateAnonymousReadAccessor(type, out members, @object)
                    : CreateReadAccessor(type, out members, types, scope);

                AccessorCache[key] = accessor;
                return(accessor);
            }
        }
        private static AccessorMember MaybeSwapMetadataMember(ICustomAttributeProvider authority, AccessorMember member,
                                                              string profile)
        {
            foreach (var attribute in authority.GetAttributes <MetadataTypeAttribute>())
            {
                if (attribute.Profile != profile)
                {
                    continue;
                }

                var types = AccessorMemberTypes.None;
                types |= member.MemberType switch
                {
                    AccessorMemberType.Field => AccessorMemberTypes.Fields,
                    AccessorMemberType.Property => AccessorMemberTypes.Properties,
                    AccessorMemberType.Method => AccessorMemberTypes.Methods,
                    _ => throw new ArgumentOutOfRangeException()
                };

                var members = AccessorMembers.Create(attribute.MetadataType, types, member.Scope);
                foreach (var m in members)
                {
                    if (m.Name != member.Name)
                    {
                        continue;
                    }
                    member = m;
                    break;
                }
            }

            return(member);
        }
Exemple #3
0
 public static ITypeReadAccessor Create(object @object, AccessorMemberTypes types, AccessorMemberScope scope,
                                        out AccessorMembers members)
 {
     if (@object is Type type)
     {
         return(Create(type, types, scope, out members));
     }
     type = @object.GetType();
     return(Create(type, @object, types, scope, out members));
 }
Exemple #4
0
        private static ITypeWriteAccessor CreateImpl(Type type, AccessorMemberTypes types, AccessorMemberScope scope,
                                                     out AccessorMembers members)
        {
            lock (Sync)
            {
                var key = KeyForType(type, types, scope);

                if (AccessorCache.TryGetValue(key, out var accessor))
                {
                    members = CreateWriteAccessorMembers(type, types, scope);
                    return(accessor);
                }

                accessor           = CreateWriteAccessor(type, types, scope, out members);
                AccessorCache[key] = accessor;
                return(accessor);
            }
        }
Exemple #5
0
        internal static bool NeedsLateBoundAccessor(this Type type, AccessorMembers members)
        {
            if (type.IsNotPublic)
            {
                return(true);
            }

            if (type.IsNestedPublic && type.DeclaringType != null && type.DeclaringType.IsNotPublic)
            {
                return(true);
            }

            foreach (var member in members)
            {
                switch (member.MemberInfo)
                {
                case FieldInfo field when !field.IsPublic:
                case PropertyInfo property when !property.GetGetMethod(true).IsPublic:
                    return(true);
                }
            }

            return(false);
        }
Exemple #6
0
        private static ITypeWriteAccessor CreateWriteAccessor(Type type, AccessorMemberTypes types,
                                                              AccessorMemberScope scope, out AccessorMembers members)
        {
            members = CreateWriteAccessorMembers(type, types, scope);

            if (type.NeedsLateBoundAccessor(members))
            {
                return(new LateBoundTypeWriteAccessor(members));
            }

            var name = type.CreateNameForWriteAccessor(members.Types, members.Scope);

            var tb = DynamicAssembly.Module.DefineType(name,
                                                       TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit |
                                                       TypeAttributes.AutoClass | TypeAttributes.AnsiClass);

            tb.AddInterfaceImplementation(typeof(ITypeWriteAccessor));

            //
            // Generate proxies for all backing field setters we will need for this type
            //
            var setFieldDelegateMethods    = new Dictionary <MethodBuilder, Delegate>();
            var getFieldDelegateMethods    = new List <MethodBuilder>();
            var invokeFieldDelegateMethods = new List <MethodBuilder>();
            var callSwaps = new Dictionary <AccessorMember, MethodInfo>();

            foreach (var member in members)
            {
                if (!member.CanWrite)
                {
                    continue; // can't write / is a computed property
                }
                if (!(member.MemberInfo is PropertyInfo property))
                {
                    continue; // not a property
                }
                if (property.GetSetMethod(true) != null)
                {
                    continue; // has public setter
                }
                var backingField = member.BackingField;
                if (backingField == null)
                {
                    continue;
                }

                var setFieldMethod = new DynamicMethod($"set_{member.Name}", typeof(void),
                                                       new[] { backingField.DeclaringType, backingField.FieldType }, backingField.DeclaringType, true);

                var setFieldIl = setFieldMethod.GetILGeneratorInternal();
                setFieldIl.Ldarg_0();
                setFieldIl.Ldarg_1();
                setFieldIl.Stfld(backingField);
                setFieldIl.Ret();

                var setFieldDelegateType =
                    typeof(Action <,>).MakeGenericType(backingField.DeclaringType, backingField.FieldType);
                var setFieldDelegate = setFieldMethod.CreateDelegate(setFieldDelegateType);

                // At this point we have a valid delegate that will set the private backing field...

                var setFieldDelegateField = tb.DefineField(
                    $"_setFieldDelegate_{member.Type.Namespace}_{member.Type.Name}_{backingField.Name}",
                    setFieldDelegateType,
                    FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly);

                var setFieldDelegateMethod = tb.DefineMethod(
                    $"_set_setFieldDelegate_{member.Type.Namespace}_{member.Type.Name}_{backingField.Name}",
                    MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard,
                    typeof(void),
                    new[] { setFieldDelegateType });

                var setFieldDelegateIl = setFieldDelegateMethod.GetILGeneratorInternal();
                setFieldDelegateIl.Ldarg_0();
                setFieldDelegateIl.Stsfld(setFieldDelegateField);
                setFieldDelegateIl.Ret();

                setFieldDelegateMethods.Add(setFieldDelegateMethod, setFieldDelegate);

                // At this point we've defined a static field to store our delegate setter and a method to set that field...

                var getFieldDelegateMethod = tb.DefineMethod(
                    $"_get_setFieldDelegate_{member.Type.Namespace}_{member.Type.Name}_{backingField.Name}",
                    MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard,
                    setFieldDelegateType, null);

                var getFieldDelegateIl = getFieldDelegateMethod.GetILGeneratorInternal();
                getFieldDelegateIl.Ldsfld(setFieldDelegateField);
                getFieldDelegateIl.Ret();

                getFieldDelegateMethods.Add(getFieldDelegateMethod);

                // At this point we can get and set the static field delegates, and now we need to cache a method to invoke them...

                var invokeFieldDelegateMethod = tb.DefineMethod(
                    $"_invoke_setFieldDelegate_{member.Type.Namespace}_{member.Type.Name}_{backingField.Name}",
                    MethodAttributes.Static | MethodAttributes.Private, CallingConventions.Standard,
                    typeof(void),
                    new[] { backingField.DeclaringType, backingField.FieldType });

                var invokeFieldDelegateIl = invokeFieldDelegateMethod.GetILGenerator();
                invokeFieldDelegateIl.Emit(OpCodes.Ldsfld, setFieldDelegateField);
                invokeFieldDelegateIl.Emit(OpCodes.Ldarg_0);
                invokeFieldDelegateIl.Emit(OpCodes.Ldarg_1);
                invokeFieldDelegateIl.Emit(OpCodes.Callvirt, setFieldDelegateType.GetMethod("Invoke"));
                invokeFieldDelegateIl.Emit(OpCodes.Ret);

                invokeFieldDelegateMethods.Add(invokeFieldDelegateMethod);
                callSwaps.Add(member, invokeFieldDelegateMethod);
            }

            //
            // Type Type =>:
            //
            {
                var getType = tb.DefineMethod($"get_{nameof(ITypeWriteAccessor.Type)}",
                                              MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                              MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.SpecialName, typeof(Type),
                                              Type.EmptyTypes);
                var il = getType.GetILGeneratorInternal();
                il.Ldtoken(type);
                il.CallOrCallvirt(type,
                                  typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), BindingFlags.Static | BindingFlags.Public));
                il.Ret();

                var getTypeProperty = tb.DefineProperty(nameof(ITypeWriteAccessor.Type), PropertyAttributes.None,
                                                        typeof(object), new[] { typeof(string) });
                getTypeProperty.SetGetMethod(getType);

                tb.DefineMethodOverride(getType,
                                        typeof(ITypeWriteAccessor).GetMethod($"get_{nameof(ITypeWriteAccessor.Type)}"));
            }

            //
            // bool TrySetValue(object target, string key, object value):
            //
            {
                var trySetValue = tb.DefineMethod(nameof(IWriteAccessor.TrySetValue),
                                                  MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                                  MethodAttributes.Virtual | MethodAttributes.NewSlot, typeof(bool),
                                                  new[] { typeof(object), typeof(string), typeof(object) });
                var il = trySetValue.GetILGeneratorInternal();

                var branches = new Dictionary <AccessorMember, Label>();
                foreach (var member in members)
                {
                    if (!member.CanWrite)
                    {
                        continue;
                    }

                    branches.Add(member, il.DefineLabel());
                }

                foreach (var member in members)
                {
                    if (!member.CanWrite)
                    {
                        continue;
                    }

                    il.Ldarg_2();                                         // key
                    il.GotoIfStringEquals(member.Name, branches[member]); // if (key == "{member.Name}") goto found;
                }

                foreach (var member in members)
                {
                    if (!member.CanWrite)
                    {
                        continue;
                    }

                    il.MarkLabel(branches[member]); // found:

                    il.Ldarg_1();                   // target
                    il.CastOrUnbox(type);           // ({Type}) target
                    il.Ldarg_3();                   // value

                    switch (member.MemberInfo)      // target.{member.Name} = value
                    {
                    case PropertyInfo property:

                        il.CastOrUnboxAny(property.PropertyType);
                        il.CallOrCallvirt(type, GetOrSwapPropertySetter(property, callSwaps, member));
                        break;

                    case FieldInfo field:
                        il.CastOrUnboxAny(field.FieldType);
                        il.Stfld(field);
                        break;
                    }

                    il.Ldc_I4_1(); //     1
                    il.Ret();      //     return 1 (true)
                }

                il.Ldnull();   //     null
                il.Starg_S();  //     value = null
                il.Ldc_I4_0(); //     0
                il.Ret();      //     return 0 (false)

                tb.DefineMethodOverride(trySetValue,
                                        typeof(IWriteAccessor).GetMethod(nameof(IWriteAccessor.TrySetValue)));
            }

            //
            // object this[object target, string key] = object value:
            //
            {
                var setItem = tb.DefineMethod("set_Item",
                                              MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                              MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.SpecialName, typeof(void),
                                              new[] { typeof(object), typeof(string), typeof(object) });
                var il = setItem.GetILGeneratorInternal();

                var branches = new Dictionary <AccessorMember, Label>();
                foreach (var member in members)
                {
                    if (!member.CanWrite)
                    {
                        continue;
                    }

                    branches.Add(member, il.DefineLabel());
                }

                foreach (var member in members)
                {
                    if (!member.CanWrite)
                    {
                        continue;
                    }

                    il.Ldarg_2();                                         // key
                    il.GotoIfStringEquals(member.Name, branches[member]); // if (key == "{member.Name}") goto found;
                }

                foreach (var member in members)
                {
                    if (!member.CanWrite)
                    {
                        continue;
                    }

                    il.MarkLabel(branches[member]); // found:

                    il.Ldarg_1();                   // target
                    il.CastOrUnbox(type);           // ({Type}) target
                    il.Ldarg_3();                   // value

                    switch (member.MemberInfo)      // result = target.{member.Name}
                    {
                    case PropertyInfo property:
                        il.CastOrUnboxAny(property.PropertyType);
                        il.CallOrCallvirt(type, GetOrSwapPropertySetter(property, callSwaps, member));
                        break;

                    case FieldInfo field:
                        il.CastOrUnboxAny(field.FieldType);
                        il.Stfld(field);
                        break;
                    }

                    il.Ret(); // return result;
                }

                il.Newobj(typeof(ArgumentNullException).GetConstructor(Type.EmptyTypes)).Throw();

                var item = tb.DefineProperty("Item", PropertyAttributes.SpecialName, typeof(object),
                                             new[] { typeof(string) });
                item.SetSetMethod(setItem);

                tb.DefineMethodOverride(setItem, typeof(IWriteAccessor).GetMethod("set_Item"));
            }

            var typeInfo = tb.CreateTypeInfo();

            // Now we need to set the static delegate fields on this type...

            foreach (var entry in setFieldDelegateMethods)
            {
                var setFieldMethod = typeInfo.GetMethod(entry.Key.Name, BindingFlags.Static | BindingFlags.NonPublic);
                if (setFieldMethod == null)
                {
                    continue;
                }

                setFieldMethod.Invoke(null, new object[] { entry.Value });
            }

            // Test that we can access the static fields correctly...

            foreach (var entry in getFieldDelegateMethods)
            {
                var getFieldMethod = typeInfo.GetMethod(entry.Name, BindingFlags.Static | BindingFlags.NonPublic);
                if (getFieldMethod == null)
                {
                    continue;
                }

                var field = getFieldMethod.Invoke(null, null);
            }

            // Finally, test that we can invoke the delegate and set a backing field

            foreach (var entry in invokeFieldDelegateMethods)
            {
                if (entry.Name != "_invoke_setFieldDelegate_System_String_<Id>k__BackingField")
                {
                    continue;
                }

                var invokeFieldMethod = typeInfo.GetMethod(entry.Name, BindingFlags.Static | BindingFlags.NonPublic);
                if (invokeFieldMethod == null)
                {
                    throw new NullReferenceException();
                }

                var test = Activator.CreateInstance(type);
                invokeFieldMethod.Invoke(null, new[] { test, "Foo" });
            }

            return((ITypeWriteAccessor)Activator.CreateInstance(typeInfo.AsType(), false));
        }
Exemple #7
0
 private static AccessorMembers CreateReadAccessorMembers(Type type,
                                                          AccessorMemberTypes types = AccessorMemberTypes.Fields | AccessorMemberTypes.Properties,
                                                          AccessorMemberScope scope = AccessorMemberScope.All)
 {
     return(AccessorMembers.Create(type, types, scope));
 }
Exemple #8
0
 public static ITypeReadAccessor Create(Type type, out AccessorMembers members)
 {
     return(Create(type, AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope.All,
                   out members));
 }
Exemple #9
0
 public static ITypeReadAccessor Create(Type type, AccessorMemberTypes types, AccessorMemberScope scope,
                                        out AccessorMembers members)
 {
     return(Create(type, null, types, scope, out members));
 }
 public LateBoundTypeReadAccessor(AccessorMembers members)
 {
     Type     = members.DeclaringType;
     _binding = LateBinding.DynamicMethodBindGet(members);
 }
Exemple #11
0
 public static ITypeWriteAccessor Create(object @object, out AccessorMembers members)
 {
     return(Create(@object, AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope.All,
                   out members));
 }
Exemple #12
0
 public static Dictionary <string, Action <object, object> > MethodInvokeBindSet(AccessorMembers members)
 {
     return(members.Members.Where(member => member.CanRead)
            .ToDictionary(member => member.Name, MethodInvokeBindSet));
 }
Exemple #13
0
 public static Dictionary <string, Action <object, object> > DynamicMethodBindSet(AccessorMembers members)
 {
     return(members.Members.Where(member => member.CanWrite)
            .ToDictionary(member => member.Name, DynamicMethodBindSet));
 }
Exemple #14
0
 public static Dictionary <string, Action <object, object> > OpenDelegateBindSet <TTarget>(AccessorMembers members)
     where TTarget : class
 {
     return(members.Members.Where(member => member.CanWrite)
            .ToDictionary(member => member.Name, OpenDelegateBindSet <TTarget>));
 }
Exemple #15
0
 public static ITypeReadAccessor Create(object @object, AccessorMemberTypes types, out AccessorMembers members)
 {
     return(Create(@object, types, AccessorMemberScope.All,
                   out members));
 }
Exemple #16
0
        private static ITypeReadAccessor CreateReadAccessor(Type type, out AccessorMembers members,
                                                            AccessorMemberTypes types = AccessorMemberTypes.Fields | AccessorMemberTypes.Properties,
                                                            AccessorMemberScope scope = AccessorMemberScope.All)
        {
            members = CreateReadAccessorMembers(type, types, scope);

            if (type.NeedsLateBoundAccessor(members))
            {
                return(new LateBoundTypeReadAccessor(members));
            }

            var name = type.CreateNameForReadAccessor(members.Types, members.Scope);

            TypeBuilder tb;

            try
            {
                tb = DynamicAssembly.Module.DefineType(
                    name,
                    TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit |
                    TypeAttributes.AutoClass | TypeAttributes.AnsiClass);

                tb.AddInterfaceImplementation(typeof(ITypeReadAccessor));
            }
            catch (ArgumentException e)
            {
                if (e.Message == "Duplicate type name within an assembly.")
                {
                    throw new ArgumentException($"Duplicate type name within an assembly: {name}", nameof(name),
                                                e);
                }
                throw;
            }

            //
            // Type Type =>:
            //
            tb.MemberProperty(nameof(ITypeReadAccessor.Type), type,
                              typeof(ITypeReadAccessor).GetMethod($"get_{nameof(ITypeReadAccessor.Type)}"));

            //
            // bool TryGetValue(object target, string key, out object value):
            //
            {
                var tryGetValue = tb.DefineMethod(nameof(ITypeReadAccessor.TryGetValue),
                                                  MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                                  MethodAttributes.Virtual | MethodAttributes.NewSlot, typeof(bool),
                                                  new[] { typeof(object), typeof(string), typeof(object).MakeByRefType() });

                var il = tryGetValue.GetILGeneratorInternal();

                var branches = new Dictionary <AccessorMember, Label>();
                foreach (var member in members)
                {
                    branches.Add(member, il.DefineLabel());
                }
                var fail = il.DefineLabel();

                foreach (var member in members)
                {
                    il.Ldarg_2();                                         // key
                    il.GotoIfStringEquals(member.Name, branches[member]); // if (key == "Foo") goto found;
                }

                il.Br(fail);

                foreach (var member in members)
                {
                    il.MarkLabel(branches[member]); // found:
                    il.Ldarg_3();                   // value
                    il.Ldarg_1();                   // target
                    il.CastOrUnbox(type);           // ({Type}) target
                    switch (member.MemberInfo)      // result = target.{member.Name}
                    {
                    case PropertyInfo property:
                        var getMethod = property.GetGetMethod(true);
                        il.Callvirt(getMethod);
                        break;

                    case FieldInfo field:
                        il.Ldfld(field);
                        break;
                    }

                    il.MaybeBox(member.Type); // (object) result
                    il.Stind_Ref();           // value = result
                    il.Ldc_I4_1();            // 1
                    il.Ret();                 // return 1 (true)
                }

                il.MarkLabel(fail);
                il.Ldarg_3();   // value
                il.Ldnull();    // null
                il.Stind_Ref(); // value = null
                il.Ldc_I4_0();  // 0
                il.Ret();       // return 0 (false)

                tb.DefineMethodOverride(tryGetValue,
                                        typeof(IReadAccessor).GetMethod(nameof(IReadAccessor.TryGetValue)));
            }

            //
            // object this[object target, string key]:
            //
            {
                var getItem = tb.DefineMethod("get_Item",
                                              MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                              MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.SpecialName, typeof(object),
                                              new[] { typeof(object), typeof(string) });

                var il = getItem.GetILGeneratorInternal();

                var branches = new Dictionary <AccessorMember, Label>();
                foreach (var member in members)
                {
                    branches.Add(member, il.DefineLabel());
                }

                foreach (var member in members)
                {
                    il.Ldarg_2();                                         // key
                    il.GotoIfStringEquals(member.Name, branches[member]); // if (key == "{member.Name}") goto found;
                }

                foreach (var member in members)
                {
                    il.MarkLabel(branches[member]);
                    il.Ldarg_1();              // target
                    il.CastOrUnbox(type);      // ({Type}) target

                    switch (member.MemberInfo) // result = target.Foo
                    {
                    case PropertyInfo property:
                        var getMethod = property.GetGetMethod(true);
                        il.Callvirt(getMethod);
                        break;

                    case FieldInfo field:
                        il.Ldfld(field);
                        break;
                    }

                    il.MaybeBox(member.Type); // (object) result
                    il.Ret();                 // return result;
                }

                il.Newobj(typeof(ArgumentNullException).GetConstructor(Type.EmptyTypes));
                il.Throw();

                var getItemProperty = tb.DefineProperty("Item", PropertyAttributes.SpecialName, typeof(object),
                                                        new[] { typeof(string) });
                getItemProperty.SetGetMethod(getItem);

                tb.DefineMethodOverride(getItem, typeof(IReadAccessor).GetMethod("get_Item"));
            }

            var typeInfo = tb.CreateTypeInfo();

            return((ITypeReadAccessor)Activator.CreateInstance(typeInfo.AsType(), false));
        }
Exemple #17
0
 private static AccessorMembers CreateWriteAccessorMembers(Type type, AccessorMemberTypes types,
                                                           AccessorMemberScope scope)
 {
     return(AccessorMembers.Create(type, types, scope));
 }
Exemple #18
0
 public static ITypeWriteAccessor Create(Type type, AccessorMemberTypes types, AccessorMemberScope scope,
                                         out AccessorMembers members)
 {
     return(CreateImpl(type, types, scope, out members));
 }
Exemple #19
0
 public static Dictionary <string, Func <object, object> > ExpressionBindGet(AccessorMembers members)
 {
     return(members.Members.Where(member => member.CanRead)
            .ToDictionary(member => member.Name, ExpressionBindGet));
 }
Exemple #20
0
 private static AccessorMembers CreateAnonymousReadAccessorMembers(Type type)
 {
     return(AccessorMembers.Create(type, AccessorMemberTypes.Properties, AccessorMemberScope.Public));
 }
Exemple #21
0
 public static Dictionary <string, Func <object, object[], object> > DynamicMethodBindCall(AccessorMembers members)
 {
     return(members.Members.Where(member => member.CanCall && member.IsInstanceMethod)
            .ToDictionary(member => member.Name, DynamicMethodBindCall));
 }
Exemple #22
0
        /// <summary>
        ///     Anonymous types only have private readonly properties with no logic before their backing fields, so we can do
        ///     a lot to optimize access to them, though we must delegate the access itself due to private reflection rules.
        /// </summary>
        private static ITypeReadAccessor CreateAnonymousReadAccessor(Type type, out AccessorMembers members,
                                                                     object debugObject = null)
        {
            members = CreateAnonymousReadAccessorMembers(type);

            var name = type.CreateNameForReadAccessor(members.Types, members.Scope);

            var tb = DynamicAssembly.Module.DefineType(name,
                                                       TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit |
                                                       TypeAttributes.AutoClass | TypeAttributes.AnsiClass);

            tb.AddInterfaceImplementation(typeof(ITypeReadAccessor));

            //
            // Perf: Add static delegates on the type, that store access to the backing fields behind readonly properties.
            //
            var staticFieldsByMethod = new Dictionary <MethodBuilder, Func <object, object> >();
            var staticFieldsByMember = new Dictionary <AccessorMember, FieldBuilder>();

            foreach (var member in members)
            {
                var backingField = type.GetField($"<{member.Name}>i__Field",
                                                 BindingFlags.NonPublic | BindingFlags.Instance);
                if (backingField == null)
                {
                    throw new NullReferenceException();
                }

                var dm   = new DynamicMethod($"_{member.Name}", typeof(object), new[] { typeof(object) }, tb.Module);
                var dmIl = dm.GetILGeneratorInternal();
                dmIl.Ldarg_0()
                .Ldfld(backingField);
                if (backingField.FieldType.IsValueType)
                {
                    dmIl.Box(backingField.FieldType);
                }
                dmIl.Ret();
                var backingFieldDelegate = (Func <object, object>)dm.CreateDelegate(typeof(Func <object, object>));

                var getField = tb.DefineField($"_Get{member.Name}", typeof(Func <object, object>),
                                              FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);
                var setField = tb.DefineMethod($"_SetGet{member.Name}",
                                               MethodAttributes.Static | MethodAttributes.Private, CallingConventions.Standard, typeof(void),
                                               new[] { typeof(Func <object, object>) });

                var setFieldIl = setField.GetILGeneratorInternal();
                setFieldIl.Ldarg_0();
                setFieldIl.Stsfld(getField);
                setFieldIl.Ret();

                staticFieldsByMethod.Add(setField, backingFieldDelegate);
                staticFieldsByMember.Add(member, getField);
            }

            //
            // Type Type =>:
            //
            {
                tb.MemberProperty(nameof(ITypeReadAccessor.Type), type,
                                  typeof(ITypeReadAccessor).GetMethod($"get_{nameof(ITypeReadAccessor.Type)}"));
            }

            //
            // bool TryGetValue(object target, string key, out object value):
            //
            {
                var tryGetValue = tb.DefineMethod(nameof(ITypeReadAccessor.TryGetValue),
                                                  MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                                  MethodAttributes.Virtual | MethodAttributes.NewSlot, typeof(bool),
                                                  new[] { typeof(object), typeof(string), typeof(object).MakeByRefType() });

                var il = tryGetValue.GetILGeneratorInternal();

                var branches = new Dictionary <AccessorMember, Label>();
                foreach (var member in members)
                {
                    branches.Add(member, il.DefineLabel());
                }

                var fail = il.DefineLabel();

                foreach (var member in members)
                {
                    il.Ldarg_2();                                         // key
                    il.GotoIfStringEquals(member.Name, branches[member]); // if(key == "Foo") goto found;
                }

                il.Br(fail);

                foreach (var member in members)
                {
                    var fb = staticFieldsByMember[member];

                    il.MarkLabel(branches[member]);            // found:
                    il.Ldarg_3();                              //     value
                    il.Ldsfld(fb);                             //     _GetFoo
                    il.Ldarg_1();                              //     target
                    il.Call(fb.FieldType.GetMethod("Invoke")); //     result = _GetFoo.Invoke(target)
                    il.Stind_Ref();                            //     value = result
                    il.Ldc_I4_1();                             //     1
                    il.Ret();                                  //     return 1 (true)
                }

                il.MarkLabel(fail);
                il.Ldarg_3();   //     value
                il.Ldnull();    //     null
                il.Stind_Ref(); //     value = null
                il.Ldc_I4_0();  //     0
                il.Ret();       //     return 0 (false)

                tb.DefineMethodOverride(tryGetValue,
                                        typeof(IReadAccessor).GetMethod(nameof(IReadAccessor.TryGetValue)));
            }

            //
            // object this[object target, string key]:
            //
            {
                var getItem = tb.DefineMethod("get_Item",
                                              MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                              MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.SpecialName, typeof(object),
                                              new[] { typeof(object), typeof(string) });

                var il = getItem.GetILGeneratorInternal();

                var branches = new Dictionary <AccessorMember, Label>();
                foreach (var member in members)
                {
                    branches.Add(member, il.DefineLabel());
                }

                foreach (var member in members)
                {
                    il.Ldarg_2();                                         // key
                    il.GotoIfStringEquals(member.Name, branches[member]); // if(key == "Foo") goto found;
                }

                foreach (var member in members)
                {
                    var fb = staticFieldsByMember[member];

                    il.MarkLabel(branches[member]);            // found:
                    il.Ldsfld(fb);                             // _GetFoo
                    il.Ldarg_1();                              // target
                    il.Call(fb.FieldType.GetMethod("Invoke")); //     result = _GetFoo.Invoke(target)
                    il.Ret();                                  // return result;
                }

                il.Newobj(typeof(ArgumentNullException).GetConstructor(Type.EmptyTypes));
                il.Throw();

                var item = tb.DefineProperty("Item", PropertyAttributes.SpecialName, typeof(object),
                                             new[] { typeof(string) });
                item.SetGetMethod(getItem);

                tb.DefineMethodOverride(getItem, typeof(IReadAccessor).GetMethod("get_Item"));
            }

            var typeInfo = tb.CreateTypeInfo();

            //
            // Perf: Set static field values to generated delegate instances.
            //
            foreach (var setter in staticFieldsByMethod)
            {
                var setField = typeInfo.GetMethod(setter.Key.Name, BindingFlags.Static | BindingFlags.NonPublic);
                if (setField == null)
                {
                    throw new NullReferenceException();
                }
                setField.Invoke(null, new object[] { setter.Value });

                if (debugObject != null)
                {
                    var memberName = setter.Key.Name.Replace("_SetGet", string.Empty);

                    var staticFieldFunc =
                        (Func <object, object>)typeInfo.GetField($"_Get{memberName}").GetValue(debugObject);
                    if (staticFieldFunc != setter.Value)
                    {
                        throw new ArgumentException(
                                  $"replacing _Get{memberName} with function from _SetGet{memberName} was unsuccessful");
                    }

                    var backingField = type.GetField($"<{memberName}>i__Field",
                                                     BindingFlags.NonPublic | BindingFlags.Instance);
                    if (backingField == null)
                    {
                        throw new NullReferenceException("backing field was not found");
                    }

                    var backingFieldValue   = backingField.GetValue(debugObject);
                    var cachedDelegateValue = setter.Value(debugObject);

                    if (backingFieldValue != null && !backingFieldValue.Equals(cachedDelegateValue))
                    {
                        throw new ArgumentException(
                                  $"{memberName} backing field value '{backingFieldValue}' does not agree with cached delegate value {cachedDelegateValue}");
                    }
                }
            }

            var accessor = (ITypeReadAccessor)Activator.CreateInstance(typeInfo, false);

            if (debugObject != null)
            {
                foreach (var member in members)
                {
                    var byAccessor   = accessor[debugObject, member.Name];
                    var byReflection =
                        ((Func <object, object>)typeInfo.GetField($"_Get{member.Name}").GetValue(debugObject))(
                            debugObject);
                    if (byAccessor != null && !byAccessor.Equals(byReflection))
                    {
                        throw new InvalidOperationException("IL produced incorrect accessor");
                    }
                }
            }

            return(accessor);
        }
Exemple #23
0
        private static ITypeCallAccessor CreateTypeCallAccessor(Type type,
                                                                AccessorMemberScope scope = AccessorMemberScope.All)
        {
            var members = AccessorMembers.Create(type, AccessorMemberTypes.Methods, scope);

            var name = type.CreateNameForCallAccessor();

            var tb = DynamicAssembly.Module.DefineType(
                name,
                TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoClass | TypeAttributes.AnsiClass);

            tb.AddInterfaceImplementation(typeof(ITypeCallAccessor));

            //
            // Type Type =>:
            //
            tb.MemberProperty(nameof(ITypeCallAccessor.Type), type,
                              typeof(ITypeCallAccessor).GetMethod($"get_{nameof(ITypeCallAccessor.Type)}"));

            //
            // object Call(object target, string name, params object[] args):
            //
            {
                var call = tb.DefineMethod(nameof(ITypeCallAccessor.Call),
                                           MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                           MethodAttributes.Virtual | MethodAttributes.NewSlot,
                                           typeof(object), new[] { typeof(object), typeof(string), typeof(object[]) });

                var il = call.GetILGeneratorInternal();

                var branches = new Dictionary <AccessorMember, Label>();
                foreach (var member in members)
                {
                    if (!member.IsInstanceMethod)
                    {
                        continue;
                    }

                    branches.Add(member, il.DefineLabel());
                }

                il.DeclareLocal(typeof(string));
                il.DeclareLocal(typeof(object));
                il.Nop();
                il.Ldarg_2();
                il.Stloc_0();

                foreach (var member in members)
                {
                    if (!member.IsInstanceMethod)
                    {
                        continue;
                    }

                    il.Ldloc_0();
                    il.GotoIfStringEquals(member.Name, branches[member]);
                }

                foreach (var member in members)
                {
                    if (!member.IsInstanceMethod)
                    {
                        continue;
                    }

                    var method         = (MethodInfo)member.MemberInfo;
                    var parameters     = method.GetParameters();
                    var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();

                    il.MarkLabel(branches[member]);
                    il.Ldarg_1();
                    il.CastOrUnbox(method.DeclaringType);

                    var returns = method.ReturnType != typeof(void);
                    if (returns)
                    {
                        continue;
                        throw new NotImplementedException();
                    }

                    if (parameters.Length > 0)
                    {
                        il.Ldarg_3();
                        il.Ldc_I4_S((byte)parameters.Length);
                        il.Ldelem_Ref();
                        il.Unbox_Any(parameterTypes[0]);
                    }

                    il.Callvirt(method);
                    il.Nop();

                    il.Ldtoken(typeof(void));
                    il.CallOrCallvirt(type, KnownMethods.GetTypeFromHandle);
                    il.Stloc_1();
                    il.Ldloc_1();
                    il.Ret();
                }

                il.Newobj(typeof(ArgumentNullException).GetConstructor(Type.EmptyTypes));
                il.Throw();

                tb.DefineMethodOverride(call, typeof(ITypeCallAccessor).GetMethod(nameof(ITypeCallAccessor.Call)));
            }

            var typeInfo = tb.CreateTypeInfo();

            return((ITypeCallAccessor)Activator.CreateInstance(typeInfo.AsType(), false));
        }
Exemple #24
0
 public static ITypeReadAccessor Create(object @object, AccessorMemberScope scope, out AccessorMembers members)
 {
     return(Create(@object, AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, scope,
                   out members));
 }