[runtime] Implement several xamarin_is_class_* variants for CoreCLR. (#11481)

When using the MonoVM, we compare MonoClass instances by pointer. This turns
out a bit complicated for CoreCLR, because our MonoClass instances are not
unique (there can be multiple MonoClass instances that refer to the same
type), so instead implement helper methods that do the comparison. This also
has the benefit of not requiring any memory allocations on CoreCLR.
This commit is contained in:
Rolf Bjarne Kvinge 2021-05-10 23:12:52 +02:00 коммит произвёл GitHub
Родитель 3623a13ab2
Коммит e4fbc5198b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 219 добавлений и 76 удалений

Просмотреть файл

@ -426,5 +426,52 @@ mono_class_is_valuetype (MonoClass * klass)
return rv;
}
bool
xamarin_is_class_nsobject (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSObject);
}
bool
xamarin_is_class_inativeobject (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_ObjCRuntime_INativeObject);
}
bool
xamarin_is_class_array (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_System_Array);
}
bool
xamarin_is_class_nsnumber (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSNumber);
}
bool
xamarin_is_class_nsvalue (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSValue);
}
bool
xamarin_is_class_nsstring (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSString);
}
bool
xamarin_is_class_intptr (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_System_IntPtr);
}
bool
xamarin_is_class_string (MonoClass *cls)
{
return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_System_String);
}
#endif // CORECLR_RUNTIME

Просмотреть файл

@ -466,6 +466,15 @@
OnlyDynamicUsage = false,
OnlyCoreCLR = true,
},
new XDelegate ("bool", "bool", "xamarin_bridge_is_class_of_type",
"MonoObject *", "MonoObject *", "classobj",
"enum XamarinLookupTypes", "Runtime.TypeLookup", "type"
) {
WrappedManagedFunction = "IsClassOfType",
OnlyDynamicUsage = false,
OnlyCoreCLR = true,
},
};
delegates.CalculateLengths ();
#><#+

Просмотреть файл

@ -57,7 +57,9 @@
"MonoClass *", "klass",
"MonoClass *", "klassc",
"mono_bool", "check_interfaces"
),
) {
XamarinRuntime = RuntimeMode.MonoVM,
},
new Export ("mono_bool", "mono_class_is_valuetype",
"MonoClass *", "klass"
@ -355,13 +357,17 @@
HasCoreCLRBridgeFunction = true,
},
new Export ("MonoClass *", "mono_get_intptr_class"),
new Export ("MonoClass *", "mono_get_intptr_class") {
XamarinRuntime = RuntimeMode.MonoVM,
},
new Export ("MonoClass *", "mono_get_string_class"),
new Export ("MonoImage *", "mono_get_corlib"),
new Export ("MonoClass *", "mono_get_array_class"),
new Export ("MonoClass *", "mono_get_array_class") {
XamarinRuntime = RuntimeMode.MonoVM,
},
new Export ("MonoDomain *", "mono_get_root_domain") {
XamarinRuntime = RuntimeMode.MonoVM,

Просмотреть файл

@ -291,6 +291,84 @@ xamarin_bridge_free_mono_signature (MonoMethodSignature **psig)
*psig = NULL;
}
bool
xamarin_is_class_nsobject (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
return mono_class_is_subclass_of (cls, xamarin_get_nsobject_class (), false);
}
bool
xamarin_is_class_inativeobject (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
return mono_class_is_subclass_of (cls, xamarin_get_inativeobject_class (), true);
}
bool
xamarin_is_class_array (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
return mono_class_is_subclass_of (cls, mono_get_array_class (), false);
}
bool
xamarin_is_class_nsnumber (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoClass *nsnumber_class = xamarin_get_nsnumber_class ();
if (nsnumber_class == NULL)
return false;
return mono_class_is_subclass_of (cls, nsnumber_class, false);
}
bool
xamarin_is_class_nsvalue (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoClass *nsvalue_class = xamarin_get_nsvalue_class ();
if (nsvalue_class == NULL)
return false;
return mono_class_is_subclass_of (cls, nsvalue_class, false);
}
bool
xamarin_is_class_nsstring (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoClass *nsstring_class = xamarin_get_nsstring_class ();
if (nsstring_class == NULL)
return false;
return mono_class_is_subclass_of (cls, nsstring_class, false);
}
bool
xamarin_is_class_intptr (MonoClass *cls)
{
return cls == mono_get_intptr_class ();
}
bool
xamarin_is_class_string (MonoClass *cls)
{
return cls == mono_get_string_class ();
}
#if DOTNET
bool

Просмотреть файл

