Skip to content

Commit 8e4d254

Browse files
authored
Merge pull request #129 from Unity-Technologies/api-isinst-and-get-class
Implement object_isinst and object_get_class
2 parents 1839784 + 2d8358c commit 8e4d254

File tree

6 files changed

+249
-21
lines changed

6 files changed

+249
-21
lines changed

src/coreclr/vm/mono/mono_coreclr.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ struct HostStruct
6060
uintptr_t (*gchandle_new_weakref_v2)(MonoObject* obj, gboolean track_resurrection);
6161
intptr_t (*load_assembly_from_data)(const char* data, int64_t size);
6262
intptr_t (*load_assembly_from_path)(const char* path, int32_t length);
63+
MonoClass* (*object_get_class)(MonoObject* obj);
64+
MonoObject* (*object_isinst)(MonoObject* obj, MonoClass* klass);
6365
ManagedStringPtr_t (*string_from_utf16)(const gunichar2* text);
6466
ManagedStringPtr_t (*string_new_len)(MonoDomain* domain, const char* text, guint32 length);
6567
ManagedStringPtr_t (*string_new_utf16)(MonoDomain* domain, const guint16* text, gint32 length);
@@ -2395,10 +2397,10 @@ extern "C" EXPORT_API MonoMethodSignature* EXPORT_CC mono_method_signature_check
23952397
return NULL;
23962398
}
23972399

2398-
extern "C" EXPORT_API MonoClass* EXPORT_CC mono_object_get_class(MonoObject *obj)
2400+
// Generated by UnityEmbedHost.Generator - Commit these changes
2401+
extern "C" EXPORT_API MonoClass* EXPORT_CC mono_object_get_class(MonoObject* obj)
23992402
{
2400-
MonoClass_clr* klass = reinterpret_cast<MonoObject_clr*>(obj)->GetMethodTable();
2401-
return (MonoClass*)klass;
2403+
return g_HostStruct->object_get_class(obj);
24022404
}
24032405

24042406
extern "C" EXPORT_API guint32 EXPORT_CC mono_object_get_size(MonoObject *obj)
@@ -2430,12 +2432,10 @@ extern "C" EXPORT_API MonoMethod* EXPORT_CC mono_object_get_virtual_method(MonoO
24302432
return (MonoMethod*)m2;
24312433
}
24322434

2433-
extern "C" EXPORT_API MonoObject* EXPORT_CC mono_object_isinst(MonoObject *obj, MonoClass* klass)
2435+
// Generated by UnityEmbedHost.Generator - Commit these changes
2436+
extern "C" EXPORT_API MonoObject* EXPORT_CC mono_object_isinst(MonoObject* obj, MonoClass* klass)
24342437
{
2435-
MonoClass* clazz = mono_object_get_class(obj);
2436-
if (mono_class_is_subclass_of(clazz, klass, TRUE))
2437-
return obj;
2438-
return NULL;
2438+
return g_HostStruct->object_isinst(obj, klass);
24392439
}
24402440

24412441
extern "C" EXPORT_API MonoObject* EXPORT_CC mono_object_new(MonoDomain *domain, MonoClass *klass)
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Runtime.CompilerServices;
54
using Unity.CoreCLRHelpers;
65

76
namespace UnityEmbedHost.Tests;
@@ -12,11 +11,14 @@ namespace UnityEmbedHost.Tests;
1211
static class CoreCLRHostTestingWrappers
1312
{
1413
public static nint gchandle_new_v2(object obj, bool pinned)
15-
=> CoreCLRHost.gchandle_new_v2(Unsafe.As<object, IntPtr>(ref obj), pinned);
14+
=> CoreCLRHost.gchandle_new_v2(obj.UnsafeAsIntPtr(), pinned);
1615

1716
public static object? gchandle_get_target_v2(nint handleIn)
18-
{
19-
var result = CoreCLRHost.gchandle_get_target_v2(handleIn);
20-
return Unsafe.As<IntPtr, object>(ref result);
21-
}
17+
=> CoreCLRHost.gchandle_get_target_v2(handleIn).UnsafeAs<object>();
18+
19+
public static object? object_isinst(object obj, Type klass)
20+
=> CoreCLRHost.object_isinst(obj.UnsafeAsIntPtr(), klass.TypeHandleIntPtr()).UnsafeAs<object>();
21+
22+
public static Type object_get_class(object obj)
23+
=> CoreCLRHost.object_get_class(obj.UnsafeAsIntPtr()).TypeFromHandleIntPtr();
2224
}

