mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-17 04:07:58 +01:00
try to use python.net as the bridge.
This commit is contained in:
166
Pythonnet.Runtime/nativecall.cs
Normal file
166
Pythonnet.Runtime/nativecall.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
// ==========================================================================
|
||||
// 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 {
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Defines native call signatures to be generated by NativeCall.
|
||||
/// </summary>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user