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); }
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)); }
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); } }
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); }
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)); }
private static AccessorMembers CreateReadAccessorMembers(Type type, AccessorMemberTypes types = AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope scope = AccessorMemberScope.All) { return(AccessorMembers.Create(type, types, scope)); }
public static ITypeReadAccessor Create(Type type, out AccessorMembers members) { return(Create(type, AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope.All, out members)); }
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); }
public static ITypeWriteAccessor Create(object @object, out AccessorMembers members) { return(Create(@object, AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope.All, out members)); }
public static Dictionary <string, Action <object, object> > MethodInvokeBindSet(AccessorMembers members) { return(members.Members.Where(member => member.CanRead) .ToDictionary(member => member.Name, MethodInvokeBindSet)); }
public static Dictionary <string, Action <object, object> > DynamicMethodBindSet(AccessorMembers members) { return(members.Members.Where(member => member.CanWrite) .ToDictionary(member => member.Name, DynamicMethodBindSet)); }
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>)); }
public static ITypeReadAccessor Create(object @object, AccessorMemberTypes types, out AccessorMembers members) { return(Create(@object, types, AccessorMemberScope.All, out members)); }
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)); }
private static AccessorMembers CreateWriteAccessorMembers(Type type, AccessorMemberTypes types, AccessorMemberScope scope) { return(AccessorMembers.Create(type, types, scope)); }
public static ITypeWriteAccessor Create(Type type, AccessorMemberTypes types, AccessorMemberScope scope, out AccessorMembers members) { return(CreateImpl(type, types, scope, out members)); }
public static Dictionary <string, Func <object, object> > ExpressionBindGet(AccessorMembers members) { return(members.Members.Where(member => member.CanRead) .ToDictionary(member => member.Name, ExpressionBindGet)); }
private static AccessorMembers CreateAnonymousReadAccessorMembers(Type type) { return(AccessorMembers.Create(type, AccessorMemberTypes.Properties, AccessorMemberScope.Public)); }
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)); }
/// <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); }
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)); }
public static ITypeReadAccessor Create(object @object, AccessorMemberScope scope, out AccessorMembers members) { return(Create(@object, AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, scope, out members)); }