Skip to content

Commit a74664c

Browse files
committed
Return eleements of interface arrays as interface objects
Even when a method is declared to return an array of interfaces, the CLR may use an array of the concrete type. Keep track of the intended type in `ArrayObject` so that elements of the array can be properly wrapped in `InterfaceObject` when accessed.
1 parent 1dd36ae commit a74664c

File tree

5 files changed

+43
-2
lines changed

5 files changed

+43
-2
lines changed

src/runtime/arrayobject.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
4343
public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
4444
{
4545
var obj = (CLRObject)GetManagedObject(ob);
46+
var arrObj = (ArrayObject)GetManagedObjectType(ob);
4647
var items = obj.inst as Array;
47-
Type itemType = obj.inst.GetType().GetElementType();
48+
Type itemType = arrObj.type.GetElementType();
4849
int rank = items.Rank;
4950
int index;
5051
object value;

src/runtime/converter.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,15 @@ internal static IntPtr ToPython(object value, Type type)
179179
return CLRObject.GetInstHandle(value, ifaceObj.pyHandle);
180180
}
181181

182+
// We need to special case interface array handling to ensure we
183+
// produce the correct type. Value may be an array of some concrete
184+
// type (FooImpl[]), but we want access to go via the interface type
185+
// (IFoo[]).
186+
if (type.IsArray && type.GetElementType().IsInterface)
187+
{
188+
return CLRObject.GetInstHandle(value, type);
189+
}
190+
182191
// it the type is a python subclass of a managed type then return the
183192
// underlying python object rather than construct a new wrapper object.
184193
var pyderived = value as IPythonDerivedType;
@@ -188,7 +197,6 @@ internal static IntPtr ToPython(object value, Type type)
188197
return ClassDerivedObject.ToPython(pyderived);
189198
}
190199

191-
192200
// hmm - from Python, we almost never care what the declared
193201
// type is. we'd rather have the object bound to the actual
194202
// implementing class.

src/runtime/managedtype.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ internal static ManagedType GetManagedObject(IntPtr ob)
4545
return null;
4646
}
4747

48+
/// <summary>
49+
/// Given a Python object, return the associated managed object type or null.
50+
/// </summary>
51+
internal static ManagedType GetManagedObjectType(IntPtr ob)
52+
{
53+
if (ob != IntPtr.Zero)
54+
{
55+
IntPtr tp = Runtime.PyObject_TYPE(ob);
56+
var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
57+
if ((flags & TypeFlags.Managed) != 0)
58+
{
59+
tp = Marshal.ReadIntPtr(tp, TypeOffset.magic());
60+
var gc = (GCHandle)tp;
61+
return (ManagedType)gc.Target;
62+
}
63+
}
64+
return null;
65+
}
66+
4867

4968
internal static ManagedType GetManagedObjectErr(IntPtr ob)
5069
{

src/testing/interfacetest.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ public ISayHello1 GetNoSayHello(out ISayHello2 hello2)
5858
return null;
5959
}
6060

61+
public ISayHello1 [] GetISayHello1Array()
62+
{
63+
return new[] { this };
64+
}
65+
6166
public interface IPublic
6267
{
6368
}

src/tests/test_interface.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,11 @@ def test_null_interface_object_returned():
9999
hello1, hello2 = ob.GetNoSayHello(None)
100100
assert hello1 is None
101101
assert hello2 is None
102+
103+
def test_interface_array_returned():
104+
"""Test interface type used for methods returning interface arrays"""
105+
from Python.Test import InterfaceTest
106+
107+
ob = InterfaceTest()
108+
hellos = ob.GetISayHello1Array()
109+
assert type(hellos[0]).__name__ == 'ISayHello1'

0 commit comments

Comments
 (0)