[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/pythonindy/pythonnet/master/pythonnet/src/runtime/methodbinder.cs [Back]  [Original]

// ==========================================================================
// 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;
	}

    }

}

Web Proxy Viewer  |  New URL  |  Original Page