@ -376,72 +376,6 @@ xamarin_new_nsobject (id self, MonoClass *klass, GCHandle *exception_gchandle)
return xamarin_gchandle_unwrap (obj);
}
bool
xamarin_is_class_nsobject (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
return mono_class_is_subclass_of (cls, xamarin_get_nsobject_class (), false);
}
bool
xamarin_is_class_inativeobject (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
return mono_class_is_subclass_of (cls, xamarin_get_inativeobject_class (), true);
}
bool
xamarin_is_class_array (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
return mono_class_is_subclass_of (cls, mono_get_array_class (), false);
}
bool
xamarin_is_class_nsnumber (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoClass *nsnumber_class = xamarin_get_nsnumber_class ();
if (nsnumber_class == NULL)
return false;
return mono_class_is_subclass_of (cls, nsnumber_class, false);
}
bool
xamarin_is_class_nsvalue (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoClass *nsvalue_class = xamarin_get_nsvalue_class ();
if (nsvalue_class == NULL)
return false;
return mono_class_is_subclass_of (cls, nsvalue_class, false);
}
bool
xamarin_is_class_nsstring (MonoClass *cls)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoClass *nsstring_class = xamarin_get_nsstring_class ();
if (nsstring_class == NULL)
return false;
return mono_class_is_subclass_of (cls, nsstring_class, false);
}
// Returns if a MonoClass is nullable.
// Will also return the element type (it the type is nullable, and if out pointer is not NULL).
bool

Просмотреть файл

