using System;
using System.Collections;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
using System.Linq;
namespace Python.Runtime
{
using MaybeMethodBase = MaybeMethodBase;
///
/// 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.
///
[Serializable]
internal class MethodBinder
{
public List list;
[NonSerialized]
public MethodBase[] methods;
[NonSerialized]
public bool init = false;
public const bool DefaultAllowThreads = true;
public bool allow_threads = DefaultAllowThreads;
internal MethodBinder()
{
list = new List();
}
internal MethodBinder(MethodInfo mi)
{
list = new List { new MaybeMethodBase(mi) };
}
public int Count
{
get { return list.Count; }
}
internal void AddMethod(MethodBase m)
{
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)
{
if (tp == null)
{
return null;
}
int count = tp.Length;
foreach (MethodInfo t in mi)
{
ParameterInfo[] pi = t.GetParameters();
if (pi.Length != count)
{
continue;
}
for (var n = 0; n < pi.Length; n++)
{
if (tp[n] != pi[n].ParameterType)
{
break;
}
if (n == pi.Length - 1)
{
return t;
}
}
}
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)
{
if (tp == null)
{
return null;
}
int count = tp.Length;
foreach (MethodInfo t in mi)
{
if (!t.IsGenericMethodDefinition)
{
continue;
}
Type[] args = t.GetGenericArguments();
if (args.Length != count)
{
continue;
}
return t.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)
{
if (genericTp == null || sigTp == null)
{
return null;
}
int genericCount = genericTp.Length;
int signatureCount = sigTp.Length;
foreach (MethodInfo t in mi)
{
if (!t.IsGenericMethodDefinition)
{
continue;
}
Type[] genericArgs = t.GetGenericArguments();
if (genericArgs.Length != genericCount)
{
continue;
}
ParameterInfo[] pi = t.GetParameters();
if (pi.Length != signatureCount)
{
continue;
}
for (var n = 0; n < pi.Length; n++)
{
if (sigTp[n] != pi[n].ParameterType)
{
break;
}
if (n == pi.Length - 1)
{
MethodInfo match = t;
if (match.IsGenericMethodDefinition)
{
// FIXME: typeArgs not used
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 precedence (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 = (from method in list where method.Valid select method.Value).ToArray();
init = true;
}
return methods;
}
///
/// Precedence algorithm largely lifted from Jython - the concerns are
/// generally the same so we'll start with this and tweak as necessary.
///
///
/// Based from Jython `org.python.core.ReflectedArgs.precedence`
/// See: https://github.com/jythontools/jython/blob/master/src/org/python/core/ReflectedArgs.java#L192
///
internal static int GetPrecedence(MethodBase mi)
{
if (mi == null)
{
return int.MaxValue;
}
ParameterInfo[] pi = mi.GetParameters();
int val = mi.IsStatic ? 3000 : 0;
int num = pi.Length;
val += mi.IsGenericMethod ? 1 : 0;
for (var 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;
}
if (t.IsArray)
{
Type e = t.GetElementType();
if (e == objectType)
{
return 2500;
}
return 100 + ArgPrecedence(e);
}
TypeCode tc = Type.GetTypeCode(t);
// TODO: Clean up
switch (tc)
{
case TypeCode.Object:
return 1;
case TypeCode.UInt64:
return 10;
case TypeCode.UInt32:
return 11;
case TypeCode.UInt16:
return 12;
case TypeCode.Int64:
return 13;
case TypeCode.Int32:
return 14;
case TypeCode.Int16:
return 15;
case TypeCode.Char:
return 16;
case TypeCode.SByte:
return 17;
case TypeCode.Byte:
return 18;
case TypeCode.Single:
return 20;
case TypeCode.Double:
return 21;
case TypeCode.String:
return 30;
case TypeCode.Boolean:
return 40;
}
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 Bind(inst, args, kw, null, null);
}
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
{
return Bind(inst, args, kw, info, null);
}
private readonly struct MatchedMethod {
public MatchedMethod(int kwargsMatched, int defaultsNeeded, object[] margs, int outs, MethodBase mb)
{
KwargsMatched = kwargsMatched;
DefaultsNeeded = defaultsNeeded;
ManagedArgs = margs;
Outs = outs;
Method = mb;
}
public int KwargsMatched { get; }
public int DefaultsNeeded { get; }
public object[] ManagedArgs { get; }
public int Outs { get; }
public MethodBase Method { get; }
}
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;
var kwargDict = new Dictionary();
if (kw != IntPtr.Zero)
{
var pynkwargs = (int)Runtime.PyDict_Size(kw);
IntPtr keylist = Runtime.PyDict_Keys(kw);
IntPtr valueList = Runtime.PyDict_Values(kw);
for (int i = 0; i < pynkwargs; ++i)
{
var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(new BorrowedReference(keylist), i));
kwargDict[keyStr] = Runtime.PyList_GetItem(new BorrowedReference(valueList), i).DangerousGetAddress();
}
Runtime.XDecref(keylist);
Runtime.XDecref(valueList);
}
var pynargs = (int)Runtime.PyTuple_Size(args);
var isGeneric = false;
if (info != null)
{
_methods = new MethodBase[1];
_methods.SetValue(info, 0);
}
else
{
_methods = GetMethods();
}
var argMatchedMethods = new List(_methods.Length);
// TODO: Clean up
foreach (MethodBase mi in _methods)
{
if (mi.IsGenericMethod)
{
isGeneric = true;
}
ParameterInfo[] pi = mi.GetParameters();
ArrayList defaultArgList;
bool paramsArray;
int kwargsMatched;
int defaultsNeeded;
bool isOperator = OperatorMethod.IsOperatorMethod(mi);
// Binary operator methods will have 2 CLR args but only one Python arg
// (unary operators will have 1 less each), since Python operator methods are bound.
isOperator = isOperator && pynargs == pi.Length - 1;
bool isReverse = isOperator && OperatorMethod.IsReverse((MethodInfo)mi); // Only cast if isOperator.
if (isReverse && OperatorMethod.IsComparisonOp((MethodInfo)mi))
continue; // Comparison operators in Python have no reverse mode.
if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded) && !isOperator)
{
continue;
}
// Preprocessing pi to remove either the first or second argument.
if (isOperator && !isReverse) {
// The first Python arg is the right operand, while the bound instance is the left.
// We need to skip the first (left operand) CLR argument.
pi = pi.Skip(1).ToArray();
}
else if (isOperator && isReverse) {
// The first Python arg is the left operand.
// We need to take the first CLR argument.
pi = pi.Take(1).ToArray();
}
var outs = 0;
var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList,
needsResolution: _methods.Length > 1, // If there's more than one possible match.
outs: out outs);
if (margs == null)
{
continue;
}
if (isOperator)
{
if (inst != IntPtr.Zero)
{
if (ManagedType.GetManagedObject(inst) is CLRObject co)
{
bool isUnary = pynargs == 0;
// Postprocessing to extend margs.
var margsTemp = isUnary ? new object[1] : new object[2];
// If reverse, the bound instance is the right operand.
int boundOperandIndex = isReverse ? 1 : 0;
// If reverse, the passed instance is the left operand.
int passedOperandIndex = isReverse ? 0 : 1;
margsTemp[boundOperandIndex] = co.inst;
if (!isUnary)
{
margsTemp[passedOperandIndex] = margs[0];
}
margs = margsTemp;
}
else { break; }
}
}
var matchedMethod = new MatchedMethod(kwargsMatched, defaultsNeeded, margs, outs, mi);
argMatchedMethods.Add(matchedMethod);
}
if (argMatchedMethods.Count > 0)
{
var bestKwargMatchCount = argMatchedMethods.Max(x => x.KwargsMatched);
var fewestDefaultsRequired = argMatchedMethods.Where(x => x.KwargsMatched == bestKwargMatchCount).Min(x => x.DefaultsNeeded);
int bestCount = 0;
int bestMatchIndex = -1;
for (int index = 0; index < argMatchedMethods.Count; index++)
{
var testMatch = argMatchedMethods[index];
if (testMatch.DefaultsNeeded == fewestDefaultsRequired && testMatch.KwargsMatched == bestKwargMatchCount)
{
bestCount++;
if (bestMatchIndex == -1)
bestMatchIndex = index;
}
}
if (bestCount > 1 && fewestDefaultsRequired > 0)
{
// Best effort for determining method to match on gives multiple possible
// matches and we need at least one default argument - bail from this point
return null;
}
// If we're here either:
// (a) There is only one best match
// (b) There are multiple best matches but none of them require
// default arguments
// in the case of (a) we're done by default. For (b) regardless of which
// method we choose, all arguments are specified _and_ can be converted
// from python to C# so picking any will suffice
MatchedMethod bestMatch = argMatchedMethods[bestMatchIndex];
var margs = bestMatch.ManagedArgs;
var outs = bestMatch.Outs;
var mi = bestMatch.Method;
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'
var 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 = MatchParameters(methodinfo, types);
return Bind(inst, args, kw, mi, null);
}
return null;
}
static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out bool isNewReference)
{
isNewReference = false;
IntPtr op;
// for a params method, we may have a sequence or single/multiple items
// here we look to see if the item at the paramIndex is there or not
// and then if it is a sequence itself.
if ((pyArgCount - arrayStart) == 1)
{
// we only have one argument left, so we need to check it
// to see if it is a sequence or a single item
IntPtr item = Runtime.PyTuple_GetItem(args, arrayStart);
if (!Runtime.PyString_Check(item) && Runtime.PySequence_Check(item))
{
// it's a sequence (and not a string), so we use it as the op
op = item;
}
else
{
isNewReference = true;
op = Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount);
}
}
else
{
isNewReference = true;
op = Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount);
}
return op;
}
///
/// Attempts to convert Python positional argument tuple and keyword argument table
/// into an array of managed objects, that can be passed to a method.
///
/// Information about expected parameters
/// true, if the last parameter is a params array.
/// A pointer to the Python argument tuple
/// Number of arguments, passed by Python
/// Dictionary of keyword argument name to python object pointer
/// A list of default values for omitted parameters
/// true, if overloading resolution is required
/// Returns number of output parameters
/// An array of .NET arguments, that can be passed to a method.
static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
IntPtr args, int pyArgCount,
Dictionary kwargDict,
ArrayList defaultArgList,
bool needsResolution,
out int outs)
{
outs = 0;
var margs = new object[pi.Length];
int arrayStart = paramsArray ? pi.Length - 1 : -1;
for (int paramIndex = 0; paramIndex < pi.Length; paramIndex++)
{
var parameter = pi[paramIndex];
bool hasNamedParam = kwargDict.ContainsKey(parameter.Name);
bool isNewReference = false;
if (paramIndex >= pyArgCount && !(hasNamedParam || (paramsArray && paramIndex == arrayStart)))
{
if (defaultArgList != null)
{
margs[paramIndex] = defaultArgList[paramIndex - pyArgCount];
}
continue;
}
IntPtr op;
if (hasNamedParam)
{
op = kwargDict[parameter.Name];
}
else
{
if(arrayStart == paramIndex)
{
op = HandleParamsArray(args, arrayStart, pyArgCount, out isNewReference);
}
else
{
op = Runtime.PyTuple_GetItem(args, paramIndex);
}
}
bool isOut;
if (!TryConvertArgument(op, parameter.ParameterType, needsResolution, out margs[paramIndex], out isOut))
{
return null;
}
if (isNewReference)
{
// TODO: is this a bug? Should this happen even if the conversion fails?
// GetSlice() creates a new reference but GetItem()
// returns only a borrow reference.
Runtime.XDecref(op);
}
if (isOut)
{
outs++;
}
}
return margs;
}
///
/// Try to convert a Python argument object to a managed CLR type.
///
/// Pointer to the object at a particular parameter.
/// That parameter's managed type.
/// There are multiple overloading methods that need resolution.
/// Converted argument.
/// Whether the CLR type is passed by reference.
///
static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution,
out object arg, out bool isOut)
{
arg = null;
isOut = false;
var clrtype = TryComputeClrArgumentType(parameterType, op, needsResolution: needsResolution);
if (clrtype == null)
{
return false;
}
if (!Converter.ToManaged(op, clrtype, out arg, false))
{
Exceptions.Clear();
return false;
}
isOut = clrtype.IsByRef;
return true;
}
static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool needsResolution)
{
// this logic below handles cases when multiple overloading methods
// are ambiguous, hence comparison between Python and CLR types
// is necessary
Type clrtype = null;
IntPtr pyoptype;
if (needsResolution)
{
// HACK: each overload should be weighted in some way instead
pyoptype = Runtime.PyObject_Type(argument);
Exceptions.Clear();
if (pyoptype != IntPtr.Zero)
{
clrtype = Converter.GetTypeByAlias(pyoptype);
}
Runtime.XDecref(pyoptype);
}
if (clrtype != null)
{
var typematch = false;
if ((parameterType != typeof(object)) && (parameterType != clrtype))
{
IntPtr pytype = Converter.GetPythonTypeByAlias(parameterType);
pyoptype = Runtime.PyObject_Type(argument);
Exceptions.Clear();
if (pyoptype != IntPtr.Zero)
{
if (pytype != pyoptype)
{
typematch = false;
}
else
{
typematch = true;
clrtype = parameterType;
}
}
if (!typematch)
{
// this takes care of enum values
TypeCode argtypecode = Type.GetTypeCode(parameterType);
TypeCode paramtypecode = Type.GetTypeCode(clrtype);
if (argtypecode == paramtypecode)
{
typematch = true;
clrtype = parameterType;
}
}
Runtime.XDecref(pyoptype);
if (!typematch)
{
return null;
}
}
else
{
typematch = true;
clrtype = parameterType;
}
}
else
{
clrtype = parameterType;
}
return clrtype;
}
///
/// Check whether the number of Python and .NET arguments match, and compute additional arg information.
///
/// Number of positional args passed from Python.
/// Parameters of the specified .NET method.
/// Keyword args passed from Python.
/// True if the final param of the .NET method is an array (`params` keyword).
/// List of default values for arguments.
/// Number of kwargs from Python that are also present in the .NET method.
/// Number of non-null defaultsArgs.
///
static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters,
Dictionary kwargDict,
out bool paramsArray,
out ArrayList defaultArgList,
out int kwargsMatched,
out int defaultsNeeded)
{
defaultArgList = null;
var match = false;
paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false;
kwargsMatched = 0;
defaultsNeeded = 0;
if (positionalArgumentCount == parameters.Length && kwargDict.Count == 0)
{
match = true;
}
else if (positionalArgumentCount < parameters.Length && (!paramsArray || positionalArgumentCount == parameters.Length - 1))
{
match = true;
// every parameter past 'positionalArgumentCount' must have either
// a corresponding keyword arg or a default param, unless the method
// method accepts a params array (which cannot have a default value)
defaultArgList = new ArrayList();
for (var v = positionalArgumentCount; v < parameters.Length; v++)
{
if (kwargDict.ContainsKey(parameters[v].Name))
{
// we have a keyword argument for this parameter,
// no need to check for a default parameter, but put a null
// placeholder in defaultArgList
defaultArgList.Add(null);
kwargsMatched++;
}
else if (parameters[v].IsOptional)
{
// IsOptional will be true if the parameter has a default value,
// or if the parameter has the [Optional] attribute specified.
// The GetDefaultValue() extension method will return the value
// to be passed in as the parameter value
defaultArgList.Add(parameters[v].GetDefaultValue());
defaultsNeeded++;
}
else if (!paramsArray)
{
match = false;
}
}
}
else if (positionalArgumentCount > parameters.Length && parameters.Length > 0 &&
Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)))
{
// This is a `foo(params object[] bar)` style method
match = true;
paramsArray = true;
}
return match;
}
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw)
{
return Invoke(inst, args, kw, null, null);
}
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
{
return Invoke(inst, args, kw, info, null);
}
protected static void AppendArgumentTypes(StringBuilder to, IntPtr args)
{
long argCount = Runtime.PyTuple_Size(args);
to.Append("(");
for (long argIndex = 0; argIndex < argCount; argIndex++)
{
var arg = Runtime.PyTuple_GetItem(args, argIndex);
if (arg != IntPtr.Zero)
{
var type = Runtime.PyObject_Type(arg);
if (type != IntPtr.Zero)
{
try
{
var description = Runtime.PyObject_Unicode(type);
if (description != IntPtr.Zero)
{
to.Append(Runtime.GetManagedString(description));
Runtime.XDecref(description);
}
}
finally
{
Runtime.XDecref(type);
}
}
}
if (argIndex + 1 < argCount)
to.Append(", ");
}
to.Append(')');
}
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo)
{
// No valid methods, nothing to bind.
if (GetMethods().Length == 0)
{
var msg = new StringBuilder("The underlying C# method(s) have been deleted");
if (list.Count > 0 && list[0].Name != null)
{
msg.Append($": {list[0].ToString()}");
}
return Exceptions.RaiseTypeError(msg.ToString());;
}
Binding binding = Bind(inst, args, kw, info, methodinfo);
object result;
IntPtr ts = IntPtr.Zero;
if (binding == null)
{
var value = new StringBuilder("No method matches given arguments");
if (methodinfo != null && methodinfo.Length > 0)
{
value.Append($" for {methodinfo[0].Name}");
}
value.Append(": ");
AppendArgumentTypes(to: value, args);
Exceptions.SetError(Exceptions.TypeError, value.ToString());
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).
var mi = (MethodInfo)binding.info;
if (binding.outs == 1 && mi.ReturnType == typeof(void))
{
}
if (binding.outs > 0)
{
ParameterInfo[] pi = mi.GetParameters();
int c = pi.Length;
var 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 (var i = 0; i < c; i++)
{
Type pt = pi[i].ParameterType;
if (pi[i].IsOut || pt.IsByRef)
{
v = Converter.ToPython(binding.args[i], pt.GetElementType());
Runtime.PyTuple_SetItem(t, n, v);
n++;
}
}
if (binding.outs == 1 && mi.ReturnType == typeof(void))
{
v = Runtime.PyTuple_GetItem(t, 1);
Runtime.XIncref(v);
Runtime.XDecref(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(MaybeMethodBase m1, MaybeMethodBase m2)
{
MethodBase me1 = m1.UnsafeValue;
MethodBase me2 = m2.UnsafeValue;
if (me1 == null && me2 == null)
{
return 0;
}
else if (me1 == null)
{
return -1;
}
else if (me2 == null)
{
return 1;
}
if (me1.DeclaringType != me2.DeclaringType)
{
// m2's type derives from m1's type, favor m2
if (me1.DeclaringType.IsAssignableFrom(me2.DeclaringType))
return 1;
// m1's type derives from m2's type, favor m1
if (me2.DeclaringType.IsAssignableFrom(me1.DeclaringType))
return -1;
}
int p1 = MethodBinder.GetPrecedence(me1);
int p2 = MethodBinder.GetPrecedence(me2);
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;
}
}
static internal class ParameterInfoExtensions
{
public static object GetDefaultValue(this ParameterInfo parameterInfo)
{
// parameterInfo.HasDefaultValue is preferable but doesn't exist in .NET 4.0
bool hasDefaultValue = (parameterInfo.Attributes & ParameterAttributes.HasDefault) ==
ParameterAttributes.HasDefault;
if (hasDefaultValue)
{
return parameterInfo.DefaultValue;
}
else
{
// [OptionalAttribute] was specified for the parameter.
// See https://stackoverflow.com/questions/3416216/optionalattribute-parameters-default-value
// for rules on determining the value to pass to the parameter
var type = parameterInfo.ParameterType;
if (type == typeof(object))
return Type.Missing;
else if (type.IsValueType)
return Activator.CreateInstance(type);
else
return null;
}
}
}
}