PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/source/library/Interlace/Utilities/ProxyGeneratorCore.cs

https://bitbucket.org/VahidN/interlace
C# | 315 lines | 217 code | 56 blank | 42 comment | 25 complexity | 63e3ff6be5da33d889f019c632ff3944 MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using System.Reflection.Emit;
  31. using System.Text;
  32. using System.Threading;
  33. #endregion
  34. namespace Interlace.Utilities
  35. {
  36. public class ProxyGeneratorCore
  37. {
  38. static readonly Dictionary<Type, OpCode> _loadFromPointerIntrinsicOpcodes;
  39. static readonly Dictionary<Type, OpCode> _storeToPointerIntrinsicOpcodes;
  40. AssemblyBuilder _assemblyBuilder;
  41. ModuleBuilder _module;
  42. static ProxyGeneratorCore()
  43. {
  44. _loadFromPointerIntrinsicOpcodes = new Dictionary<Type, OpCode>();
  45. _loadFromPointerIntrinsicOpcodes[typeof(char)] = OpCodes.Ldind_U2;
  46. _loadFromPointerIntrinsicOpcodes[typeof(bool)] = OpCodes.Ldind_I1;
  47. _loadFromPointerIntrinsicOpcodes[typeof(short)] = OpCodes.Ldind_I2;
  48. _loadFromPointerIntrinsicOpcodes[typeof(int)] = OpCodes.Ldind_I4;
  49. _loadFromPointerIntrinsicOpcodes[typeof(long)] = OpCodes.Ldind_I8;
  50. _loadFromPointerIntrinsicOpcodes[typeof(ushort)] = OpCodes.Ldind_U2;
  51. _loadFromPointerIntrinsicOpcodes[typeof(uint)] = OpCodes.Ldind_U4;
  52. _loadFromPointerIntrinsicOpcodes[typeof(ulong)] = OpCodes.Ldind_I8;
  53. _loadFromPointerIntrinsicOpcodes[typeof(float)] = OpCodes.Ldind_R4;
  54. _loadFromPointerIntrinsicOpcodes[typeof(double)] = OpCodes.Ldind_R8;
  55. _storeToPointerIntrinsicOpcodes = new Dictionary<Type, OpCode>();
  56. _storeToPointerIntrinsicOpcodes[typeof(char)] = OpCodes.Stind_I2;
  57. _storeToPointerIntrinsicOpcodes[typeof(bool)] = OpCodes.Stind_I1;
  58. _storeToPointerIntrinsicOpcodes[typeof(short)] = OpCodes.Stind_I2;
  59. _storeToPointerIntrinsicOpcodes[typeof(int)] = OpCodes.Stind_I4;
  60. _storeToPointerIntrinsicOpcodes[typeof(long)] = OpCodes.Stind_I8;
  61. _storeToPointerIntrinsicOpcodes[typeof(ushort)] = OpCodes.Stind_I2;
  62. _storeToPointerIntrinsicOpcodes[typeof(uint)] = OpCodes.Stind_I4;
  63. _storeToPointerIntrinsicOpcodes[typeof(ulong)] = OpCodes.Stind_I8;
  64. _storeToPointerIntrinsicOpcodes[typeof(float)] = OpCodes.Stind_R4;
  65. _storeToPointerIntrinsicOpcodes[typeof(double)] = OpCodes.Stind_R8;
  66. }
  67. public ProxyGeneratorCore()
  68. {
  69. AssemblyName assemblyName = new AssemblyName();
  70. assemblyName.Name = "ProxiesAssembly";
  71. _assemblyBuilder =
  72. Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
  73. _module = _assemblyBuilder.DefineDynamicModule("ProxiesModule.dll", true);
  74. }
  75. public ProxyFactory<TInterface> GenerateProxy<TInterface>() where TInterface : class
  76. {
  77. return GenerateProxy(typeof(TInterface)) as ProxyFactory<TInterface>;
  78. }
  79. public ProxyFactoryBase GenerateProxy(Type interfaceType)
  80. {
  81. if (interfaceType == null) throw new ArgumentNullException("interfaceType");
  82. if (!interfaceType.IsInterface) throw new ArgumentException("An interface type is required to generate a proxy.");
  83. if (interfaceType.ContainsGenericParameters) throw new ArgumentException("An interface with generic parameters is not supported.");
  84. TypeBuilder typeBuilder = _module.DefineType(string.Format("ProxyFor{0}", interfaceType.Name),
  85. TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed);
  86. typeBuilder.AddInterfaceImplementation(interfaceType);
  87. FieldBuilder handlerField = typeBuilder.DefineField("_handler",
  88. typeof(IProxyHandler), FieldAttributes.Private);
  89. FieldBuilder methodListField = typeBuilder.DefineField("_methodList",
  90. typeof(MethodInfo[]), FieldAttributes.Private);
  91. MethodInfo[] methodList = interfaceType.GetMethods();
  92. BuildProxyConstructor(typeBuilder, methodListField, handlerField);
  93. for (int i = 0; i < methodList.Length; i++)
  94. {
  95. BuildProxyMethod(typeBuilder, methodList[i], i, handlerField, methodListField);
  96. }
  97. Type proxy = typeBuilder.CreateType();
  98. Type proxyFactory = typeof(ProxyFactory<>).MakeGenericType(interfaceType);
  99. ConstructorInfo proxyFactoryConstructor = proxyFactory.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null,
  100. new Type[] { typeof(Type), typeof(MethodInfo[]) }, null);
  101. return proxyFactoryConstructor.Invoke(new object[] { proxy, methodList }) as ProxyFactoryBase;
  102. }
  103. static void BuildProxyConstructor(TypeBuilder typeBuilder, FieldBuilder methodListField,
  104. FieldBuilder handlerField)
  105. {
  106. ConstructorBuilder builder = typeBuilder.DefineConstructor(MethodAttributes.Public,
  107. CallingConventions.Standard, new Type[] { typeof(MethodInfo[]), typeof(IProxyHandler) });
  108. ILGenerator generator = builder.GetILGenerator();
  109. // Store the first argument into this._methodList:
  110. generator.Emit(OpCodes.Ldarg_0);
  111. generator.Emit(OpCodes.Ldarg_1);
  112. generator.Emit(OpCodes.Stfld, methodListField);
  113. // Store the second argument into this._handler:
  114. generator.Emit(OpCodes.Ldarg_0);
  115. generator.Emit(OpCodes.Ldarg_2);
  116. generator.Emit(OpCodes.Stfld, handlerField);
  117. // Return:
  118. generator.Emit(OpCodes.Ret);
  119. }
  120. static void BuildProxyMethod(TypeBuilder typeBuilder, MethodInfo method, int methodIndex,
  121. FieldBuilder handlerField, FieldBuilder methodListField)
  122. {
  123. // Get a parameter list and build a parameter type list:
  124. ParameterInfo[] parameters = method.GetParameters();
  125. Type[] parameterTypes = new Type[parameters.Length];
  126. for (int i = 0; i < parameters.Length; i++)
  127. {
  128. parameterTypes[i] = parameters[i].ParameterType;
  129. }
  130. // Check the parameter types:
  131. foreach (ParameterInfo parameter in parameters)
  132. {
  133. if (parameter.ParameterType.IsByRef &&
  134. (parameter.Attributes & ParameterAttributes.Out) != ParameterAttributes.Out)
  135. {
  136. throw new NotImplementedException("Proxies can not be created for interfaces " +
  137. "containing methods with \"ref\" parameters.");
  138. }
  139. }
  140. // Methods with more than 255 arguments, apart from being insane, are
  141. // not supported; because they are insane:
  142. if (parameters.Length > 255)
  143. {
  144. throw new InvalidOperationException(
  145. "Interface methods with more than 255 parameters are not supported.");
  146. }
  147. // Create the method:
  148. MethodBuilder methodBuilder = typeBuilder.DefineMethod(method.Name,
  149. MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.Standard,
  150. method.ReturnType, parameterTypes);
  151. ILGenerator generator = methodBuilder.GetILGenerator();
  152. LocalBuilder argumentsLocal = generator.DeclareLocal(typeof(object[]));
  153. MethodInfo handlerInvokeMethod = typeof(IProxyHandler).GetMethod(
  154. "Invoke", new Type[] { typeof(object), typeof(MethodInfo), typeof(object[]) });
  155. // Create the arguments array:
  156. generator.Emit(OpCodes.Ldc_I4, (int)parameters.Length);
  157. generator.Emit(OpCodes.Newarr, typeof(object));
  158. generator.Emit(OpCodes.Stloc, argumentsLocal);
  159. // Set each element of the array:
  160. for (int i = 0; i < parameters.Length; i++)
  161. {
  162. Type parameterType = parameterTypes[i];
  163. if (!parameterType.IsByRef)
  164. {
  165. generator.Emit(OpCodes.Ldloc, argumentsLocal);
  166. generator.Emit(OpCodes.Ldc_I4, (int)i);
  167. generator.Emit(OpCodes.Ldarg_S, (byte)(i + 1));
  168. if (parameterType.IsValueType)
  169. {
  170. generator.Emit(OpCodes.Box, parameterType);
  171. }
  172. generator.Emit(OpCodes.Stelem_Ref);
  173. }
  174. }
  175. // Push _handler, then "this", followed by the method information from the method list,
  176. // followed by arguments array; then call the handler:
  177. generator.Emit(OpCodes.Ldarg_0);
  178. generator.Emit(OpCodes.Ldfld, handlerField);
  179. generator.Emit(OpCodes.Ldarg_0);
  180. generator.Emit(OpCodes.Ldarg_0);
  181. generator.Emit(OpCodes.Ldfld, methodListField);
  182. generator.Emit(OpCodes.Ldc_I4, (int)methodIndex);
  183. generator.Emit(OpCodes.Ldelem_Ref);
  184. generator.Emit(OpCodes.Ldloc, argumentsLocal);
  185. generator.Emit(OpCodes.Callvirt, handlerInvokeMethod);
  186. // Return the data in the "out" arguments:
  187. for (int i = 0; i < parameters.Length; i++)
  188. {
  189. Type parameterType = parameterTypes[i];
  190. if (parameterType.IsByRef)
  191. {
  192. // Get the reference pointer:
  193. generator.Emit(OpCodes.Ldarg_S, (byte)(i + 1));
  194. // Get the boxed value from the arguments array:
  195. generator.Emit(OpCodes.Ldloc, argumentsLocal);
  196. generator.Emit(OpCodes.Ldc_I4, (int)i);
  197. generator.Emit(OpCodes.Ldelem_Ref);
  198. if (parameterType.GetElementType().IsValueType)
  199. {
  200. generator.Emit(OpCodes.Unbox_Any, parameterType.GetElementType());
  201. }
  202. // Store the value in the reference pointer:
  203. EmitStoreToReference(generator, parameterType.GetElementType());
  204. }
  205. }
  206. // Return the return value:
  207. if (method.ReturnType.Equals(typeof(void)))
  208. {
  209. generator.Emit(OpCodes.Pop);
  210. }
  211. else if (method.ReturnType.IsValueType)
  212. {
  213. generator.Emit(OpCodes.Unbox_Any, method.ReturnType);
  214. }
  215. generator.Emit(OpCodes.Ret);
  216. }
  217. static void EmitStoreToReference(ILGenerator generator, Type type)
  218. {
  219. if (!type.IsValueType)
  220. {
  221. generator.Emit(OpCodes.Stind_Ref);
  222. }
  223. else if (type.IsPrimitive)
  224. {
  225. generator.Emit(_storeToPointerIntrinsicOpcodes[type]);
  226. }
  227. else
  228. {
  229. if (type.IsEnum)
  230. {
  231. generator.Emit(OpCodes.Stind_I4);
  232. }
  233. else
  234. {
  235. generator.Emit(OpCodes.Stind_Ref);
  236. }
  237. }
  238. }
  239. static void EmitLoadFromPointer(ILGenerator generator, Type type)
  240. {
  241. if (!type.IsValueType) throw new InvalidOperationException();
  242. if (type.IsPrimitive)
  243. {
  244. generator.Emit(_loadFromPointerIntrinsicOpcodes[type]);
  245. }
  246. else
  247. {
  248. if (type.IsEnum)
  249. {
  250. generator.Emit(OpCodes.Ldind_I4);
  251. }
  252. else
  253. {
  254. generator.Emit(OpCodes.Ldobj);
  255. }
  256. }
  257. }
  258. }
  259. }