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 AccessorMembers(Type type, AccessorMemberTypes types, AccessorMemberScope scope) { DeclaringType = type; DisplayName = GetDisplayName(); Types = types; Scope = scope; NameToMember = new Dictionary <string, AccessorMember>(StringComparer.OrdinalIgnoreCase); var flags = BindingFlags.Instance | BindingFlags.Static; if (scope.HasFlagFast(AccessorMemberScope.Public)) { flags |= BindingFlags.Public; } if (scope.HasFlagFast(AccessorMemberScope.Private)) { flags |= BindingFlags.NonPublic; } // First pass: everything SetWith(type, types, scope, flags); // Second pass: declared only (prefer overrides) SetWith(type, types, scope, flags | BindingFlags.DeclaredOnly); var fields = FieldInfo ?? Enumerable.Empty <FieldInfo>(); var properties = PropertyInfo ?? Enumerable.Empty <PropertyInfo>(); var methods = MethodInfo ?? Enumerable.Empty <MethodInfo>(); MemberInfo = fields.Cast <MemberInfo>().Concat(properties).Concat(methods).OrderBy(TryOrderMemberInfo).ToArray(); Members = NameToMember.Values.OrderBy(TryOrderMember).ToList(); }
private static AccessorMembersKey KeyForType(Type type, AccessorMemberTypes types, AccessorMemberScope scope) { var key = type.IsAnonymous() ? new AccessorMembersKey(type, AccessorMemberTypes.Properties, AccessorMemberScope.Public) : new AccessorMembersKey(type, types, scope); return(key); }
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)); }
public static ITypeReadAccessor Create(object @object, AccessorMemberTypes types = AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope scope = AccessorMemberScope.All) { if (@object is Type type) { return(Create(type, types, scope)); } type = @object.GetType(); return(Create(type, @object, types, scope, out _)); }
private void SetWith(Type type, AccessorMemberTypes types, AccessorMemberScope scope, BindingFlags flags) { if (types.HasFlagFast(AccessorMemberTypes.Fields)) { var fields = type.GetFields(flags).OrderBy(f => f.Name); FieldInfo = FieldInfo == null?fields.ToArray() : FieldInfo.Concat(fields).Distinct().ToArray(); foreach (var field in FieldInfo) { NameToMember[field.Name] = new AccessorMember(type, field.Name, field.FieldType, true, true, false, scope, AccessorMemberType.Field, field); } } if (types.HasFlagFast(AccessorMemberTypes.Properties)) { var properties = type.GetProperties(flags).OrderBy(p => p.Name); PropertyInfo = PropertyInfo == null ? properties.ToArray() : PropertyInfo.Concat(properties).Distinct().ToArray(); foreach (var property in PropertyInfo) { NameToMember[property.Name] = new AccessorMember(type, property.Name, property.PropertyType, CanAccessorRead(property, scope), CanAccessorWrite(property, scope), false, scope, AccessorMemberType.Property, property); } } if (types.HasFlagFast(AccessorMemberTypes.Methods)) { var methods = type.GetMethods(flags).OrderBy(m => m.Name); MethodInfo = MethodInfo == null?methods.ToArray() : MethodInfo.Concat(methods).Distinct().ToArray(); foreach (var method in MethodInfo) { NameToMember[method.Name] = new AccessorMember(type, method.Name, method.ReturnType, false, false, true, scope, AccessorMemberType.Method, method); } } }
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); } }
private static AccessorMembers CreateReadAccessorMembers(Type type, AccessorMemberTypes types = AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope scope = AccessorMemberScope.All) { return(AccessorMembers.Create(type, types, scope)); }
public AccessorMembersKey(Type type, AccessorMemberTypes types, AccessorMemberScope scope) { Type = type; Scope = scope; Types = types; }
private static AccessorMembers CreateWriteAccessorMembers(Type type, AccessorMemberTypes types, AccessorMemberScope scope) { return(AccessorMembers.Create(type, types, scope)); }
public static ITypeReadAccessor Create(Type type, AccessorMemberTypes types = AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope scope = AccessorMemberScope.All) { return(Create(type, null, types, scope, out _)); }
public static ITypeReadAccessor Create(Type type, AccessorMemberTypes types, AccessorMemberScope scope, out AccessorMembers members) { return(Create(type, null, types, scope, out members)); }
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)); }
public static bool HasFlagFast(this AccessorMemberTypes value, AccessorMemberTypes flag) { return((value & flag) != 0); }
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)); }
public static ITypeWriteAccessor Create(Type type, AccessorMemberTypes types, AccessorMemberScope scope, out AccessorMembers members) { return(CreateImpl(type, types, scope, out members)); }
public static ITypeWriteAccessor Create(Type type, AccessorMemberTypes types = AccessorMemberTypes.Fields | AccessorMemberTypes.Properties, AccessorMemberScope scope = AccessorMemberScope.All) { return(CreateImpl(type, types, scope, out _)); }
public static string CreateNameForWriteAccessor(this Type type, AccessorMemberTypes types, AccessorMemberScope scope) { return($"Write_{type.Name}_{types}_{scope}_{TypeHash(type, nameof(CreateNameForWriteAccessor))}"); }
private static AccessorMembersKey KeyForType(Type type, AccessorMemberTypes types, AccessorMemberScope scope) { var key = new AccessorMembersKey(type, types, scope); return(key); }