@ -311,7 +311,7 @@ xamarin_invoke_trampoline (enum TrampolineType type, id self, SEL sel, iterator_
arg_frame [ofs] = mobj;
ADD_TO_MONOOBJECT_RELEASE_LIST (mobj);
LOGZ (" argument %i is a ref ptr/INativeObject %p: %p\n", i + 1, arg, arg_frame [ofs]);
} else if (p_klass == mono_get_string_class ()) {
} else if (xamarin_is_class_string (p_klass)) {
MonoString *mstr = xamarin_nsstring_to_string (domain, *(NSString **) arg);
arg_frame [ofs] = mstr;
ADD_TO_MONOOBJECT_RELEASE_LIST (mstr);
@ -426,7 +426,7 @@ xamarin_invoke_trampoline (enum TrampolineType type, id self, SEL sel, iterator_
id id_arg = (id) arg;
MonoClass *p_klass = mono_class_from_mono_type (p);
ADD_TO_MONOOBJECT_RELEASE_LIST (p_klass);
if (p_klass == mono_get_intptr_class ()) {
if (xamarin_is_class_intptr (p_klass)) {
arg_frame [ofs] = id_arg;
arg_ptrs [i + mofs] = &arg_frame [frameofs];
LOGZ (" argument %i is IntPtr: %p\n", i + 1, id_arg);
@ -435,7 +435,7 @@ xamarin_invoke_trampoline (enum TrampolineType type, id self, SEL sel, iterator_
arg_ptrs [i + mofs] = NULL;
break;
} else {
if (p_klass == mono_get_string_class ()) {
if (xamarin_is_class_string (p_klass)) {
NSString *str = (NSString *) id_arg;
MonoString *mstr = xamarin_nsstring_to_string (domain, str);
arg_ptrs [i + mofs] = mstr;
@ -639,7 +639,7 @@ xamarin_invoke_trampoline (enum TrampolineType type, id self, SEL sel, iterator_
continue;
} else if (value == NULL) {
LOGZ (" writing back null to argument at index %i (%p)\n", i + 1, arg);
} else if (p_klass == mono_get_string_class ()) {
} else if (xamarin_is_class_string (p_klass)) {
obj = xamarin_string_to_nsstring ((MonoString *) value, false);
LOGZ (" writing back managed string %p to argument at index %i (%p)\n", value, i + 1, arg);
} else if (xamarin_is_class_nsobject (p_klass)) {

Просмотреть файл

@ -102,7 +102,7 @@ xamarin_marshal_return_value_impl (MonoType *mtype, const char *type, MonoObject
MonoType *original_tp = mono_reflection_type_get_type (original_type);
xamarin_mono_object_release (&original_type);
returnValue = xamarin_generate_conversion_to_native (retval, mono_class_get_type (r_klass), original_tp, method, (void *) INVALID_TOKEN_REF, exception_gchandle);
} else if (r_klass == mono_get_string_class ()) {
} else if (xamarin_is_class_string (r_klass)) {
returnValue = xamarin_string_to_nsstring ((MonoString *) retval, retain);
} else if (xamarin_is_class_array (r_klass)) {
NSArray *rv = xamarin_managed_array_to_nsarray ((MonoArray *) retval, NULL, r_klass, exception_gchandle);
@ -1245,7 +1245,7 @@ xamarin_managed_array_to_nsarray (MonoArray *array, MonoType *managed_type, Mono
xamarin_mono_object_release (&mclass);
if (e_klass == mono_get_string_class ()) {
if (xamarin_is_class_string (e_klass)) {
return xamarin_managed_string_array_to_nsarray (array, exception_gchandle);
} else if (xamarin_is_class_nsobject (e_klass)) {
return xamarin_managed_nsobject_array_to_nsarray (array, exception_gchandle);
@ -1344,7 +1344,7 @@ xamarin_nsarray_to_managed_array (NSArray *array, MonoType *managed_type, MonoCl
xamarin_mono_object_release (&mclass);
MonoClass *e_klass = mono_class_get_element_class (managed_class);
if (e_klass == mono_get_string_class ()) {
if (xamarin_is_class_string (e_klass)) {
return xamarin_nsarray_to_managed_string_array (array, exception_gchandle);
} else if (xamarin_is_class_nsobject (e_klass)) {
return xamarin_nsarray_to_managed_nsobject_array (array, managed_type, e_klass, exception_gchandle);

Просмотреть файл

@ -63,6 +63,18 @@ enum XamarinGCHandleType : int {
XamarinGCHandleTypePinned = 3,
};
// Keep in sync with Runtime.LookupTypes in Runtime.CoreCLR.cs
enum XamarinLookupTypes : int {
XamarinLookupTypes_System_Array,
XamarinLookupTypes_System_String,
XamarinLookupTypes_System_IntPtr,
XamarinLookupTypes_Foundation_NSNumber,
XamarinLookupTypes_Foundation_NSObject,
XamarinLookupTypes_Foundation_NSString,
XamarinLookupTypes_Foundation_NSValue,
XamarinLookupTypes_ObjCRuntime_INativeObject,
};
extern bool mono_use_llvm; // this is defined inside mono
#if DEBUG

Просмотреть файл

@ -167,6 +167,8 @@ bool xamarin_is_class_nsnumber (MonoClass *cls);
bool xamarin_is_class_nsvalue (MonoClass *cls);
bool xamarin_is_class_nsstring (MonoClass *cls);
bool xamarin_is_class_nullable (MonoClass *cls, MonoClass **element_type, GCHandle *exception_gchandle);
bool xamarin_is_class_intptr (MonoClass *cls);
bool xamarin_is_class_string (MonoClass *cls);
MonoClass * xamarin_get_nullable_type (MonoClass *cls, GCHandle *exception_gchandle);
MonoType * xamarin_get_parameter_type (MonoMethod *managed_method, int index);
MonoObject * xamarin_get_nsobject_with_type_for_ptr (id self, bool owns, MonoType* type, GCHandle *exception_gchandle);

Просмотреть файл

@ -23,6 +23,18 @@ using MonoObjectPtr=System.IntPtr;
namespace ObjCRuntime {
public partial class Runtime {
// Keep in sync with XamarinLookupTypes in coreclr-bridge.h
internal enum TypeLookup {
System_Array,
System_String,
System_IntPtr,
Foundation_NSNumber,
Foundation_NSObject,
Foundation_NSString,
Foundation_NSValue,
ObjCRuntime_INativeObject,
}
// This struct must be kept in sync with the _MonoObject struct in coreclr-bridge.h
[StructLayout (LayoutKind.Sequential)]
internal struct MonoObject {
@ -75,6 +87,49 @@ namespace ObjCRuntime {
throw new InvalidOperationException ($"Could not find any assemblies named {name}");
}
unsafe static bool IsClassOfType (MonoObject *typeobj, TypeLookup match)
{
return IsClassOfType ((Type) GetMonoObjectTarget (typeobj), match);
}
static bool IsClassOfType (Type type, TypeLookup match)
{
var rv = false;
switch (match) {
case TypeLookup.System_Array:
rv = type.IsArray;
break;
case TypeLookup.System_String:
rv = type == typeof (System.String);
break;
case TypeLookup.System_IntPtr:
rv = type == typeof (System.IntPtr);
break;
case TypeLookup.Foundation_NSNumber:
rv = typeof (Foundation.NSNumber).IsAssignableFrom (type);
break;
case TypeLookup.Foundation_NSObject:
rv = typeof (Foundation.NSObject).IsAssignableFrom (type);
break;
case TypeLookup.Foundation_NSString:
rv = typeof (Foundation.NSString).IsAssignableFrom (type);
break;
case TypeLookup.Foundation_NSValue:
rv = typeof (Foundation.NSValue).IsAssignableFrom (type);
break;
case TypeLookup.ObjCRuntime_INativeObject:
rv = typeof (ObjCRuntime.INativeObject).IsAssignableFrom (type);
break;
default:
throw new ArgumentOutOfRangeException (nameof (type));
}
log_coreclr ($"IsClassOfType ({type}, {match}) => {rv}");
return rv;
}
static IntPtr CreateGCHandle (IntPtr gchandle, GCHandleType type)
{
// It's valid to create a GCHandle to a null value.