mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
try to use python.net as the bridge.
This commit is contained in:
465
Pythonnet.Runtime/methodbinder.cs
Normal file
465
Pythonnet.Runtime/methodbinder.cs
Normal file
@@ -0,0 +1,465 @@
|
||||
// ==========================================================================
|
||||
// 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;
|
||||
public bool allow_threads = true;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// Given a sequence of MethodInfo and a sequence of types, return the
|
||||
// MethodInfo that matches the signature represented by those types.
|
||||
//====================================================================
|
||||
|
||||
internal static MethodInfo MatchSignature(MethodInfo[] mi, Type[] tp) {
|
||||
int count = tp.Length;
|
||||
for (int i = 0; i < mi.Length; i++) {
|
||||
ParameterInfo[] pi = mi[i].GetParameters();
|
||||
if (pi.Length != count) {
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < pi.Length; n++) {
|
||||
if (tp[n]!= pi[n].ParameterType) {
|
||||
break;
|
||||
}
|
||||
if (n == (pi.Length - 1)) {
|
||||
return mi[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// Given a sequence of MethodInfo and a sequence of type parameters,
|
||||
// return the MethodInfo that represents the matching closed generic.
|
||||
//====================================================================
|
||||
|
||||
internal static MethodInfo MatchParameters(MethodInfo[] mi,Type[] tp) {
|
||||
int count = tp.Length;
|
||||
for (int i = 0; i < mi.Length; i++) {
|
||||
if (!mi[i].IsGenericMethodDefinition) {
|
||||
continue;
|
||||
}
|
||||
Type[] args = mi[i].GetGenericArguments();
|
||||
if (args.Length != count) {
|
||||
continue;
|
||||
}
|
||||
return mi[i].MakeGenericMethod(tp);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//====================================================================
|
||||
// Given a sequence of MethodInfo and two sequences of type parameters,
|
||||
// return the MethodInfo that matches the signature and the closed generic.
|
||||
//====================================================================
|
||||
|
||||
internal static MethodInfo MatchSignatureAndParameters(MethodInfo[] mi, Type[] genericTp, Type[] sigTp)
|
||||
{
|
||||
int genericCount = genericTp.Length;
|
||||
int signatureCount = sigTp.Length;
|
||||
for (int i = 0; i < mi.Length; i++)
|
||||
{
|
||||
if (!mi[i].IsGenericMethodDefinition)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Type[] genericArgs = mi[i].GetGenericArguments();
|
||||
if (genericArgs.Length != genericCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ParameterInfo[] pi = mi[i].GetParameters();
|
||||
if (pi.Length != signatureCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < pi.Length; n++)
|
||||
{
|
||||
if (sigTp[n] != pi[n].ParameterType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (n == (pi.Length - 1))
|
||||
{
|
||||
MethodInfo match = mi[i];
|
||||
if (match.IsGenericMethodDefinition)
|
||||
{
|
||||
Type[] typeArgs = match.GetGenericArguments();
|
||||
return match.MakeGenericMethod(genericTp);
|
||||
}
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//====================================================================
|
||||
// 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;
|
||||
|
||||
val += (mi.IsGenericMethod ? 1 : 0);
|
||||
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;
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// 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, null);
|
||||
}
|
||||
|
||||
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw,
|
||||
MethodBase info) {
|
||||
return this.Bind(inst, args, kw, info, null);
|
||||
}
|
||||
|
||||
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw,
|
||||
MethodBase info, MethodInfo[] methodinfo) {
|
||||
// loop to find match, return invoker w/ or /wo error
|
||||
MethodBase[] _methods = null;
|
||||
int pynargs = Runtime.PyTuple_Size(args);
|
||||
object arg;
|
||||
bool isGeneric = false;
|
||||
|
||||
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];
|
||||
if (mi.IsGenericMethod) { isGeneric = true; }
|
||||
ParameterInfo[] pi = mi.GetParameters();
|
||||
int clrnargs = pi.Length;
|
||||
bool match = false;
|
||||
int arrayStart = -1;
|
||||
int outs = 0;
|
||||
|
||||
if (pynargs == clrnargs) {
|
||||
match = true;
|
||||
} else if ((pynargs > clrnargs) && (clrnargs > 0) &&
|
||||
(pi[clrnargs-1].ParameterType.IsArray)) {
|
||||
// The last argument of the mananged functions seems to
|
||||
// accept multiple arguments as a array. Hopefully it's a
|
||||
// spam(params object[] egg) style method
|
||||
match = true;
|
||||
arrayStart = clrnargs - 1;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
Object[] margs = new Object[clrnargs];
|
||||
|
||||
for (int n = 0; n < clrnargs; n++) {
|
||||
IntPtr op;
|
||||
if (arrayStart == n) {
|
||||
// map remaining Python arguments to a tuple since
|
||||
// the managed function accepts it - hopefully :]
|
||||
op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs);
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
if (arrayStart == n) {
|
||||
// GetSlice() creates a new reference but GetItem()
|
||||
// returns only a borrow reference.
|
||||
Runtime.Decref(op);
|
||||
}
|
||||
margs[n] = arg;
|
||||
}
|
||||
|
||||
if (margs == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object target = null;
|
||||
if ((!mi.IsStatic) && (inst != IntPtr.Zero)) {
|
||||
//CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst);
|
||||
// InvalidCastException: Unable to cast object of type
|
||||
// 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject'
|
||||
CLRObject co = ManagedType.GetManagedObject(inst) as CLRObject;
|
||||
|
||||
// Sanity check: this ensures a graceful exit if someone does
|
||||
// something intentionally wrong like call a non-static method
|
||||
// on the class rather than on an instance of the class.
|
||||
// XXX maybe better to do this before all the other rigmarole.
|
||||
if (co == null) {
|
||||
return null;
|
||||
}
|
||||
target = co.inst;
|
||||
}
|
||||
|
||||
return new Binding(mi, target, margs, outs);
|
||||
}
|
||||
}
|
||||
// We weren't able to find a matching method but at least one
|
||||
// is a generic method and info is null. That happens when a generic
|
||||
// method was not called using the [] syntax. Let's introspect the
|
||||
// type of the arguments and use it to construct the correct method.
|
||||
if (isGeneric && (info == null) && (methodinfo != null))
|
||||
{
|
||||
Type[] types = Runtime.PythonArgsToTypeArray(args, true);
|
||||
MethodInfo mi = MethodBinder.MatchParameters(methodinfo, types);
|
||||
return Bind(inst, args, kw, mi, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) {
|
||||
return this.Invoke(inst, args, kw, null, null);
|
||||
|
||||
}
|
||||
|
||||
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw,
|
||||
MethodBase info) {
|
||||
return this.Invoke(inst, args, kw, info, null);
|
||||
}
|
||||
|
||||
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw,
|
||||
MethodBase info, MethodInfo[] methodinfo) {
|
||||
Binding binding = this.Bind(inst, args, kw, info, methodinfo);
|
||||
Object result;
|
||||
IntPtr ts = IntPtr.Zero;
|
||||
|
||||
if (binding == null) {
|
||||
Exceptions.SetError(Exceptions.TypeError,
|
||||
"No method matches given arguments"
|
||||
);
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (allow_threads) {
|
||||
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;
|
||||
}
|
||||
if (allow_threads) {
|
||||
PythonEngine.EndAllowThreads(ts);
|
||||
}
|
||||
Exceptions.SetError(e);
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (allow_threads) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user