PageRenderTime 40ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/IronPython/IronPython.Modules/_ctypes/CFuncPtrType.cs

http://github.com/IronLanguages/main
C# | 381 lines | 283 code | 65 blank | 33 comment | 41 complexity | eb7ae17bb20702c2dc2628854d293890 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if FEATURE_NATIVE
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Reflection.Emit;
  20. using System.Runtime.CompilerServices;
  21. using System.Runtime.InteropServices;
  22. using Microsoft.Scripting;
  23. using Microsoft.Scripting.Actions;
  24. using Microsoft.Scripting.Generation;
  25. using Microsoft.Scripting.Runtime;
  26. using Microsoft.Scripting.Utils;
  27. using IronPython.Runtime;
  28. using IronPython.Runtime.Operations;
  29. using IronPython.Runtime.Types;
  30. #if CLR2
  31. using Microsoft.Scripting.Math;
  32. #else
  33. using System.Numerics;
  34. #endif
  35. namespace IronPython.Modules {
  36. /// <summary>
  37. /// Provides support for interop with native code from Python code.
  38. /// </summary>
  39. public static partial class CTypes {
  40. /// <summary>
  41. /// The meta class for ctypes function pointer instances.
  42. /// </summary>
  43. [PythonType, PythonHidden]
  44. public class CFuncPtrType : PythonType, INativeType {
  45. internal readonly int _flags;
  46. internal readonly PythonType _restype;
  47. internal readonly INativeType[] _argtypes;
  48. private DynamicMethod _reverseDelegate; // reverse delegates are lazily computed the 1st time a callable is turned into a func ptr
  49. private List<object> _reverseDelegateConstants;
  50. private Type _reverseDelegateType;
  51. private static Dictionary<DelegateCacheKey, Type> _reverseDelegates = new Dictionary<DelegateCacheKey, Type>();
  52. //from_buffer_copy, from_param, from_address, from_buffer, __doc__ __mul__ __rmul__ in_dll __new__
  53. public CFuncPtrType(CodeContext/*!*/ context, string name, PythonTuple bases, PythonDictionary members)
  54. : base(context, name, bases, members) {
  55. object flags;
  56. if (!members.TryGetValue("_flags_", out flags) || !(flags is int)) {
  57. throw PythonOps.TypeError("class must define _flags_ which must be an integer");
  58. }
  59. _flags = (int)flags;
  60. object restype;
  61. if (members.TryGetValue("_restype_", out restype) && (restype is PythonType)) {
  62. _restype = (PythonType)restype;
  63. }
  64. object argtypes;
  65. if (members.TryGetValue("_argtypes_", out argtypes) && (argtypes is PythonTuple)) {
  66. PythonTuple pt = argtypes as PythonTuple;
  67. _argtypes = new INativeType[pt.Count];
  68. for (int i = 0; i < pt.Count; i++) {
  69. _argtypes[i] = (INativeType)pt[i];
  70. }
  71. }
  72. }
  73. private CFuncPtrType(Type underlyingSystemType)
  74. : base(underlyingSystemType) {
  75. }
  76. internal static PythonType MakeSystemType(Type underlyingSystemType) {
  77. return PythonType.SetPythonType(underlyingSystemType, new CFuncPtrType(underlyingSystemType));
  78. }
  79. /// <summary>
  80. /// Converts an object into a function call parameter.
  81. /// </summary>
  82. public object from_param(object obj) {
  83. return null;
  84. }
  85. // TODO: Move to Ops class
  86. public object internal_restype {
  87. get {
  88. return _restype;
  89. }
  90. }
  91. #region INativeType Members
  92. int INativeType.Size {
  93. get {
  94. return IntPtr.Size;
  95. }
  96. }
  97. int INativeType.Alignment {
  98. get {
  99. return IntPtr.Size;
  100. }
  101. }
  102. object INativeType.GetValue(MemoryHolder owner, object readingFrom, int offset, bool raw) {
  103. IntPtr funcAddr = owner.ReadIntPtr(offset);
  104. if (raw) {
  105. return funcAddr.ToPython();
  106. }
  107. return CreateInstance(Context.SharedContext, funcAddr);
  108. }
  109. object INativeType.SetValue(MemoryHolder address, int offset, object value) {
  110. if (value is int) {
  111. address.WriteIntPtr(offset, new IntPtr((int)value));
  112. } else if (value is BigInteger) {
  113. address.WriteIntPtr(offset, new IntPtr((long)(BigInteger)value));
  114. } else if (value is _CFuncPtr) {
  115. address.WriteIntPtr(offset, ((_CFuncPtr)value).addr);
  116. return value;
  117. } else {
  118. throw PythonOps.TypeErrorForTypeMismatch("func pointer", value);
  119. }
  120. return null;
  121. }
  122. Type INativeType.GetNativeType() {
  123. return typeof(IntPtr);
  124. }
  125. MarshalCleanup INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg argIndex, List<object>/*!*/ constantPool, int constantPoolArgument) {
  126. Type argumentType = argIndex.Type;
  127. argIndex.Emit(method);
  128. if (argumentType.IsValueType) {
  129. method.Emit(OpCodes.Box, argumentType);
  130. }
  131. constantPool.Add(this);
  132. method.Emit(OpCodes.Ldarg, constantPoolArgument);
  133. method.Emit(OpCodes.Ldc_I4, constantPool.Count - 1);
  134. method.Emit(OpCodes.Ldelem_Ref);
  135. method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod("GetFunctionPointerValue"));
  136. return null;
  137. }
  138. Type/*!*/ INativeType.GetPythonType() {
  139. return typeof(_CFuncPtr);
  140. }
  141. void INativeType.EmitReverseMarshalling(ILGenerator method, LocalOrArg value, List<object> constantPool, int constantPoolArgument) {
  142. value.Emit(method);
  143. constantPool.Add(this);
  144. method.Emit(OpCodes.Ldarg, constantPoolArgument);
  145. method.Emit(OpCodes.Ldc_I4, constantPool.Count - 1);
  146. method.Emit(OpCodes.Ldelem_Ref);
  147. method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod("CreateCFunction"));
  148. }
  149. string INativeType.TypeFormat {
  150. get {
  151. return "X{}";
  152. }
  153. }
  154. #endregion
  155. internal CallingConvention CallingConvention {
  156. get {
  157. switch (_flags & 0x07) {
  158. case FUNCFLAG_STDCALL: return CallingConvention.StdCall;
  159. case FUNCFLAG_CDECL: return CallingConvention.Cdecl;
  160. case FUNCFLAG_HRESULT:
  161. case FUNCFLAG_PYTHONAPI:
  162. break;
  163. }
  164. return CallingConvention.Cdecl;
  165. }
  166. }
  167. internal Delegate MakeReverseDelegate(CodeContext/*!*/ context, object target) {
  168. if (_reverseDelegate == null) {
  169. lock (this) {
  170. if (_reverseDelegate == null) {
  171. MakeReverseDelegateWorker(context);
  172. }
  173. }
  174. }
  175. object[] constantPool = _reverseDelegateConstants.ToArray();
  176. constantPool[0] = target;
  177. return _reverseDelegate.CreateDelegate(_reverseDelegateType, constantPool);
  178. }
  179. private void MakeReverseDelegateWorker(CodeContext context) {
  180. Type[] sigTypes;
  181. Type[] callSiteType;
  182. Type retType;
  183. GetSignatureInfo(out sigTypes, out callSiteType, out retType);
  184. DynamicMethod dm = new DynamicMethod("ReverseInteropInvoker", retType, ArrayUtils.RemoveLast(sigTypes), DynamicModule);
  185. ILGenerator ilGen = dm.GetILGenerator();
  186. PythonContext pc = PythonContext.GetContext(context);
  187. Type callDelegateSiteType = CompilerHelpers.MakeCallSiteDelegateType(callSiteType);
  188. CallSite site = CallSite.Create(callDelegateSiteType, pc.Invoke(new CallSignature(_argtypes.Length)));
  189. List<object> constantPool = new List<object>();
  190. constantPool.Add(null); // 1st item is the target object, will be put in later.
  191. constantPool.Add(site);
  192. ilGen.BeginExceptionBlock();
  193. //CallSite<Func<CallSite, object, object>> mySite;
  194. //mySite.Target(mySite, target, ...);
  195. LocalBuilder siteLocal = ilGen.DeclareLocal(site.GetType());
  196. ilGen.Emit(OpCodes.Ldarg_0);
  197. ilGen.Emit(OpCodes.Ldc_I4, constantPool.Count - 1);
  198. ilGen.Emit(OpCodes.Ldelem_Ref);
  199. ilGen.Emit(OpCodes.Castclass, site.GetType());
  200. ilGen.Emit(OpCodes.Stloc, siteLocal);
  201. ilGen.Emit(OpCodes.Ldloc, siteLocal);
  202. ilGen.Emit(OpCodes.Ldfld, site.GetType().GetField("Target"));
  203. ilGen.Emit(OpCodes.Ldloc, siteLocal);
  204. // load code context
  205. int contextIndex = constantPool.Count;
  206. Debug.Assert(pc.SharedContext != null);
  207. constantPool.Add(pc.SharedContext);
  208. ilGen.Emit(OpCodes.Ldarg_0);
  209. ilGen.Emit(OpCodes.Ldc_I4, contextIndex);
  210. ilGen.Emit(OpCodes.Ldelem_Ref);
  211. // load function target, in constant pool slot 0
  212. ilGen.Emit(OpCodes.Ldarg_0);
  213. ilGen.Emit(OpCodes.Ldc_I4_0);
  214. ilGen.Emit(OpCodes.Ldelem_Ref);
  215. // load arguments
  216. for (int i = 0; i < _argtypes.Length; i++) {
  217. INativeType nativeType = _argtypes[i];
  218. nativeType.EmitReverseMarshalling(ilGen, new Arg(i + 1, sigTypes[i + 1]), constantPool, 0);
  219. }
  220. ilGen.Emit(OpCodes.Call, callDelegateSiteType.GetMethod("Invoke"));
  221. LocalBuilder finalRes = null;
  222. // emit forward marshaling for return value
  223. if (_restype != null) {
  224. LocalBuilder tmpRes = ilGen.DeclareLocal(typeof(object));
  225. ilGen.Emit(OpCodes.Stloc, tmpRes);
  226. finalRes = ilGen.DeclareLocal(retType);
  227. ((INativeType)_restype).EmitMarshalling(ilGen, new Local(tmpRes), constantPool, 0);
  228. ilGen.Emit(OpCodes.Stloc, finalRes);
  229. } else {
  230. ilGen.Emit(OpCodes.Pop);
  231. }
  232. // } catch(Exception e) {
  233. // emit the cleanup code
  234. ilGen.BeginCatchBlock(typeof(Exception));
  235. ilGen.Emit(OpCodes.Ldarg_0);
  236. ilGen.Emit(OpCodes.Ldc_I4, contextIndex);
  237. ilGen.Emit(OpCodes.Ldelem_Ref);
  238. ilGen.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod("CallbackException"));
  239. ilGen.EndExceptionBlock();
  240. if (_restype != null) {
  241. ilGen.Emit(OpCodes.Ldloc, finalRes);
  242. }
  243. ilGen.Emit(OpCodes.Ret);
  244. _reverseDelegateConstants = constantPool;
  245. _reverseDelegateType = GetReverseDelegateType(ArrayUtils.RemoveFirst(sigTypes), CallingConvention);
  246. _reverseDelegate = dm;
  247. }
  248. private void GetSignatureInfo(out Type[] sigTypes, out Type[] callSiteType, out Type retType) {
  249. sigTypes = new Type[_argtypes.Length + 2]; // constant pool, args ..., ret type
  250. callSiteType = new Type[_argtypes.Length + 4]; // CallSite, context, target, args ..., ret type
  251. sigTypes[0] = typeof(object[]);
  252. callSiteType[0] = typeof(CallSite);
  253. callSiteType[1] = typeof(CodeContext);
  254. callSiteType[2] = typeof(object);
  255. callSiteType[callSiteType.Length - 1] = typeof(object);
  256. for (int i = 0; i < _argtypes.Length; i++) {
  257. sigTypes[i + 1] = _argtypes[i].GetNativeType();
  258. Debug.Assert(sigTypes[i + 1] != typeof(object));
  259. callSiteType[i + 3] = _argtypes[i].GetPythonType();
  260. }
  261. if (_restype != null) {
  262. sigTypes[sigTypes.Length - 1] = retType = ((INativeType)_restype).GetNativeType();
  263. } else {
  264. sigTypes[sigTypes.Length - 1] = retType = typeof(void);
  265. }
  266. }
  267. private static Type GetReverseDelegateType(Type[] nativeSig, CallingConvention callingConvention) {
  268. Type res;
  269. lock (_reverseDelegates) {
  270. DelegateCacheKey key = new DelegateCacheKey(nativeSig, callingConvention);
  271. if (!_reverseDelegates.TryGetValue(key, out res)) {
  272. res = _reverseDelegates[key] = PythonOps.MakeNewCustomDelegate(nativeSig, callingConvention);
  273. }
  274. }
  275. return res;
  276. }
  277. struct DelegateCacheKey : IEquatable<DelegateCacheKey> {
  278. private readonly Type[] _types;
  279. private readonly CallingConvention _callConv;
  280. public DelegateCacheKey(Type[] sig, CallingConvention callingConvention) {
  281. Assert.NotNullItems(sig);
  282. _types = sig;
  283. _callConv = callingConvention;
  284. }
  285. public override int GetHashCode() {
  286. int res = _callConv.GetHashCode();
  287. for (int i = 0; i < _types.Length; i++) {
  288. res ^= _types[i].GetHashCode();
  289. }
  290. return res;
  291. }
  292. public override bool Equals(object obj) {
  293. if (obj is DelegateCacheKey) {
  294. return Equals((DelegateCacheKey)obj);
  295. }
  296. return false;
  297. }
  298. #region IEquatable<DelegateCacheKey> Members
  299. public bool Equals(DelegateCacheKey other) {
  300. if (other._types.Length != _types.Length ||
  301. other._callConv != _callConv) {
  302. return false;
  303. }
  304. for (int i = 0; i < _types.Length; i++) {
  305. if (_types[i] != other._types[i]) {
  306. return false;
  307. }
  308. }
  309. return true;
  310. }
  311. #endregion
  312. }
  313. }
  314. }
  315. }
  316. #endif