mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-14 11:39:16 +08:00
167 lines
5.8 KiB
C#
167 lines
5.8 KiB
C#
|
// ==========================================================================
|
||
|
// 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);
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|