diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 4c9de1461..ff814b023 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -94,7 +94,7 @@ import clr
from Python.EmbeddingTest.Domain import MyClass
obj = MyClass()
obj.Method()
-obj.StaticMethod()
+MyClass.StaticMethod()
obj.Property = 1
obj.Field = 10
", Assembly.GetExecutingAssembly().FullName);
@@ -137,16 +137,18 @@ public override ValueType Execute(ValueType arg)
Assert.That(tp_clear, Is.Not.Null);
using (PyObject obj = new PyObject(handle))
+ using (PyObject myClass = new PyObject(new BorrowedReference(tp)))
{
obj.InvokeMethod("Method");
- obj.InvokeMethod("StaticMethod");
+ myClass.InvokeMethod("StaticMethod");
using (var scope = Py.CreateScope())
{
scope.Set("obj", obj);
+ scope.Set("myClass", myClass);
scope.Exec(@"
obj.Method()
-obj.StaticMethod()
+myClass.StaticMethod()
obj.Property += 1
obj.Field += 10
");
@@ -191,7 +193,7 @@ from Python.EmbeddingTest.Domain import MyClass
def test_obj_call():
obj = MyClass()
obj.Method()
- obj.StaticMethod()
+ MyClass.StaticMethod()
obj.Property = 1
obj.Field = 10
diff --git a/src/runtime/BorrowedReference.cs b/src/runtime/BorrowedReference.cs
index 8ae382e77..479942b06 100644
--- a/src/runtime/BorrowedReference.cs
+++ b/src/runtime/BorrowedReference.cs
@@ -11,9 +11,11 @@ readonly ref struct BorrowedReference
public bool IsNull => this.pointer == IntPtr.Zero;
- /// Gets a raw pointer to the Python object
+ /// Gets a raw pointer to the Python object. Performs null check
public IntPtr DangerousGetAddress()
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
+ /// Gets a raw pointer to the Python object
+ public IntPtr DangerousGetAddressOrNull() => this.pointer;
///
/// Creates new instance of from raw pointer. Unsafe.
@@ -22,5 +24,7 @@ public BorrowedReference(IntPtr pointer)
{
this.pointer = pointer;
}
+
+ public static BorrowedReference Null => default;
}
}
diff --git a/src/runtime/Native/PyFrameObjectReference.cs b/src/runtime/Native/PyFrameObjectReference.cs
new file mode 100644
index 000000000..eae6a943f
--- /dev/null
+++ b/src/runtime/Native/PyFrameObjectReference.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Python.Runtime.Native
+{
+ readonly ref struct PyFrameObjectReference
+ {
+ readonly IntPtr pointer;
+
+ public bool IsNull => this.pointer == IntPtr.Zero;
+ }
+}
diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj
index 4ca8140e9..1d15741cb 100644
--- a/src/runtime/Python.Runtime.15.csproj
+++ b/src/runtime/Python.Runtime.15.csproj
@@ -122,6 +122,9 @@
+
+ 4.7.0
+
@@ -143,20 +146,20 @@
-
-
- TextTemplatingFileGenerator
- intern_.cs
-
+
+
+ TextTemplatingFileGenerator
+ intern_.cs
+
-
-
- True
- True
- intern_.tt
-
+
+
+ True
+ True
+ intern_.tt
+
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index a11a4b852..29e97abae 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -92,6 +92,7 @@
+
diff --git a/src/runtime/Util.cs b/src/runtime/Util.cs
index eb21cddbb..353758dae 100644
--- a/src/runtime/Util.cs
+++ b/src/runtime/Util.cs
@@ -7,6 +7,8 @@ internal static class Util
{
internal const string UnstableApiMessage =
"This API is unstable, and might be changed or removed in the next minor release";
+ internal const string UseReferenceOverloadMessage =
+ "This is a legacy overload. Please use an overload, that works with *Reference types";
internal static Int64 ReadCLong(IntPtr tp, int offset)
{
diff --git a/src/runtime/callsitebinder.cs b/src/runtime/callsitebinder.cs
new file mode 100644
index 000000000..3ebfee4cd
--- /dev/null
+++ b/src/runtime/callsitebinder.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+
+using Microsoft.CSharp.RuntimeBinder;
+
+using CSharpBinder = Microsoft.CSharp.RuntimeBinder.Binder;
+
+namespace Python.Runtime
+{
+ public class PythonNetCallSiteBinder : CallSiteBinder
+ {
+ readonly CallSiteBinder voidBinder;
+ readonly CallSiteBinder resultBinder;
+
+ PythonNetCallSiteBinder(string methodName,
+ IEnumerable typeArguments,
+ Type context,
+ IEnumerable argumentInfo)
+ {
+ this.voidBinder = CSharpBinder.InvokeMember(CSharpBinderFlags.ResultDiscarded,
+ methodName,
+ typeArguments: typeArguments,
+ context: context,
+ argumentInfo
+ );
+ this.resultBinder = CSharpBinder.InvokeMember(CSharpBinderFlags.None,
+ methodName,
+ typeArguments: typeArguments,
+ context: context,
+ argumentInfo
+ );
+ }
+
+ public override Expression Bind(object[] args,
+ ReadOnlyCollection parameters,
+ LabelTarget returnLabel)
+ {
+ var result = this.resultBinder.Bind(args, parameters, returnLabel);
+ if (result.Type == typeof(void))
+ {
+ var voidExpr = this.voidBinder.Bind(args, parameters, returnLabel);
+ return Expression.Block(
+ voidExpr,
+ Expression.Return(returnLabel, Expression.Constant(null))
+ );
+ }
+
+ return result;
+ }
+
+ public static PythonNetCallSiteBinder InvokeMember(string methodName,
+ IEnumerable typeArguments,
+ Type context,
+ IEnumerable argumentInfo)
+ => new PythonNetCallSiteBinder(methodName, typeArguments, context, argumentInfo);
+ }
+}
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index a62e76050..9c983af35 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -347,9 +347,10 @@ protected override void OnLoad(InterDomainContext context)
///
/// Implements __getitem__ for reflected classes and value types.
///
- public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
+ public static IntPtr mp_subscript(IntPtr obRaw, IntPtr idx)
{
- IntPtr tp = Runtime.PyObject_TYPE(ob);
+ var ob = new BorrowedReference(obRaw);
+ var tp = Runtime.PyObject_Type(ob);
var cls = (ClassBase)GetManagedObject(tp);
if (cls.indexer == null || !cls.indexer.CanGet)
@@ -361,40 +362,35 @@ public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
// Arg may be a tuple in the case of an indexer with multiple
// parameters. If so, use it directly, else make a new tuple
// with the index arg (method binders expect arg tuples).
- IntPtr args = idx;
- var free = false;
-
- if (!Runtime.PyTuple_Check(idx))
- {
- args = Runtime.PyTuple_New(1);
- Runtime.XIncref(idx);
- Runtime.PyTuple_SetItem(args, 0, idx);
- free = true;
- }
-
- IntPtr value;
- try
+ if (Runtime.PyTuple_Check(idx))
{
- value = cls.indexer.GetItem(ob, args);
+ return cls.indexer.GetItem(ob, new BorrowedReference(idx));
}
- finally
+ else
{
- if (free)
+ var args = NewReference.DangerousFromPointer(Runtime.PyTuple_New(1));
+ try
{
- Runtime.XDecref(args);
+ Runtime.XIncref(idx);
+ Runtime.PyTuple_SetItem(args, 0, idx);
+ return cls.indexer.GetItem(ob, args);
+ }
+ finally
+ {
+ args.Dispose();
}
}
- return value;
}
///
/// Implements __setitem__ for reflected classes and value types.
///
- public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
+ public static int mp_ass_subscript(IntPtr obRaw, IntPtr idx, IntPtr v)
{
- IntPtr tp = Runtime.PyObject_TYPE(ob);
+ var ob = new BorrowedReference(obRaw);
+ BorrowedReference tp = Runtime.PyObject_Type(ob);
var cls = (ClassBase)GetManagedObject(tp);
if (cls.indexer == null || !cls.indexer.CanSet)
@@ -422,7 +418,7 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args);
var numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs);
var temp = i + numOfDefaultArgs;
- IntPtr real = Runtime.PyTuple_New(temp + 1);
+ var real = NewReference.DangerousFromPointer(Runtime.PyTuple_New(temp + 1));
for (var n = 0; n < i; n++)
{
IntPtr item = Runtime.PyTuple_GetItem(args, n);
@@ -451,7 +447,7 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
}
finally
{
- Runtime.XDecref(real);
+ real.Dispose();
if (free)
{
diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs
index e55e89240..63523410a 100644
--- a/src/runtime/classderived.cs
+++ b/src/runtime/classderived.cs
@@ -52,7 +52,7 @@ internal ClassDerivedObject(Type tp) : base(tp)
var cls = GetManagedObject(tp) as ClassDerivedObject;
// call the managed constructor
- object obj = cls.binder.InvokeRaw(IntPtr.Zero, args, kw);
+ object obj = cls.binder.InvokeRaw(IntPtr.Zero, new BorrowedReference(args), kw);
if (obj == null)
{
return IntPtr.Zero;
diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs
index 18816781f..3a4ce8133 100644
--- a/src/runtime/classobject.cs
+++ b/src/runtime/classobject.cs
@@ -32,7 +32,7 @@ internal ClassObject(Type tp) : base(tp)
///
internal IntPtr GetDocString()
{
- MethodBase[] methods = binder.GetMethods();
+ var methods = binder.GetMethods();
var str = "";
foreach (MethodBase t in methods)
{
@@ -49,8 +49,9 @@ internal IntPtr GetDocString()
///
/// Implements __new__ for reflected classes and value types.
///
- public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
+ public static IntPtr tp_new(IntPtr tp, IntPtr rawArgs, IntPtr kw)
{
+ var args = new BorrowedReference(rawArgs);
var self = GetManagedObject(tp) as ClassObject;
// Sanity check: this ensures a graceful error if someone does
@@ -74,7 +75,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
return IntPtr.Zero;
}
- IntPtr op = Runtime.PyTuple_GetItem(args, 0);
+ BorrowedReference op = Runtime.PyTuple_GetItem(args, 0);
object result;
if (!Converter.ToManaged(op, type, out result, true))
diff --git a/src/runtime/constructorbinder.cs b/src/runtime/constructorbinder.cs
index 0cda3a3d9..15b69b7ba 100644
--- a/src/runtime/constructorbinder.cs
+++ b/src/runtime/constructorbinder.cs
@@ -29,7 +29,7 @@ internal ConstructorBinder(Type containingType)
/// 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)
+ internal object InvokeRaw(IntPtr inst, BorrowedReference args, IntPtr kw)
{
return InvokeRaw(inst, args, kw, null);
}
@@ -49,7 +49,7 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw)
/// 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)
+ internal object InvokeRaw(IntPtr inst, BorrowedReference args, IntPtr kw, MethodBase info)
{
object result;
@@ -78,7 +78,7 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
return result;
}
- Binding binding = Bind(inst, args, kw, info);
+ Binding binding = Bind(inst, args.DangerousGetAddress(), kw, info);
if (binding == null)
{
diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs
index 0c81c0a93..93ccb462d 100644
--- a/src/runtime/constructorbinding.cs
+++ b/src/runtime/constructorbinding.cs
@@ -121,7 +121,7 @@ public static IntPtr tp_repr(IntPtr ob)
Runtime.XIncref(self.repr);
return self.repr;
}
- MethodBase[] methods = self.ctorBinder.GetMethods();
+ var methods = self.ctorBinder.GetMethods();
string name = self.type.FullName;
var doc = "";
foreach (MethodBase t in methods)
@@ -211,7 +211,7 @@ public static IntPtr tp_call(IntPtr op, IntPtr args, IntPtr kw)
}*/
// Bind using ConstructorBinder.Bind and invoke the ctor providing a null instancePtr
// which will fire self.ctorInfo using ConstructorInfo.Invoke().
- object obj = self.ctorBinder.InvokeRaw(IntPtr.Zero, args, kw, self.ctorInfo);
+ object obj = self.ctorBinder.InvokeRaw(IntPtr.Zero, new BorrowedReference(args), kw, self.ctorInfo);
if (obj == null)
{
// XXX set an error
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 98fe99141..62b1a6fb5 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -298,11 +298,18 @@ internal static IntPtr ToPythonImplicit(object value)
return ToPython(value, objectType);
}
+ ///
+ /// Return a managed object for the given Python object, taking funny
+ /// byref types into account.
+ ///
+ internal static bool ToManaged(BorrowedReference value, Type type, out object result, bool setError)
+ => ToManaged(value.DangerousGetAddress(), type, out result, setError);
///
/// Return a managed object for the given Python object, taking funny
/// byref types into account.
///
+ [Obsolete(Util.UseReferenceOverloadMessage)]
internal static bool ToManaged(IntPtr value, Type type,
out object result, bool setError)
{
diff --git a/src/runtime/delegateobject.cs b/src/runtime/delegateobject.cs
index c5078740f..6a8efb45f 100644
--- a/src/runtime/delegateobject.cs
+++ b/src/runtime/delegateobject.cs
@@ -72,10 +72,11 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
///
/// Implements __call__ for reflected delegate types.
///
- public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw)
+ public static IntPtr tp_call(IntPtr obRaw, IntPtr args, IntPtr kw)
{
+ var ob = new BorrowedReference(obRaw);
// TODO: add fast type check!
- IntPtr pytype = Runtime.PyObject_TYPE(ob);
+ BorrowedReference pytype = Runtime.PyObject_Type(ob);
var self = (DelegateObject)GetManagedObject(pytype);
var o = GetManagedObject(ob) as CLRObject;
@@ -90,7 +91,7 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw)
{
return Exceptions.RaiseTypeError("invalid argument");
}
- return self.binder.Invoke(ob, args, kw);
+ return self.binder.Invoke(ob, new BorrowedReference(args), new BorrowedReference(kw));
}
diff --git a/src/runtime/indexer.cs b/src/runtime/indexer.cs
index 0772b57c6..22c122369 100644
--- a/src/runtime/indexer.cs
+++ b/src/runtime/indexer.cs
@@ -44,22 +44,22 @@ public void AddProperty(PropertyInfo pi)
}
}
- internal IntPtr GetItem(IntPtr inst, IntPtr args)
+ internal IntPtr GetItem(BorrowedReference inst, BorrowedReference args)
{
- return GetterBinder.Invoke(inst, args, IntPtr.Zero);
+ return GetterBinder.Invoke(inst, args, BorrowedReference.Null);
}
- internal void SetItem(IntPtr inst, IntPtr args)
+ internal void SetItem(BorrowedReference inst, BorrowedReference args)
{
- SetterBinder.Invoke(inst, args, IntPtr.Zero);
+ SetterBinder.Invoke(inst, args, BorrowedReference.Null);
}
internal bool NeedsDefaultArgs(IntPtr args)
{
var pynargs = Runtime.PyTuple_Size(args);
- MethodBase[] methods = SetterBinder.GetMethods();
- if (methods.Length == 0)
+ var methods = SetterBinder.GetMethods();
+ if (methods.Count == 0)
{
return false;
}
@@ -99,7 +99,7 @@ internal IntPtr GetDefaultArgs(IntPtr args)
var pynargs = Runtime.PyTuple_Size(args);
// Get the default arg tuple
- MethodBase[] methods = SetterBinder.GetMethods();
+ var methods = SetterBinder.GetMethods();
MethodBase mi = methods[0];
ParameterInfo[] pi = mi.GetParameters();
int clrnargs = pi.Length - 1;
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index bc2805d80..c3d237fcf 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -75,6 +75,9 @@ internal void FreeGCHandle()
}
}
+ internal static ManagedType GetManagedObject(BorrowedReference ob)
+ => ob.IsNull ? null : GetManagedObject(ob.DangerousGetAddress());
+ [Obsolete(Util.UseReferenceOverloadMessage)]
///
/// Given a Python object, return the associated managed object or null.
///
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index c0cc4c75c..3c376495e 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -1,9 +1,12 @@
using System;
-using System.Collections;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+
+using Microsoft.CSharp.RuntimeBinder;
namespace Python.Runtime
{
@@ -14,31 +17,31 @@ namespace Python.Runtime
/// ConstructorBinder, a minor variation used to invoke constructors.
///
[Serializable]
- internal class MethodBinder
+ internal class MethodBinder: System.Runtime.Serialization.IDeserializationCallback
{
- public ArrayList list;
- public MethodBase[] methods;
+ readonly List methods = new List();
+ [NonSerialized]
+ Lazy> callSiteCache;
public bool init = false;
public bool allow_threads = true;
- internal MethodBinder()
- {
- list = new ArrayList();
+ internal MethodBinder() {
+ this.InitializeCallSiteCache();
}
- internal MethodBinder(MethodInfo mi)
+ internal MethodBinder(MethodInfo mi):this()
{
- list = new ArrayList { mi };
+ this.methods.Add(mi);
}
public int Count
{
- get { return list.Count; }
+ get { return this.methods.Count; }
}
internal void AddMethod(MethodBase m)
{
- list.Add(m);
+ this.methods.Add(m);
}
///
@@ -158,13 +161,12 @@ internal static MethodInfo MatchSignatureAndParameters(MethodInfo[] mi, Type[] g
/// is arranged in order of precedence (done lazily to avoid doing it
/// at all for methods that are never called).
///
- internal MethodBase[] GetMethods()
+ internal List GetMethods()
{
if (!init)
{
// I'm sure this could be made more efficient.
- list.Sort(new MethodSorter());
- methods = (MethodBase[])list.ToArray(typeof(MethodBase));
+ this.methods.Sort(new MethodSorter());
init = true;
}
return methods;
@@ -281,9 +283,6 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
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)
{
@@ -301,15 +300,9 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
var pynargs = (int)Runtime.PyTuple_Size(args);
var isGeneric = false;
- if (info != null)
- {
- _methods = new MethodBase[1];
- _methods.SetValue(info, 0);
- }
- else
- {
- _methods = GetMethods();
- }
+
+ // loop to find match, return invoker w/ or /wo error
+ List _methods = info != null ? new List { info } : GetMethods();
// TODO: Clean up
foreach (MethodBase mi in _methods)
@@ -319,16 +312,15 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
isGeneric = true;
}
ParameterInfo[] pi = mi.GetParameters();
- ArrayList defaultArgList;
bool paramsArray;
- if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList))
+ if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out var defaultArgList))
{
continue;
}
var outs = 0;
var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList,
- needsResolution: _methods.Length > 1,
+ needsResolution: _methods.Count > 1,
outs: out outs);
if (margs == null)
@@ -417,7 +409,7 @@ static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out
static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
IntPtr args, int pyArgCount,
Dictionary kwargDict,
- ArrayList defaultArgList,
+ List