Skip to content

DLR-based overload resolution #1278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/embed_tests/TestDomainReload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
");
Expand Down Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion src/runtime/BorrowedReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ readonly ref struct BorrowedReference
public bool IsNull => this.pointer == IntPtr.Zero;


/// <summary>Gets a raw pointer to the Python object</summary>
/// <summary>Gets a raw pointer to the Python object. Performs null check</summary>
public IntPtr DangerousGetAddress()
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
/// <summary>Gets a raw pointer to the Python object</summary>
public IntPtr DangerousGetAddressOrNull() => this.pointer;

/// <summary>
/// Creates new instance of <see cref="BorrowedReference"/> from raw pointer. Unsafe.
Expand All @@ -22,5 +24,7 @@ public BorrowedReference(IntPtr pointer)
{
this.pointer = pointer;
}

public static BorrowedReference Null => default;
}
}
11 changes: 11 additions & 0 deletions src/runtime/Native/PyFrameObjectReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Python.Runtime.Native
{
readonly ref struct PyFrameObjectReference
{
readonly IntPtr pointer;

public bool IsNull => this.pointer == IntPtr.Zero;
}
}
25 changes: 14 additions & 11 deletions src/runtime/Python.Runtime.15.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.CSharp">
<Version>4.7.0</Version>
</PackageReference>
<PackageReference Include="System.Security.Permissions" Version="4.4.0" />
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
</ItemGroup>
Expand All @@ -143,20 +146,20 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<None Update="intern_.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>intern_.cs</LastGenOutput>
</None>
<ItemGroup>
<None Update="intern_.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>intern_.cs</LastGenOutput>
</None>
</ItemGroup>


<ItemGroup>
<Compile Update="intern_.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>intern_.tt</DependentUpon>
</Compile>
<ItemGroup>
<Compile Update="intern_.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>intern_.tt</DependentUpon>
</Compile>
</ItemGroup>

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
Expand Down
1 change: 1 addition & 0 deletions src/runtime/Python.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<Compile Include="assemblymanager.cs" />
<Compile Include="BorrowedReference.cs" />
<Compile Include="bufferinterface.cs" />
<Compile Include="callsitebinder.cs" />
<Compile Include="classderived.cs" />
<Compile Include="classbase.cs" />
<Compile Include="classmanager.cs" />
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
60 changes: 60 additions & 0 deletions src/runtime/callsitebinder.cs
Original file line number Diff line number Diff line change
@@ -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<Type> typeArguments,
Type context,
IEnumerable<CSharpArgumentInfo> 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<ParameterExpression> 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<Type> typeArguments,
Type context,
IEnumerable<CSharpArgumentInfo> argumentInfo)
=> new PythonNetCallSiteBinder(methodName, typeArguments, context, argumentInfo);
}
}
44 changes: 20 additions & 24 deletions src/runtime/classbase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,10 @@ protected override void OnLoad(InterDomainContext context)
/// <summary>
/// Implements __getitem__ for reflected classes and value types.
/// </summary>
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)
Expand All @@ -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;
}


/// <summary>
/// Implements __setitem__ for reflected classes and value types.
/// </summary>
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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -451,7 +447,7 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
}
finally
{
Runtime.XDecref(real);
real.Dispose();

if (free)
{
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/classderived.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions src/runtime/classobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ internal ClassObject(Type tp) : base(tp)
/// </summary>
internal IntPtr GetDocString()
{
MethodBase[] methods = binder.GetMethods();
var methods = binder.GetMethods();
var str = "";
foreach (MethodBase t in methods)
{
Expand All @@ -49,8 +49,9 @@ internal IntPtr GetDocString()
/// <summary>
/// Implements __new__ for reflected classes and value types.
/// </summary>
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
Expand All @@ -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))
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/constructorbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
/// </summary>
internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw)
internal object InvokeRaw(IntPtr inst, BorrowedReference args, IntPtr kw)
{
return InvokeRaw(inst, args, kw, null);
}
Expand All @@ -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).
/// </remarks>
internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
internal object InvokeRaw(IntPtr inst, BorrowedReference args, IntPtr kw, MethodBase info)
{
object result;

Expand Down Expand Up @@ -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)
{
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/constructorbinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,18 @@ internal static IntPtr ToPythonImplicit(object value)
return ToPython(value, objectType);
}

/// <summary>
/// Return a managed object for the given Python object, taking funny
/// byref types into account.
/// </summary>
internal static bool ToManaged(BorrowedReference value, Type type, out object result, bool setError)
=> ToManaged(value.DangerousGetAddress(), type, out result, setError);

/// <summary>
/// Return a managed object for the given Python object, taking funny
/// byref types into account.
/// </summary>
[Obsolete(Util.UseReferenceOverloadMessage)]
internal static bool ToManaged(IntPtr value, Type type,
out object result, bool setError)
{
Expand Down
Loading