PageRenderTime 62ms CodeModel.GetById 44ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

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

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