[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/patstew/pythonnet/python3/src/runtime/nativecall.cs [Back]  [Original]

// ==========================================================================
// This software is subject to the provisions of the Zope Public License,
// Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
// FOR A PARTICULAR PURPOSE.
// ==========================================================================

using System;
using System.Threading;
using System.Runtime.InteropServices;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;

namespace Python.Runtime {

    /// 
    /// Provides support for calling native code indirectly through 
    /// function pointers. Most of the important parts of the Python
    /// C API can just be wrapped with p/invoke, but there are some
    /// situations (specifically, calling functions through Python
    /// type structures) where we need to call functions indirectly.
    ///
    /// This class uses Reflection.Emit to generate IJW thunks that
    /// support indirect calls to native code using various common
    /// call signatures. This is mainly a workaround for the fact 
    /// that you can't spell an indirect call in C# (but can in IL).
    ///
    /// Another approach that would work is for this to be turned 
    /// into a separate utility program that could be run during the
    /// build process to generate the thunks as a separate assembly 
    /// that could then be referenced by the main Python runtime.
    /// 

    internal class NativeCall {

        static AssemblyBuilder aBuilder;
        static ModuleBuilder mBuilder;

        public static INativeCall Impl;

        static NativeCall() {

            // The static constructor is responsible for generating the
            // assembly and the methods that implement the IJW thunks.
            //
            // To do this, we actually use reflection on the INativeCall
            // interface (defined below) and generate the required thunk 
            // code based on the method signatures.

            AssemblyName aname = new AssemblyName();
            aname.Name = "e__NativeCall_Assembly";
            AssemblyBuilderAccess aa = AssemblyBuilderAccess.Run;

            aBuilder = Thread.GetDomain().DefineDynamicAssembly(aname, aa);
            mBuilder = aBuilder.DefineDynamicModule("e__NativeCall_Module");

            TypeAttributes ta = TypeAttributes.Public;
            TypeBuilder tBuilder = mBuilder.DefineType("e__NativeCall", ta);

            Type iType = typeof(INativeCall);
            tBuilder.AddInterfaceImplementation(iType);

            // Use reflection to loop over the INativeCall interface methods, 
            // calling GenerateThunk to create a managed thunk for each one.

            foreach (MethodInfo method in iType.GetMethods()) {
                GenerateThunk(tBuilder, method);
            }
            
            Type theType = tBuilder.CreateType();

            Impl = (INativeCall)Activator.CreateInstance(theType);

        }

        private static void GenerateThunk(TypeBuilder tb, MethodInfo method) {

            ParameterInfo[] pi = method.GetParameters();
            int count = pi.Length;
            int argc = count - 1;

            Type[] args = new Type[count];
            for (int i = 0; i < count; i++) {
                args[i] = pi[i].ParameterType;
            }

            MethodBuilder mb = tb.DefineMethod(
                                  method.Name,
                                  MethodAttributes.Public | 
                                  MethodAttributes.Virtual,
                                  method.ReturnType,
                                  args
                                  );

            // Build the method signature for the actual native function.
            // This is essentially the signature of the wrapper method
            // minus the first argument (the passed in function pointer).

            Type[] nargs = new Type[argc];
            for (int i = 1; i < count; i++) {
                nargs[(i - 1)] = args[i];
            }

            // IL generation: the (implicit) first argument of the method 
            // is the 'this' pointer and the second is the function pointer.
            // This code pushes the real args onto the stack, followed by
            // the function pointer, then the calli opcode to make the call.

            ILGenerator il = mb.GetILGenerator();

            for (int i = 0; i < argc; i++) {
                il.Emit(OpCodes.Ldarg_S, (i + 2));
            }

            il.Emit(OpCodes.Ldarg_1);

            il.EmitCalli(OpCodes.Calli, 
                         CallingConvention.Cdecl, 
                         method.ReturnType, 
                         nargs
                         );

            il.Emit(OpCodes.Ret);

            tb.DefineMethodOverride(mb, method);
            return;
        }


        public static void Void_Call_1(IntPtr fp, IntPtr a1) {
            Impl.Void_Call_1(fp, a1);
        }

        public static IntPtr Call_3(IntPtr fp, IntPtr a1, IntPtr a2, 
                                    IntPtr a3) {
            return Impl.Call_3(fp, a1, a2, a3);
        }

        public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, 
                                     IntPtr a3) {
            return Impl.Int_Call_3(fp, a1, a2, a3);
        }

    }


    /// 
    /// Defines native call signatures to be generated by NativeCall.
    /// 

    public interface INativeCall {

        void Void_Call_0(IntPtr funcPtr);

        void Void_Call_1(IntPtr funcPtr, IntPtr arg1);

        int Int_Call_3(IntPtr funcPtr, IntPtr t, IntPtr n, IntPtr v);

        IntPtr Call_3(IntPtr funcPtr, IntPtr a1, IntPtr a2, IntPtr a3);

    }

}

Web Proxy Viewer  |  New URL  |  Original Page