unity/UnityEmbedHost.Tests/EmbeddingApiTests.cs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,125 @@ public void GCHandleNewAndGetTarget()
5555
GCHandle.FromIntPtr(handle1).Free();
5656
GCHandle.FromIntPtr(handle2).Free();
5757
}
58+
59+
// Classes and classes
60+
[TestCase(typeof(object), typeof(object), true)]
61+
[TestCase(typeof(Mammal), typeof(Mammal), true)]
62+
[TestCase(typeof(Mammal), typeof(Anaimal), true)]
63+
[TestCase(typeof(Cat), typeof(Anaimal), true)]
64+
[TestCase(typeof(Rock), typeof(Anaimal), false)]
65+
66+
// Classes and interfaces
67+
[TestCase(typeof(Mammal), typeof(IMammal), true)]
68+
[TestCase(typeof(Mammal), typeof(IAnimal), true)]
69+
[TestCase(typeof(Cat), typeof(IAnimal), true)]
70+
[TestCase(typeof(CatOnlyInterface), typeof(IAnimal), true)]
71+
72+
[TestCase(typeof(Rock), typeof(IAnimal), false)]
73+
[TestCase(typeof(NoInterfaces), typeof(IAnimal), false)]
74+
[TestCase(typeof(object), typeof(IRock), false)]
75+
76+
// Structs and ValueType
77+
[TestCase(typeof(ValueMammal), typeof(ValueType), true)]
78+
79+
// Structs and interfaces
80+
[TestCase(typeof(ValueMammal), typeof(IMammal), true)]
81+
[TestCase(typeof(ValueMammal), typeof(IAnimal), true)]
82+
[TestCase(typeof(ValueCat), typeof(IAnimal), true)]
83+
84+
[TestCase(typeof(ValueRock), typeof(IAnimal), false)]
85+
[TestCase(typeof(ValueNoInterfaces), typeof(IAnimal), false)]
86+
public void IsInst(Type obj, Type type, bool shouldBeInstanceOfType)
87+
{
88+
var instance = Activator.CreateInstance(obj)!;
89+
90+
CheckObjectIsInstance(obj, type, shouldBeInstanceOfType, instance);
91+
}
92+
93+
[TestCase(typeof(Mammal), 1, typeof(Mammal), 1, true)]
94+
[TestCase(typeof(Mammal), 1, typeof(Anaimal), 1, true)]
95+
96+
[TestCase(typeof(Mammal), 1, typeof(Mammal), 3, false)]
97+
98+
[TestCase(typeof(ValueMammal), 1, typeof(ValueMammal), 1, true)]
99+
[TestCase(typeof(ValueMammal), 1, typeof(IMammal), 1, false)]
100+
101+
[TestCase(typeof(ValueMammal), 1, typeof(ValueMammal), 3, false)]
102+
103+
[TestCase(typeof(Mammal), 1, typeof(IMammal), 1, true)]
104+
[TestCase(typeof(Mammal), 1, typeof(IAnimal), 1, true)]
105+
[TestCase(typeof(Mammal), 1, typeof(IMammal), 2, false)]
106+
public void IsInstArrays(Type obj, int rank, Type checkType, int checkRank, bool shouldBeInstanceOfType)
107+
{
108+
var instance = Array.CreateInstance(obj, new int[rank]);
109+
Type arrayType = Array.CreateInstance(checkType, new int[checkRank]).GetType();
110+
CheckObjectIsInstance(instance.GetType(), arrayType, shouldBeInstanceOfType, instance);
111+
}
112+
113+
[TestCase(typeof(Mammal), typeof(Mammal), 1, false)]
114+
[TestCase(typeof(Mammal), typeof(Anaimal), 1, false)]
115+
116+
[TestCase(typeof(Mammal), typeof(Mammal), 2, false)]
117+
118+
[TestCase(typeof(ValueMammal),typeof(ValueMammal), 1, false)]
119+
[TestCase(typeof(ValueMammal), typeof(IMammal), 1, false)]
120+
121+
[TestCase(typeof(ValueMammal), typeof(ValueMammal), 2, false)]
122+
123+
[TestCase(typeof(Mammal), typeof(IMammal), 1, false)]
124+
[TestCase(typeof(Mammal), typeof(IAnimal), 1, false)]
125+
[TestCase(typeof(Mammal), typeof(IMammal), 2, false)]
126+
public void IsInstNoneArrayToArrays(Type obj, Type checkType, int checkRank, bool shouldBeInstanceOfType)
127+
{
128+
var instance = Activator.CreateInstance(obj)!;
129+
130+
CheckObjectIsInstance(instance.GetType(), Array.CreateInstance(checkType, new int[checkRank]).GetType(), shouldBeInstanceOfType, instance);
131+
}
132+
133+
[TestCase(typeof(Mammal), 1, typeof(Mammal), false)]
134+
[TestCase(typeof(Mammal), 1, typeof(Anaimal), false)]
135+
136+
[TestCase(typeof(Mammal), 1, typeof(Mammal), false)]
137+
138+
[TestCase(typeof(ValueMammal), 1, typeof(ValueMammal), false)]
139+
[TestCase(typeof(ValueMammal), 1, typeof(IMammal), false)]
140+
141+
[TestCase(typeof(ValueMammal), 1, typeof(ValueMammal), false)]
142+
143+
[TestCase(typeof(Mammal), 1, typeof(IMammal), false)]
144+
[TestCase(typeof(Mammal), 1, typeof(IAnimal), false)]
145+
[TestCase(typeof(Mammal), 1, typeof(IMammal), false)]
146+
public void IsInstArrayToNoneArray(Type obj, int rank, Type checkType, bool shouldBeInstanceOfType)
147+
{
148+
var instance = Array.CreateInstance(obj, new int[rank]);
149+
150+
CheckObjectIsInstance(instance.GetType(), checkType, shouldBeInstanceOfType, instance);
151+
}
152+
153+
private static void CheckObjectIsInstance(Type obj, Type type, bool shouldBeInstanceOfType, object instance)
154+
{
155+
var result = CoreCLRHostTestingWrappers.object_isinst(instance, type);
156+
157+
if (shouldBeInstanceOfType)
158+
{
159+
if (result == null)
160+
Assert.Fail($"Expected {obj} to be of type {type}, but {nameof(CoreCLRHost.object_isinst)} claimed it wasn't");
161+
162+
Assert.That(result, Is.EqualTo(instance));
163+
}
164+
else
165+
{
166+
if (result != null)
167+
Assert.Fail($"Expected {obj} to NOT be of type {type}, but {nameof(CoreCLRHost.object_isinst)} claimed it was");
168+
169+
Assert.That(result, Is.Null);
170+
}
171+
}
172+
173+
[TestCase(typeof(Anaimal))]
174+
[TestCase(typeof(ValueAnimal))]
175+
public void GetClass(Type type)
176+
{
177+
Assert.That(CoreCLRHostTestingWrappers.object_get_class(Activator.CreateInstance(type)!), Is.EqualTo(type));
178+
}
58179
}

