using System;
using System.Reflection;
using System.Text;
namespace Python.Runtime
{
///
/// A ConstructorBinder encapsulates information about one or more managed
/// constructors, and is responsible for selecting the right constructor
/// given a set of Python arguments. This is slightly different than the
/// standard MethodBinder because of a difference in invoking constructors
/// using reflection (which is seems to be a CLR bug).
///
[Serializable]
internal class ConstructorBinder : MethodBinder
{
private Type _containingType;
internal ConstructorBinder(Type containingType)
{
_containingType = containingType;
}
///
/// Constructors get invoked when an instance of a wrapped managed
/// class or a subclass of a managed class is created. This differs
/// from the MethodBinder implementation in that we return the raw
/// result of the constructor rather than wrapping it as a Python
/// object - the reason is that only the caller knows the correct
/// Python type to use when wrapping the result (may be a subclass).
///
internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw)
{
return InvokeRaw(inst, args, kw, null);
}
///
/// Allows ctor selection to be limited to a single attempt at a
/// match by providing the MethodBase to use instead of searching
/// the entire MethodBinder.list (generic ArrayList)
///
/// (possibly null) instance
/// PyObject* to the arg tuple
/// PyObject* to the keyword args dict
/// The sole ContructorInfo to use or null
/// The result of the constructor call with converted params
///
/// 2010-07-24 BC: I added the info parameter to the call to Bind()
/// Binding binding = this.Bind(inst, args, kw, info);
/// to take advantage of Bind()'s ability to use a single MethodBase (CI or MI).
///
internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
{
object result;
if (_containingType.IsValueType && !_containingType.IsPrimitive &&
!_containingType.IsEnum && _containingType != typeof(decimal) &&
Runtime.PyTuple_Size(args) == 0)
{
// If you are trying to construct an instance of a struct by
// calling its default constructor, that ConstructorInfo
// instance will not appear in reflection and the object must
// instead be constructed via a call to
// Activator.CreateInstance().
try
{
result = Activator.CreateInstance(_containingType);
}
catch (Exception e)
{
if (e.InnerException != null)
{
e = e.InnerException;
}
Exceptions.SetError(e);
return null;
}
return result;
}
Binding binding = Bind(inst, args, kw, info);
if (binding == null)
{
// It is possible for __new__ to be invoked on construction
// of a Python subclass of a managed class, so args may
// reflect more args than are required to instantiate the
// class. So if we cant find a ctor that matches, we'll see
// if there is a default constructor and, if so, assume that
// any extra args are intended for the subclass' __init__.
IntPtr eargs = Runtime.PyTuple_New(0);
binding = Bind(inst, eargs, IntPtr.Zero);
Runtime.XDecref(eargs);
if (binding == null)
{
var errorMessage = new StringBuilder("No constructor matches given arguments");
if (info != null && info.IsConstructor && info.DeclaringType != null)
{
errorMessage.Append(" for ").Append(info.DeclaringType.Name);
}
errorMessage.Append(": ");
AppendArgumentTypes(to: errorMessage, args);
Exceptions.SetError(Exceptions.TypeError, errorMessage.ToString());
return null;
}
}
// Fire the selected ctor and catch errors...
var ci = (ConstructorInfo)binding.info;
// Object construction is presumed to be non-blocking and fast
// enough that we shouldn't really need to release the GIL.
try
{
result = ci.Invoke(binding.args);
}
catch (Exception e)
{
if (e.InnerException != null)
{
e = e.InnerException;
}
Exceptions.SetError(e);
return null;
}
return result;
}
}
}