forked from pythonnet/pythonnet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnativecall.cs
More file actions
166 lines (122 loc) · 5.84 KB
/
nativecall.cs
File metadata and controls
166 lines (122 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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);
}
}