unity/UnityEmbedHost.Tests/Samples.cs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace UnityEmbedHost.Tests;
5+
6+
class Mammal : Anaimal, IMammal
7+
{
8+
}
9+
10+
class Cat : Mammal, ICat
11+
{
12+
}
13+
14+
class CatOnlyInterface : ICat
15+
{
16+
}
17+
18+
class Rock : IRock
19+
{
20+
}
21+
22+
class Anaimal : IAnimal
23+
{
24+
}
25+
26+
class NoInterfaces
27+
{
28+
}
29+
30+
interface IAnimal
31+
{
32+
}
33+
34+
interface IMammal : IAnimal
35+
{
36+
}
37+
38+
interface ICat : IMammal
39+
{
40+
}
41+
42+
interface IRock
43+
{
44+
}
45+
46+
struct MyStruct
47+
{
48+
49+
}
50+
51+
struct ValueMammal : IMammal
52+
{
53+
}
54+
55+
struct ValueCat : ICat
56+
{
57+
}
58+
59+
struct ValueRock : IRock
60+
{
61+
}
62+
63+
struct ValueAnimal : IAnimal
64+
{
65+
}
66+
67+
struct ValueNoInterfaces
68+
{
69+
}

unity/unity-embed-host/CoreCLRHost.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,24 +76,28 @@ public static StringPtr string_new_utf16([NativeCallbackType("MonoDomain*")] voi
7676
[return: NativeCallbackType("uintptr_t")]
7777
public static IntPtr gchandle_new_v2([NativeCallbackType("MonoObject*")] IntPtr obj, bool pinned)
7878
{
79-
GCHandle handle = GCHandle.Alloc(Unsafe.As<IntPtr, Object>(ref obj), pinned ? GCHandleType.Pinned : GCHandleType.Normal);
79+
GCHandle handle = GCHandle.Alloc(obj.UnsafeAs<object>(), pinned ? GCHandleType.Pinned : GCHandleType.Normal);
8080
return GCHandle.ToIntPtr(handle);
8181
}
8282

8383
[return: NativeCallbackType("uintptr_t")]
8484
public static IntPtr gchandle_new_weakref_v2([NativeCallbackType("MonoObject*")] IntPtr obj, bool track_resurrection)
8585
{
86-
GCHandle handle = GCHandle.Alloc(Unsafe.As<IntPtr, Object>(ref obj), track_resurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
86+
GCHandle handle = GCHandle.Alloc(obj.UnsafeAs<object>(), track_resurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
8787
return GCHandle.ToIntPtr(handle);
8888
}
8989

9090
[return: NativeWrapperType("MonoObject*")]
9191
public static IntPtr gchandle_get_target_v2([NativeWrapperType("uintptr_t")] IntPtr handleIn)
92-
{
93-
GCHandle handle = GCHandle.FromIntPtr(handleIn);
94-
object obj = handle.Target;
95-
return Unsafe.As<object, IntPtr>(ref obj);
96-
}
92+
=> handleIn.ToGCHandle().Target.UnsafeAsIntPtr();
93+
94+
[return: NativeCallbackType("MonoObject*")]
95+
public static IntPtr object_isinst([NativeCallbackType("MonoObject*")] IntPtr obj, [NativeCallbackType("MonoClass*")] IntPtr klass)
96+
=> obj.UnsafeAs<object>().GetType().IsAssignableTo(klass.TypeFromHandleIntPtr()) ? obj : nint.Zero;
97+
98+
[return: NativeCallbackType("MonoClass*")]
99+
public static IntPtr object_get_class([NativeCallbackType("MonoObject*")] IntPtr obj)
100+
=> obj.UnsafeAs<object>().TypeHandleIntPtr();
97101

98102
static StringPtr StringToPtr(string s)
99103
{

unity/unity-embed-host/Extensions.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
8+
namespace Unity.CoreCLRHelpers;
9+
10+
static class Extensions
11+
{
12+
public static Type TypeFromHandleIntPtr(this nint intPtrToTypeHandle)
13+
=> Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(intPtrToTypeHandle));
14+
15+
public static nint TypeHandleIntPtr(this object obj)
16+
=> obj.GetType().TypeHandleIntPtr();
17+
18+
public static nint TypeHandleIntPtr(this Type type)
19+
=> RuntimeTypeHandle.ToIntPtr(type.TypeHandle);
20+
21+
public static T UnsafeAs<T>(this nint intPtr)
22+
=> Unsafe.As<nint, T>(ref intPtr);
23+
24+
public static nint UnsafeAsIntPtr(this object obj)
25+
=> obj.UnsafeAs<nint>();
26+
27+
public static T UnsafeAs<T>(this object obj)
28+
=> Unsafe.As<object, T>(ref obj);
29+
30+
public static GCHandle ToGCHandle(this nint intPtr)
31+
=> GCHandle.FromIntPtr(intPtr);
32+
}

0 commit comments

Comments
 (0)