PageRenderTime 383ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/Serenity.Core/Reflection/TypeAccessor.cs

https://gitlab.com/pgksunilkumar/Serenity
C# | 309 lines | 241 code | 20 blank | 48 comment | 38 complexity | 0c98b68b65dd604869e8600aff0cc404 MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Reflection;
  4. using System.Reflection.Emit;
  5. using System.Threading;
  6. //#if !NO_DYNAMIC
  7. //using System.Dynamic;
  8. //#endif
  9. namespace Serenity.Reflection
  10. {
  11. /// <summary>
  12. /// Provides by-name member-access to objects of a given type
  13. /// </summary>
  14. public abstract class TypeAccessor
  15. {
  16. // hash-table has better read-without-locking semantics than dictionary
  17. private static readonly Hashtable typeLookyp = new Hashtable();
  18. /// <summary>
  19. /// Does this type support new instances via a parameterless constructor?
  20. /// </summary>
  21. public virtual bool CreateNewSupported { get { return false; } }
  22. /// <summary>
  23. /// Create a new instance of this type
  24. /// </summary>
  25. public virtual object CreateNew() { throw new NotSupportedException(); }
  26. /// <summary>
  27. /// Provides a type-specific accessor, allowing by-name access for all objects of that type
  28. /// </summary>
  29. /// <remarks>The accessor is cached internally; a pre-existing accessor may be returned</remarks>
  30. public static TypeAccessor Create(Type type)
  31. {
  32. if (type == null) throw new ArgumentNullException("type");
  33. TypeAccessor obj = (TypeAccessor)typeLookyp[type];
  34. if (obj != null) return obj;
  35. lock (typeLookyp)
  36. {
  37. // double-check
  38. obj = (TypeAccessor)typeLookyp[type];
  39. if (obj != null) return obj;
  40. obj = CreateNew(type);
  41. typeLookyp[type] = obj;
  42. return obj;
  43. }
  44. }
  45. //#if !NO_DYNAMIC
  46. // sealed class DynamicAccessor : TypeAccessor
  47. // {
  48. // public static readonly DynamicAccessor Singleton = new DynamicAccessor();
  49. // private DynamicAccessor() { }
  50. // public override object this[object target, string name]
  51. // {
  52. // get { return CallSiteCache.GetValue(name, target); }
  53. // set { CallSiteCache.SetValue(name, target, value); }
  54. // }
  55. // }
  56. //#endif
  57. private static AssemblyBuilder assembly;
  58. private static ModuleBuilder module;
  59. private static int counter;
  60. private static void WriteGetter(ILGenerator il, Type type, PropertyInfo[] props, FieldInfo[] fields, bool isStatic)
  61. {
  62. LocalBuilder loc = type.IsValueType ? il.DeclareLocal(type) : null;
  63. OpCode propName = isStatic ? OpCodes.Ldarg_1 : OpCodes.Ldarg_2, target = isStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1;
  64. foreach (PropertyInfo prop in props)
  65. {
  66. MethodInfo getter;
  67. if (prop.GetIndexParameters().Length != 0 || !prop.CanRead || (getter = prop.GetGetMethod(false)) == null) continue;
  68. Label next = il.DefineLabel();
  69. il.Emit(propName);
  70. il.Emit(OpCodes.Ldstr, prop.Name);
  71. il.EmitCall(OpCodes.Call, strinqEquals, null);
  72. il.Emit(OpCodes.Brfalse_S, next);
  73. // match:
  74. il.Emit(target);
  75. Cast(il, type, loc);
  76. il.EmitCall(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, getter, null);
  77. if (prop.PropertyType.IsValueType)
  78. {
  79. il.Emit(OpCodes.Box, prop.PropertyType);
  80. }
  81. il.Emit(OpCodes.Ret);
  82. // not match:
  83. il.MarkLabel(next);
  84. }
  85. foreach (FieldInfo field in fields)
  86. {
  87. Label next = il.DefineLabel();
  88. il.Emit(propName);
  89. il.Emit(OpCodes.Ldstr, field.Name);
  90. il.EmitCall(OpCodes.Call, strinqEquals, null);
  91. il.Emit(OpCodes.Brfalse_S, next);
  92. // match:
  93. il.Emit(target);
  94. Cast(il, type, loc);
  95. il.Emit(OpCodes.Ldfld, field);
  96. if (field.FieldType.IsValueType)
  97. {
  98. il.Emit(OpCodes.Box, field.FieldType);
  99. }
  100. il.Emit(OpCodes.Ret);
  101. // not match:
  102. il.MarkLabel(next);
  103. }
  104. il.Emit(OpCodes.Ldstr, "name");
  105. il.Emit(OpCodes.Newobj, typeof(ArgumentOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
  106. il.Emit(OpCodes.Throw);
  107. }
  108. private static void WriteSetter(ILGenerator il, Type type, PropertyInfo[] props, FieldInfo[] fields, bool isStatic)
  109. {
  110. if (type.IsValueType)
  111. {
  112. il.Emit(OpCodes.Ldstr, "Write is not supported for structs");
  113. il.Emit(OpCodes.Newobj, typeof(NotSupportedException).GetConstructor(new Type[] { typeof(string) }));
  114. il.Emit(OpCodes.Throw);
  115. }
  116. else
  117. {
  118. OpCode propName = isStatic ? OpCodes.Ldarg_1 : OpCodes.Ldarg_2,
  119. target = isStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1,
  120. value = isStatic ? OpCodes.Ldarg_2 : OpCodes.Ldarg_3;
  121. LocalBuilder loc = type.IsValueType ? il.DeclareLocal(type) : null;
  122. foreach (PropertyInfo prop in props)
  123. {
  124. MethodInfo setter;
  125. if (prop.GetIndexParameters().Length != 0 || !prop.CanWrite || (setter = prop.GetSetMethod(false)) == null) continue;
  126. Label next = il.DefineLabel();
  127. il.Emit(propName);
  128. il.Emit(OpCodes.Ldstr, prop.Name);
  129. il.EmitCall(OpCodes.Call, strinqEquals, null);
  130. il.Emit(OpCodes.Brfalse_S, next);
  131. // match:
  132. il.Emit(target);
  133. Cast(il, type, loc);
  134. il.Emit(value);
  135. Cast(il, prop.PropertyType, null);
  136. il.EmitCall(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, setter, null);
  137. il.Emit(OpCodes.Ret);
  138. // not match:
  139. il.MarkLabel(next);
  140. }
  141. foreach (FieldInfo field in fields)
  142. {
  143. Label next = il.DefineLabel();
  144. il.Emit(propName);
  145. il.Emit(OpCodes.Ldstr, field.Name);
  146. il.EmitCall(OpCodes.Call, strinqEquals, null);
  147. il.Emit(OpCodes.Brfalse_S, next);
  148. // match:
  149. il.Emit(target);
  150. Cast(il, type, loc);
  151. il.Emit(value);
  152. Cast(il, field.FieldType, null);
  153. il.Emit(OpCodes.Stfld, field);
  154. il.Emit(OpCodes.Ret);
  155. // not match:
  156. il.MarkLabel(next);
  157. }
  158. il.Emit(OpCodes.Ldstr, "name");
  159. il.Emit(OpCodes.Newobj, typeof(ArgumentOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
  160. il.Emit(OpCodes.Throw);
  161. }
  162. }
  163. private static readonly MethodInfo strinqEquals = typeof(string).GetMethod("op_Equality", new Type[] { typeof(string), typeof(string) });
  164. sealed class DelegateAccessor : TypeAccessor
  165. {
  166. private readonly Func<object, string, object> getter;
  167. private readonly Action<object, string, object> setter;
  168. private readonly Func<object> ctor;
  169. public DelegateAccessor(Func<object, string, object> getter, Action<object, string, object> setter, Func<object> ctor)
  170. {
  171. this.getter = getter;
  172. this.setter = setter;
  173. this.ctor = ctor;
  174. }
  175. public override bool CreateNewSupported { get { return ctor != null; } }
  176. public override object CreateNew()
  177. {
  178. return ctor != null ? ctor() : base.CreateNew();
  179. }
  180. public override object this[object target, string name]
  181. {
  182. get { return getter(target, name); }
  183. set { setter(target, name, value); }
  184. }
  185. }
  186. private static bool IsFullyPublic(Type type)
  187. {
  188. while (type.IsNestedPublic) type = type.DeclaringType;
  189. return type.IsPublic;
  190. }
  191. static TypeAccessor CreateNew(Type type)
  192. {
  193. //#if !NO_DYNAMIC
  194. // if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type))
  195. // {
  196. // return DynamicAccessor.Singleton;
  197. // }
  198. //#endif
  199. PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
  200. FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
  201. ConstructorInfo ctor = null;
  202. if (type.IsClass && !type.IsAbstract)
  203. {
  204. ctor = type.GetConstructor(Type.EmptyTypes);
  205. }
  206. ILGenerator il;
  207. if (!IsFullyPublic(type))
  208. {
  209. DynamicMethod dynGetter = new DynamicMethod(type.FullName + "_get", typeof(object), new Type[] { typeof(object), typeof(string) }, type, true),
  210. dynSetter = new DynamicMethod(type.FullName + "_set", null, new Type[] { typeof(object), typeof(string), typeof(object) }, type, true);
  211. WriteGetter(dynGetter.GetILGenerator(), type, props, fields, true);
  212. WriteSetter(dynSetter.GetILGenerator(), type, props, fields, true);
  213. DynamicMethod dynCtor = null;
  214. if (ctor != null)
  215. {
  216. dynCtor = new DynamicMethod(type.FullName + "_ctor", typeof(object), Type.EmptyTypes, type, true);
  217. il = dynCtor.GetILGenerator();
  218. il.Emit(OpCodes.Newobj, ctor);
  219. il.Emit(OpCodes.Ret);
  220. }
  221. return new DelegateAccessor(
  222. (Func<object, string, object>)dynGetter.CreateDelegate(typeof(Func<object, string, object>)),
  223. (Action<object, string, object>)dynSetter.CreateDelegate(typeof(Action<object, string, object>)),
  224. dynCtor == null ? null : (Func<object>)dynCtor.CreateDelegate(typeof(Func<object>)));
  225. }
  226. // note this region is synchronized; only one is being created at a time so we don't need to stress about the builders
  227. if (assembly == null)
  228. {
  229. AssemblyName name = new AssemblyName("FastMember_dynamic");
  230. assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
  231. module = assembly.DefineDynamicModule(name.Name);
  232. }
  233. TypeBuilder tb = module.DefineType("FastMember_dynamic." + type.Name + "_" + Interlocked.Increment(ref counter),
  234. (typeof(TypeAccessor).Attributes | TypeAttributes.Sealed) & ~TypeAttributes.Abstract, typeof(TypeAccessor));
  235. tb.DefineDefaultConstructor(MethodAttributes.Public);
  236. PropertyInfo indexer = typeof(TypeAccessor).GetProperty("Item");
  237. MethodInfo baseGetter = indexer.GetGetMethod(), baseSetter = indexer.GetSetMethod();
  238. MethodBuilder body = tb.DefineMethod(baseGetter.Name, baseGetter.Attributes & ~MethodAttributes.Abstract, typeof(object), new Type[] { typeof(object), typeof(string) });
  239. il = body.GetILGenerator();
  240. WriteGetter(il, type, props, fields, false);
  241. tb.DefineMethodOverride(body, baseGetter);
  242. body = tb.DefineMethod(baseSetter.Name, baseSetter.Attributes & ~MethodAttributes.Abstract, null, new Type[] { typeof(object), typeof(string), typeof(object) });
  243. il = body.GetILGenerator();
  244. WriteSetter(il, type, props, fields, false);
  245. tb.DefineMethodOverride(body, baseSetter);
  246. if (ctor != null)
  247. {
  248. MethodInfo baseMethod = typeof(TypeAccessor).GetProperty("CreateNewSupported").GetGetMethod();
  249. body = tb.DefineMethod(baseMethod.Name, baseMethod.Attributes, typeof(bool), Type.EmptyTypes);
  250. il = body.GetILGenerator();
  251. il.Emit(OpCodes.Ldc_I4_1);
  252. il.Emit(OpCodes.Ret);
  253. tb.DefineMethodOverride(body, baseMethod);
  254. baseMethod = typeof(TypeAccessor).GetMethod("CreateNew");
  255. body = tb.DefineMethod(baseMethod.Name, baseMethod.Attributes, typeof(object), Type.EmptyTypes);
  256. il = body.GetILGenerator();
  257. il.Emit(OpCodes.Newobj, ctor);
  258. il.Emit(OpCodes.Ret);
  259. tb.DefineMethodOverride(body, baseMethod);
  260. }
  261. return (TypeAccessor)Activator.CreateInstance(tb.CreateType());
  262. }
  263. private static void Cast(ILGenerator il, Type type, LocalBuilder addr)
  264. {
  265. if (type == typeof(object)) { }
  266. else if (type.IsValueType)
  267. {
  268. il.Emit(OpCodes.Unbox_Any, type);
  269. if (addr != null)
  270. {
  271. il.Emit(OpCodes.Stloc, addr);
  272. il.Emit(OpCodes.Ldloca_S, addr);
  273. }
  274. }
  275. else
  276. {
  277. il.Emit(OpCodes.Castclass, type);
  278. }
  279. }
  280. /// <summary>
  281. /// Get or set the value of a named member on the target instance
  282. /// </summary>
  283. public abstract object this[object target, string name]
  284. {
  285. get;
  286. set;
  287. }
  288. }
  289. }