forked from pythonnet/pythonnet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmethodbinder.cs
More file actions
executable file
·366 lines (291 loc) · 9.9 KB
/
methodbinder.cs
File metadata and controls
executable file
·366 lines (291 loc) · 9.9 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
// ==========================================================================
// 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.Collections;
using System.Reflection;
namespace Python.Runtime {
//========================================================================
// A MethodBinder encapsulates information about a (possibly overloaded)
// managed method, and is responsible for selecting the right method given
// a set of Python arguments. This is also used as a base class for the
// ConstructorBinder, a minor variation used to invoke constructors.
//========================================================================
internal class MethodBinder {
public ArrayList list;
public MethodBase[] methods;
public bool init = false;
internal MethodBinder () {
this.list = new ArrayList();
}
internal MethodBinder(MethodInfo mi) : base () {
this.list = new ArrayList();
this.list.Add(mi);
}
public int Count {
get { return this.list.Count; }
}
internal void AddMethod(MethodBase m) {
this.list.Add(m);
}
//====================================================================
// Return the array of MethodInfo for this method. The result array
// is arranged in order of precendence (done lazily to avoid doing it
// at all for methods that are never called).
//====================================================================
internal MethodBase[] GetMethods() {
if (!init) {
// I'm sure this could be made more efficient.
list.Sort(new MethodSorter());
methods = (MethodBase[])list.ToArray(typeof(MethodBase));
init = true;
}
return methods;
}
//====================================================================
// Precedence algorithm largely lifted from jython - the concerns are
// generally the same so we'll start w/this and tweak as necessary.
//====================================================================
internal static int GetPrecedence(MethodBase mi) {
ParameterInfo[] pi = mi.GetParameters();
int val = mi.IsStatic ? 3000 : 0;
int num = pi.Length;
for (int i = 0; i < num; i++) {
val += ArgPrecedence(pi[i].ParameterType);
}
return val;
}
//====================================================================
// Return a precedence value for a particular Type object.
//====================================================================
internal static int ArgPrecedence(Type t) {
Type objectType = typeof(Object);
if (t == objectType) return 3000;
TypeCode tc = Type.GetTypeCode(t);
if (tc == TypeCode.Object) return 1;
if (tc == TypeCode.UInt64) return 10;
if (tc == TypeCode.UInt32) return 11;
if (tc == TypeCode.UInt16) return 12;
if (tc == TypeCode.Int64) return 13;
if (tc == TypeCode.Int32) return 14;
if (tc == TypeCode.Int16) return 15;
if (tc == TypeCode.Char) return 16;
if (tc == TypeCode.SByte) return 17;
if (tc == TypeCode.Byte) return 18;
if (tc == TypeCode.Single) return 20;
if (tc == TypeCode.Double) return 21;
if (tc == TypeCode.String) return 30;
if (tc == TypeCode.Boolean) return 40;
if (t.IsArray) {
Type e = t.GetElementType();
if (e == objectType)
return 2500;
return 100 + ArgPrecedence(e);
}
return 2000;
}
internal static MethodInfo MatchByTypeSig(MethodInfo[] msig,
IntPtr psig) {
IntPtr args = psig;
bool free = false;
if (!Runtime.PyTuple_Check(psig)) {
args = Runtime.PyTuple_New(1);
Runtime.Incref(psig);
Runtime.PyTuple_SetItem(args, 0, psig);
free = true;
}
int plen = Runtime.PyTuple_Size(args);
MethodInfo match = null;
// XXX: what about out args, etc.?
for (int i = 0; i < msig.Length; i++) {
ParameterInfo[] pi = msig[i].GetParameters();
if (pi.Length != plen) {
continue;
}
bool matched = true;
for (int n = 0; n < pi.Length; n++) {
IntPtr p = Runtime.PyTuple_GetItem(args, n);
if (p == IntPtr.Zero) {
Exceptions.Clear();
break;
}
ClassBase c = ManagedType.GetManagedObject(p) as ClassBase;
Type t = (c != null) ? c.type :
Converter.GetTypeByAlias(p);
if (t == null) {
break;
}
if (t != pi[n].ParameterType) {
matched = false;
break;
}
}
if (matched) {
match = msig[i];
break;
}
}
if (free) {
Runtime.Decref(args);
}
return match;
}
//====================================================================
// Bind the given Python instance and arguments to a particular method
// overload and return a structure that contains the converted Python
// instance, converted arguments and the correct method to call.
//====================================================================
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw) {
return this.Bind(inst, args, kw, null);
}
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw,
MethodBase info) {
// loop to find match, return invoker w/ or /wo error
MethodBase[] _methods = null;
int nargs = Runtime.PyTuple_Size(args);
object arg;
if (info != null) {
_methods = (MethodBase[])Array.CreateInstance(
typeof(MethodBase), 1
);
_methods.SetValue(info, 0);
}
else {
_methods = GetMethods();
}
for (int i = 0; i < _methods.Length; i++) {
MethodBase mi = _methods[i];
ParameterInfo[] pi = mi.GetParameters();
int count = pi.Length;
int outs = 0;
if ( nargs == count ) {
Object[] margs = new Object[count];
for (int n = 0; n < count; n++) {
IntPtr op = Runtime.PyTuple_GetItem(args, n);
Type type = pi[n].ParameterType;
if (pi[n].IsOut || type.IsByRef) {
outs++;
}
if (!Converter.ToManaged(op, type, out arg, false)) {
Exceptions.Clear();
margs = null;
break;
}
margs[n] = arg;
}
if (margs == null) {
continue;
}
Object target = null;
if ((!mi.IsStatic) && (inst != IntPtr.Zero)) {
CLRObject co = (CLRObject)ManagedType.GetManagedObject(
inst
);
target = co.inst;
}
return new Binding(mi, target, margs, outs);
}
}
return null;
}
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) {
return this.Invoke(inst, args, kw, null);
}
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw,
MethodBase info) {
Binding binding = this.Bind(inst, args, kw, info);
Object result;
if (binding == null) {
Exceptions.SetError(Exceptions.TypeError,
"no method matches given arguments"
);
return IntPtr.Zero;
}
IntPtr ts = PythonEngine.BeginAllowThreads();
try {
result = binding.info.Invoke(binding.inst,
BindingFlags.Default,
null,
binding.args,
null);
}
catch (Exception e) {
if (e.InnerException != null) {
e = e.InnerException;
}
PythonEngine.EndAllowThreads(ts);
Exceptions.SetError(e);
return IntPtr.Zero;
}
PythonEngine.EndAllowThreads(ts);
// If there are out parameters, we return a tuple containing
// the result followed by the out parameters. If there is only
// one out parameter and the return type of the method is void,
// we return the out parameter as the result to Python (for
// code compatibility with ironpython).
MethodInfo mi = (MethodInfo)binding.info;
if ((binding.outs == 1) && (mi.ReturnType == typeof(void))) {
}
if (binding.outs > 0) {
ParameterInfo[] pi = mi.GetParameters();
int c = pi.Length;
int n = 0;
IntPtr t = Runtime.PyTuple_New(binding.outs + 1);
IntPtr v = Converter.ToPython(result, mi.ReturnType);
Runtime.PyTuple_SetItem(t, n, v);
n++;
for (int i=0; i < c; i++) {
Type pt = pi[i].ParameterType;
if (pi[i].IsOut || pt.IsByRef) {
v = Converter.ToPython(binding.args[i], pt);
Runtime.PyTuple_SetItem(t, n, v);
n++;
}
}
if ((binding.outs == 1) && (mi.ReturnType == typeof(void))) {
v = Runtime.PyTuple_GetItem(t, 1);
Runtime.Incref(v);
Runtime.Decref(t);
return v;
}
return t;
}
return Converter.ToPython(result, mi.ReturnType);
}
}
//========================================================================
// Utility class to sort method info by parameter type precedence.
//========================================================================
internal class MethodSorter : IComparer {
int IComparer.Compare(Object m1, Object m2) {
int p1 = MethodBinder.GetPrecedence((MethodBase)m1);
int p2 = MethodBinder.GetPrecedence((MethodBase)m2);
if (p1 < p2) return -1;
if (p1 > p2) return 1;
return 0;
}
}
//========================================================================
// A Binding is a utility instance that bundles together a MethodInfo
// representing a method to call, a (possibly null) target instance for
// the call, and the arguments for the call (all as managed values).
//========================================================================
internal class Binding {
public MethodBase info;
public Object[] args;
public Object inst;
public int outs;
internal Binding(MethodBase info, Object inst, Object[] args,
int outs) {
this.info = info;
this.inst = inst;
this.args = args;
this.outs = outs;
}
}
}