[Mono] UnsafeAccessor: Support ambiguous method match (#89217)
authorFan Yang <52458914+fanyang-mono@users.noreply.github.com>
Tue, 25 Jul 2023 17:25:55 +0000 (13:25 -0400)
committerGitHub <noreply@github.com>
Tue, 25 Jul 2023 17:25:55 +0000 (13:25 -0400)
* Support Ambiguous Match

* Stop unsafe accessor method lookup from walking the type hierarchy. And address the review feedback

* Handle the AmbiguousMatchException

* Fix test failure

* Adjust Mono function pointer comparison behavior to match with CoreCLR

* Fix ambiguous match

* Enable Verify_InheritanceMethodResolution

* Remove redundant curly braces

* Address review feedback

src/mono/mono/metadata/loader.c
src/mono/mono/metadata/marshal-lightweight.c
src/mono/mono/metadata/metadata-internals.h
src/mono/mono/metadata/metadata.c
src/mono/mono/metadata/method-builder-ilgen.c
src/mono/mono/metadata/unsafe-accessor.c
src/mono/mono/utils/mono-error-internals.h
src/mono/mono/utils/mono-error.c
src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs

index b3b7bbd..5f21d33 100644 (file)
@@ -403,31 +403,6 @@ mono_field_from_token_checked (MonoImage *image, guint32 token, MonoClass **retk
        return field;
 }
 
-static gboolean
-mono_metadata_signature_vararg_match (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
-{
-       int i;
-
-       if (sig1->hasthis != sig2->hasthis ||
-           sig1->sentinelpos != sig2->sentinelpos)
-               return FALSE;
-
-       for (i = 0; i < sig1->sentinelpos; i++) {
-               MonoType *p1 = sig1->params[i];
-               MonoType *p2 = sig2->params[i];
-
-               /*if (p1->attrs != p2->attrs)
-                       return FALSE;
-               */
-               if (!mono_metadata_type_equal (p1, p2))
-                       return FALSE;
-       }
-
-       if (!mono_metadata_type_equal (sig1->ret, sig2->ret))
-               return FALSE;
-       return TRUE;
-}
-
 static MonoMethod *
 find_method_in_class (MonoClass *klass, const char *name, const char *qname, const char *fqname,
                      MonoMethodSignature *sig, MonoClass *from_class, MonoError *error)
@@ -441,7 +416,7 @@ find_method_in_class (MonoClass *klass, const char *name, const char *qname, con
 
        MonoImage *klass_image = m_class_get_image (klass);
        /* FIXME: !mono_class_is_ginst (from_class) condition causes test failures. */
-       if (m_class_get_type_token (klass) && !image_is_dynamic (klass_image) && !m_class_get_methods (klass) && !m_class_get_rank (klass) && klass == from_class && !mono_class_is_ginst (from_class)) {
+       if (m_class_get_type_token (klass) && !image_is_dynamic (klass_image) && !m_class_get_methods (klass) && !m_class_get_rank (klass) && klass == from_class && !mono_class_is_ginst (from_class) && (sig->call_convention != MONO_CALL_VARARG)) {
                int first_idx = mono_class_get_first_method_idx (klass);
                int mcount = mono_class_get_method_count (klass);
                for (i = 0; i < mcount; ++i) {
@@ -466,7 +441,7 @@ find_method_in_class (MonoClass *klass, const char *name, const char *qname, con
                                other_sig = mono_method_signature_checked (method, error);
                                if (!is_ok (error)) //bail out if we hit a loader error
                                        return NULL;
-                               if (other_sig && (sig->call_convention != MONO_CALL_VARARG) && mono_metadata_signature_equal (sig, other_sig))
+                               if (other_sig && mono_metadata_signature_equal (sig, other_sig))
                                        return method;
                        }
                }
@@ -514,7 +489,7 @@ find_method_in_class (MonoClass *klass, const char *name, const char *qname, con
                        continue;
 
                if (sig->call_convention == MONO_CALL_VARARG) {
-                       if (mono_metadata_signature_vararg_match (sig, msig)) {
+                       if (mono_metadata_signature_equal_vararg (sig, msig)) {
                                matched = TRUE;
                                break;
                        }
@@ -972,22 +947,6 @@ fail:
        return NULL;
 }
 
-MonoMethod*
-mono_unsafe_accessor_find_ctor (MonoClass *in_class, MonoMethodSignature *sig, MonoClass *from_class, MonoError *error)
-{
-       // This doesn't work for constructors because find_method explicitly disallows ".ctor" and ".cctor"
-       //return find_method (in_class, /*ic*/NULL, name, sig, from_class, error);
-       return find_method_in_class (in_class, ".ctor", /*qname*/NULL, /*fqname*/NULL, sig, from_class, error);
-}
-
-MonoMethod*
-mono_unsafe_accessor_find_method (MonoClass *in_class, const char *name, MonoMethodSignature *sig, MonoClass *from_class, MonoError *error)
-{
-       // This doesn't work for constructors because find_method explicitly disallows ".ctor" and ".cctor"
-       return find_method (in_class, /*ic*/NULL, name, sig, from_class, error);
-}
-
-
 static MonoMethod *
 method_from_methodspec (MonoImage *image, MonoGenericContext *context, guint32 idx, MonoError *error)
 {
index 7df5b7d..9a0b55f 100644 (file)
@@ -2446,7 +2446,10 @@ emit_unsafe_accessor_ctor_wrapper (MonoMethodBuilder *mb, MonoMethod *accessor_m
        MonoClass *in_class = mono_class_is_ginst (target_class) ? mono_class_get_generic_class (target_class)->container_class : target_class;
        MonoMethod *target_method = mono_unsafe_accessor_find_ctor (in_class, member_sig, target_class, find_method_error);
        if (!is_ok (find_method_error) || target_method == NULL) {
-               emit_missing_method_error (mb, find_method_error, "constructor");
+               if (mono_error_get_error_code (find_method_error) == MONO_ERROR_GENERIC)
+                       mono_mb_emit_exception_for_error (mb, find_method_error);
+               else
+                       emit_missing_method_error (mb, find_method_error, "constructor");
                mono_error_cleanup (find_method_error);
                return;
        }
@@ -2491,7 +2494,10 @@ emit_unsafe_accessor_method_wrapper (MonoMethodBuilder *mb, MonoMethod *accessor
        else
                target_method = mono_unsafe_accessor_find_ctor (in_class, member_sig, target_class, find_method_error);
        if (!is_ok (find_method_error) || target_method == NULL) {
-               emit_missing_method_error (mb, find_method_error, member_name);
+               if (mono_error_get_error_code (find_method_error) == MONO_ERROR_GENERIC)
+                       mono_mb_emit_exception_for_error (mb, find_method_error);
+               else
+                       emit_missing_method_error (mb, find_method_error, member_name);
                mono_error_cleanup (find_method_error);
                return;
        }
index 98896b1..47890b0 100644 (file)
@@ -1018,6 +1018,15 @@ gboolean       mono_metadata_generic_inst_equal (gconstpointer ka, gconstpointer
 gboolean
 mono_metadata_signature_equal_no_ret (MonoMethodSignature *sig1, MonoMethodSignature *sig2);
 
+gboolean
+mono_metadata_signature_equal_ignore_custom_modifier (MonoMethodSignature *sig1, MonoMethodSignature *sig2);
+
+gboolean
+mono_metadata_signature_equal_vararg (MonoMethodSignature *sig1, MonoMethodSignature *sig2);
+
+gboolean
+mono_metadata_signature_equal_vararg_ignore_custom_modifier (MonoMethodSignature *sig1, MonoMethodSignature *sig2);
+
 MONO_API void
 mono_metadata_field_info_with_mempool (
                                          MonoImage *meta,
index c7398d5..7c7cd44 100644 (file)
@@ -46,12 +46,17 @@ typedef struct {
        MonoGenericContext context;
 } MonoInflatedMethodSignature;
 
+enum {
+       MONO_TYPE_EQ_FLAGS_SIG_ONLY = 1,
+       MONO_TYPE_EQ_FLAG_IGNORE_CMODS = 2,
+};
+
 static gboolean do_mono_metadata_parse_type (MonoType *type, MonoImage *m, MonoGenericContainer *container, gboolean transient,
                                         const char *ptr, const char **rptr, MonoError *error);
 
-static gboolean do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, gboolean signature_only);
+static gboolean do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, int equiv_flags);
 static gboolean mono_metadata_class_equal (MonoClass *c1, MonoClass *c2, gboolean signature_only);
-static gboolean mono_metadata_fnptr_equal (MonoMethodSignature *s1, MonoMethodSignature *s2, gboolean signature_only);
+static gboolean mono_metadata_fnptr_equal (MonoMethodSignature *s1, MonoMethodSignature *s2, int equiv_flags);
 static gboolean _mono_metadata_generic_class_equal (const MonoGenericClass *g1, const MonoGenericClass *g2,
                                                    gboolean signature_only);
 static void free_generic_inst (MonoGenericInst *ginst);
@@ -1901,7 +1906,7 @@ mono_generic_inst_equal_full (const MonoGenericInst *a, const MonoGenericInst *b
        if (a->is_open != b->is_open || a->type_argc != b->type_argc)
                return FALSE;
        for (guint i = 0; i < a->type_argc; ++i) {
-               if (!do_mono_metadata_type_equal (a->type_argv [i], b->type_argv [i], signature_only))
+               if (!do_mono_metadata_type_equal (a->type_argv [i], b->type_argv [i], signature_only ? MONO_TYPE_EQ_FLAGS_SIG_ONLY : 0))
                        return FALSE;
        }
        return TRUE;
@@ -5690,32 +5695,58 @@ mono_metadata_class_equal (MonoClass *c1, MonoClass *c2, gboolean signature_only
                return mono_metadata_class_equal (c1_type->data.klass, c2_type->data.klass, signature_only);
        if (signature_only &&
            (c1_type->type == MONO_TYPE_ARRAY) && (c2_type->type == MONO_TYPE_ARRAY))
-               return do_mono_metadata_type_equal (c1_type, c2_type, signature_only);
+               return do_mono_metadata_type_equal (c1_type, c2_type, signature_only ? MONO_TYPE_EQ_FLAGS_SIG_ONLY : 0);
        if (signature_only &&
                (c1_type->type == MONO_TYPE_PTR) && (c2_type->type == MONO_TYPE_PTR))
-               return do_mono_metadata_type_equal (c1_type->data.type, c2_type->data.type, signature_only);
+               return do_mono_metadata_type_equal (c1_type->data.type, c2_type->data.type, signature_only ? MONO_TYPE_EQ_FLAGS_SIG_ONLY : 0);
        if (signature_only &&
                (c1_type->type == MONO_TYPE_FNPTR) && (c2_type->type == MONO_TYPE_FNPTR))
-               return mono_metadata_fnptr_equal (c1_type->data.method, c2_type->data.method, signature_only);
+               return mono_metadata_fnptr_equal (c1_type->data.method, c2_type->data.method, signature_only ? MONO_TYPE_EQ_FLAGS_SIG_ONLY : 0);
        return FALSE;
 }
 
+static int
+mono_metadata_check_call_convention_category (unsigned int call_convention)
+{
+       switch (call_convention) {
+       case MONO_CALL_DEFAULT:
+               return 1;
+       case MONO_CALL_C:
+       case MONO_CALL_STDCALL:
+       case MONO_CALL_THISCALL:
+       case MONO_CALL_FASTCALL:
+       case MONO_CALL_UNMANAGED_MD:
+               return 2;
+       case MONO_CALL_VARARG:
+               return 3;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
 static gboolean
-mono_metadata_fnptr_equal (MonoMethodSignature *s1, MonoMethodSignature *s2, gboolean signature_only)
+mono_metadata_fnptr_equal (MonoMethodSignature *s1, MonoMethodSignature *s2, int equiv_flags)
 {
        gpointer iter1 = 0, iter2 = 0;
 
        if (s1 == s2)
                return TRUE;
-       if (s1->call_convention != s2->call_convention)
-               return FALSE;
+
+       if ((equiv_flags & MONO_TYPE_EQ_FLAG_IGNORE_CMODS) == 0) {
+               if (s1->call_convention != s2->call_convention)
+                       return FALSE;
+       } else {
+               if (mono_metadata_check_call_convention_category (s1->call_convention) != mono_metadata_check_call_convention_category (s2->call_convention))
+                       return FALSE;
+       }
+
        if (s1->sentinelpos != s2->sentinelpos)
                return FALSE;
        if (s1->hasthis != s2->hasthis)
                return FALSE;
        if (s1->explicit_this != s2->explicit_this)
                return FALSE;
-       if (! do_mono_metadata_type_equal (s1->ret, s2->ret, signature_only))
+       if (! do_mono_metadata_type_equal (s1->ret, s2->ret, equiv_flags))
                return FALSE;
        if (s1->param_count != s2->param_count)
                return FALSE;
@@ -5726,7 +5757,7 @@ mono_metadata_fnptr_equal (MonoMethodSignature *s1, MonoMethodSignature *s2, gbo
 
                if (t1 == NULL || t2 == NULL)
                        return (t1 == t2);
-               if (! do_mono_metadata_type_equal (t1, t2, signature_only))
+               if (! do_mono_metadata_type_equal (t1, t2, equiv_flags))
                        return FALSE;
        }
 }
@@ -5755,7 +5786,7 @@ mono_metadata_custom_modifiers_equal (MonoType *t1, MonoType *t2, gboolean signa
                if (cm1_required != cm2_required)
                        return FALSE;
 
-               if (!do_mono_metadata_type_equal (cm1_type, cm2_type, signature_only))
+               if (!do_mono_metadata_type_equal (cm1_type, cm2_type, signature_only ? MONO_TYPE_EQ_FLAGS_SIG_ONLY : 0))
                        return FALSE;
        }
        return TRUE;
@@ -5771,17 +5802,19 @@ mono_metadata_custom_modifiers_equal (MonoType *t1, MonoType *t2, gboolean signa
  * Returns: #TRUE if @t1 and @t2 are equal.
  */
 static gboolean
-do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, gboolean signature_only)
+do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, int equiv_flags)
 {
        if (t1->type != t2->type || m_type_is_byref (t1) != m_type_is_byref (t2))
                return FALSE;
 
        gboolean cmod_reject = FALSE;
 
-       if (t1->has_cmods != t2->has_cmods)
-               cmod_reject = TRUE;
-       else if (t1->has_cmods && t2->has_cmods) {
-               cmod_reject = !mono_metadata_custom_modifiers_equal (t1, t2, signature_only);
+       if ((equiv_flags & MONO_TYPE_EQ_FLAG_IGNORE_CMODS) == 0) {
+               if (t1->has_cmods != t2->has_cmods)
+                       cmod_reject = TRUE;
+               else if (t1->has_cmods && t2->has_cmods) {
+                       cmod_reject = !mono_metadata_custom_modifiers_equal (t1, t2, (equiv_flags & MONO_TYPE_EQ_FLAGS_SIG_ONLY) != 0);
+               }
        }
 
        gboolean result = FALSE;
@@ -5810,31 +5843,31 @@ do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, gboolean signature_only
        case MONO_TYPE_VALUETYPE:
        case MONO_TYPE_CLASS:
        case MONO_TYPE_SZARRAY:
-               result = mono_metadata_class_equal (t1->data.klass, t2->data.klass, signature_only);
+               result = mono_metadata_class_equal (t1->data.klass, t2->data.klass, (equiv_flags & MONO_TYPE_EQ_FLAGS_SIG_ONLY) != 0);
                break;
        case MONO_TYPE_PTR:
-               result = do_mono_metadata_type_equal (t1->data.type, t2->data.type, signature_only);
+               result = do_mono_metadata_type_equal (t1->data.type, t2->data.type, equiv_flags);
                break;
        case MONO_TYPE_ARRAY:
                if (t1->data.array->rank != t2->data.array->rank)
                        result = FALSE;
                else
-                       result = mono_metadata_class_equal (t1->data.array->eklass, t2->data.array->eklass, signature_only);
+                       result = mono_metadata_class_equal (t1->data.array->eklass, t2->data.array->eklass, (equiv_flags & MONO_TYPE_EQ_FLAGS_SIG_ONLY) != 0);
                break;
        case MONO_TYPE_GENERICINST:
                result = _mono_metadata_generic_class_equal (
-                       t1->data.generic_class, t2->data.generic_class, signature_only);
+                       t1->data.generic_class, t2->data.generic_class, (equiv_flags & MONO_TYPE_EQ_FLAGS_SIG_ONLY) != 0);
                break;
        case MONO_TYPE_VAR:
                result = mono_metadata_generic_param_equal_internal (
-                       t1->data.generic_param, t2->data.generic_param, signature_only);
+                       t1->data.generic_param, t2->data.generic_param, (equiv_flags & MONO_TYPE_EQ_FLAGS_SIG_ONLY) != 0);
                break;
        case MONO_TYPE_MVAR:
                result = mono_metadata_generic_param_equal_internal (
-                       t1->data.generic_param, t2->data.generic_param, signature_only);
+                       t1->data.generic_param, t2->data.generic_param, (equiv_flags & MONO_TYPE_EQ_FLAGS_SIG_ONLY) != 0);
                break;
        case MONO_TYPE_FNPTR:
-               result = mono_metadata_fnptr_equal (t1->data.method, t2->data.method, signature_only);
+               result = mono_metadata_fnptr_equal (t1->data.method, t2->data.method, equiv_flags);
                break;
        default:
                g_error ("implement type compare for %0x!", t1->type);
@@ -5850,7 +5883,7 @@ do_mono_metadata_type_equal (MonoType *t1, MonoType *t2, gboolean signature_only
 gboolean
 mono_metadata_type_equal (MonoType *t1, MonoType *t2)
 {
-       return do_mono_metadata_type_equal (t1, t2, FALSE);
+       return do_mono_metadata_type_equal (t1, t2, 0);
 }
 
 /**
@@ -5867,11 +5900,12 @@ mono_metadata_type_equal (MonoType *t1, MonoType *t2)
 gboolean
 mono_metadata_type_equal_full (MonoType *t1, MonoType *t2, gboolean signature_only)
 {
-       return do_mono_metadata_type_equal (t1, t2, signature_only);
+       return do_mono_metadata_type_equal (t1, t2, signature_only ? MONO_TYPE_EQ_FLAGS_SIG_ONLY : 0);
 }
 
 enum {
        SIG_EQUIV_FLAG_NO_RET = 1,
+       SIG_EQUIV_FLAG_IGNORE_CMODS = 2,
 };
 
 gboolean
@@ -5899,6 +5933,11 @@ mono_metadata_signature_equal_no_ret (MonoMethodSignature *sig1, MonoMethodSigna
        return signature_equiv (sig1, sig2, SIG_EQUIV_FLAG_NO_RET);
 }
 
+gboolean
+mono_metadata_signature_equal_ignore_custom_modifier (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
+{
+       return signature_equiv (sig1, sig2, SIG_EQUIV_FLAG_IGNORE_CMODS);
+}
 
 gboolean
 signature_equiv (MonoMethodSignature *sig1, MonoMethodSignature *sig2, int equiv_flags)
@@ -5911,6 +5950,8 @@ signature_equiv (MonoMethodSignature *sig1, MonoMethodSignature *sig2, int equiv
        if (sig1->generic_param_count != sig2->generic_param_count)
                return FALSE;
 
+       int flag = MONO_TYPE_EQ_FLAGS_SIG_ONLY | (((equiv_flags & SIG_EQUIV_FLAG_IGNORE_CMODS) != 0) ? MONO_TYPE_EQ_FLAG_IGNORE_CMODS : 0);
+
        /*
         * We're just comparing the signatures of two methods here:
         *
@@ -5927,13 +5968,55 @@ signature_equiv (MonoMethodSignature *sig1, MonoMethodSignature *sig2, int equiv
                /* if (p1->attrs != p2->attrs)
                        return FALSE;
                */
-               if (!do_mono_metadata_type_equal (p1, p2, TRUE))
+               if (!do_mono_metadata_type_equal (p1, p2, flag))
                        return FALSE;
        }
 
        if ((equiv_flags & SIG_EQUIV_FLAG_NO_RET) != 0)
                return TRUE;
-       if (!do_mono_metadata_type_equal (sig1->ret, sig2->ret, TRUE))
+       if (!do_mono_metadata_type_equal (sig1->ret, sig2->ret, flag))
+               return FALSE;
+       return TRUE;
+}
+
+gboolean
+signature_equiv_vararg (MonoMethodSignature *sig1, MonoMethodSignature *sig2, int equiv_flags);
+
+gboolean
+mono_metadata_signature_equal_vararg (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
+{
+       return signature_equiv_vararg (sig1, sig2, 0);
+}
+
+gboolean
+mono_metadata_signature_equal_vararg_ignore_custom_modifier (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
+{
+       return signature_equiv_vararg (sig1, sig2, SIG_EQUIV_FLAG_IGNORE_CMODS);
+}
+
+gboolean
+signature_equiv_vararg (MonoMethodSignature *sig1, MonoMethodSignature *sig2, int equiv_flags)
+{
+       int i;
+
+       if (sig1->hasthis != sig2->hasthis ||
+           sig1->sentinelpos != sig2->sentinelpos)
+               return FALSE;
+       
+       int flag = MONO_TYPE_EQ_FLAGS_SIG_ONLY | (((equiv_flags & SIG_EQUIV_FLAG_IGNORE_CMODS) != 0) ? MONO_TYPE_EQ_FLAG_IGNORE_CMODS : 0);
+
+       for (i = 0; i < sig1->sentinelpos; i++) {
+               MonoType *p1 = sig1->params[i];
+               MonoType *p2 = sig2->params[i];
+
+               /*if (p1->attrs != p2->attrs)
+                       return FALSE;
+               */
+               if (!do_mono_metadata_type_equal (p1, p2, flag))
+                       return FALSE;
+       }
+
+       if (!do_mono_metadata_type_equal (sig1->ret, sig2->ret, flag))
                return FALSE;
        return TRUE;
 }
index 63bf819..41bfc8f 100644 (file)
@@ -611,7 +611,7 @@ mono_mb_emit_exception_for_error (MonoMethodBuilder *mb, MonoError *error)
        g_assert (mono_error_get_error_code (error) == MONO_ERROR_GENERIC && "Unsupported error code.");
        /* Have to copy the message because it will be referenced from JITed code while the MonoError may be freed. */
        char *msg = mono_mb_strdup (mb, mono_error_get_message (error));
-       mono_mb_emit_exception_full (mb, "System", mono_error_get_exception_name (error), msg);
+       mono_mb_emit_exception_full (mb, mono_error_get_exception_name_space (error), mono_error_get_exception_name (error), msg);
 }
 
 /**
index 070b05d..e7287a0 100644 (file)
@@ -4,7 +4,210 @@
 
 #include "config.h"
 #include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "mono/metadata/metadata.h"
+#include "mono/metadata/image.h"
+#include "mono/metadata/tokentype.h"
+#include "mono/metadata/metadata-internals.h"
+#include "mono/metadata/class-init.h"
+#include "mono/metadata/class-internals.h"
+#include "mono/utils/mono-error-internals.h"
 #include "mono/metadata/unsafe-accessor.h"
-#include "mono/utils/mono-compiler.h"
 
-MONO_EMPTY_SOURCE_FILE (unsafe_accessor)
+
+
+static MonoMethod *
+find_method_simple (MonoClass *klass, const char *name, const char *qname, const char *fqname,
+                     MonoMethodSignature *sig, MonoClass *from_class, gboolean ignore_cmods, MonoError *error)
+{
+       MonoMethod *method_maybe = NULL;
+
+       /* Search directly in the metadata to avoid calling setup_methods () */
+       error_init (error);
+
+       MonoImage *klass_image = m_class_get_image (klass);
+       /* FIXME: !mono_class_is_ginst (from_class) condition causes test failures. */
+       if (m_class_get_type_token (klass) && !image_is_dynamic (klass_image) && !m_class_get_methods (klass) && !m_class_get_rank (klass) && klass == from_class && !mono_class_is_ginst (from_class)) {
+               int first_idx = mono_class_get_first_method_idx (klass);
+               int mcount = mono_class_get_method_count (klass);
+               for (int i = 0; i < mcount; ++i) {
+                       guint32 cols [MONO_METHOD_SIZE];
+                       MonoMethod *method;
+                       const char *m_name;
+                       MonoMethodSignature *other_sig;
+
+                       mono_metadata_decode_table_row (klass_image, MONO_TABLE_METHOD, first_idx + i, cols, MONO_METHOD_SIZE);
+
+                       m_name = mono_metadata_string_heap (klass_image, cols [MONO_METHOD_NAME]);
+
+                       if (!((fqname && !strcmp (m_name, fqname)) ||
+                                 (qname && !strcmp (m_name, qname)) ||
+                                 (name && !strcmp (m_name, name))))
+                               continue;
+
+                       method = mono_get_method_checked (klass_image, MONO_TOKEN_METHOD_DEF | (first_idx + i + 1), klass, NULL, error);
+                       if (!is_ok (error)) //bail out if we hit a loader error
+                               return NULL;
+
+                       // Check method signature
+                       if (method) {
+                               other_sig = mono_method_signature_checked (method, error);
+                               if (!is_ok (error)) //bail out if we hit a loader error
+                                       return NULL;
+                               if (other_sig) {
+                                       gboolean found = ignore_cmods ? mono_metadata_signature_equal_ignore_custom_modifier (sig, other_sig) : mono_metadata_signature_equal (sig, other_sig);
+                                       if (found) {
+                                               if (method_maybe != NULL) {
+                                                       if (ignore_cmods) {
+                                                               MonoMethod *precise_match = find_method_simple (klass, name, qname, fqname, sig, from_class, FALSE, error);
+                                                               if (precise_match)
+                                                                       return precise_match;
+                                                       }
+                                                       mono_error_set_generic_error (error, "System.Reflection", "AmbiguousMatchException", "Ambiguity in binding of UnsafeAccessorAttribute.");
+                                                       return NULL;
+                                               }
+                                               method_maybe = method;
+                                       }
+                               }
+                       }
+               }
+               return method_maybe;
+       }
+
+       return NULL;
+}
+
+typedef struct MethodLookupResultInfo {
+       int i;
+       MonoMethod *m;
+       gboolean matched;
+} MethodLookupResultInfo;
+
+static MethodLookupResultInfo *
+find_method_slow (MonoClass *klass, const char *name, const char *qname, const char *fqname,
+                     MonoMethodSignature *sig, gboolean ignore_cmods, MonoError *error)
+{
+       gpointer iter = NULL;
+       MethodLookupResultInfo *result = (MethodLookupResultInfo *)g_malloc0 (sizeof (MethodLookupResultInfo));
+       int i = -1;
+       MonoMethod *m = NULL;
+       gboolean matched = FALSE;
+       result->i = i;
+       result->m = m;
+       result->matched = matched;
+
+       /* FIXME: metadata-update iterating using
+        * mono_class_get_methods will break if `m` is NULL.  Need to
+        * reconcile with the `if (!m)` "we must cope" comment below.
+        */
+       while ((m = mono_class_get_methods (klass, &iter))) {
+               i++;
+               MonoMethodSignature *msig;
+
+               /* We must cope with failing to load some of the types. */
+               if (!m)
+                       continue;
+
+               if (!((fqname && !strcmp (m->name, fqname)) ||
+                     (qname && !strcmp (m->name, qname)) ||
+                     (name && !strcmp (m->name, name))))
+                       continue;
+               msig = mono_method_signature_checked (m, error);
+               if (!is_ok (error)) //bail out if we hit a loader error
+                       return NULL;
+
+               if (!msig)
+                       continue;
+
+               gboolean found = FALSE;
+               if (ignore_cmods)
+                       found = sig->call_convention == MONO_CALL_VARARG ? mono_metadata_signature_equal_vararg_ignore_custom_modifier (sig, msig) : mono_metadata_signature_equal_ignore_custom_modifier (sig, msig);
+               else
+                       found = sig->call_convention == MONO_CALL_VARARG ? mono_metadata_signature_equal_vararg (sig, msig) : mono_metadata_signature_equal (sig, msig);
+               
+               if (found) {
+                       if (matched) {
+                               if (ignore_cmods) {
+                                       MethodLookupResultInfo *precise_match = find_method_slow (klass, name, qname, fqname, sig, FALSE, error);
+                                       if (precise_match->m)
+                                               return precise_match;
+                               }
+                               mono_error_set_generic_error (error, "System.Reflection", "AmbiguousMatchException", "Ambiguity in binding of UnsafeAccessorAttribute.");
+                               return NULL;
+                       }
+                       matched = TRUE;
+                       result->i = i;
+                       result->m = m;
+                       result->matched = matched;
+               }
+       }
+
+       return result;
+}
+
+static MonoMethod *
+find_method_in_class_unsafe_accessor (MonoClass *klass, const char *name, const char *qname, const char *fqname,
+                     MonoMethodSignature *sig, MonoClass *from_class, gboolean ignore_cmods, MonoError *error)
+{
+       MonoMethod *method = NULL;
+       if (sig->call_convention != MONO_CALL_VARARG)
+               method = find_method_simple (klass, name, qname, fqname, sig, from_class, ignore_cmods, error);
+       if (method)
+               return method;
+       if (!is_ok(error) && mono_error_get_error_code (error) == MONO_ERROR_GENERIC)
+               return NULL;
+
+       mono_class_setup_methods (klass); /* FIXME don't swallow the error here. */
+       /*
+       We can't fail lookup of methods otherwise the runtime will fail with MissingMethodException instead of TypeLoadException.
+       See mono/tests/generic-type-load-exception.2.il
+       FIXME we should better report this error to the caller
+        */
+       if (!m_class_get_methods (klass) || mono_class_has_failure (klass)) {
+               ERROR_DECL (cause_error);
+               mono_error_set_for_class_failure (cause_error, klass);
+               mono_error_set_type_load_class (error, klass, "Could not find method '%s' due to a type load error: %s", name, mono_error_get_message (cause_error));
+               mono_error_cleanup (cause_error);
+               return NULL;
+       }
+       
+       MethodLookupResultInfo *result = find_method_slow (klass, name, qname, fqname, sig, ignore_cmods, error);
+       if (!is_ok(error) && mono_error_get_error_code (error) == MONO_ERROR_GENERIC)
+               return NULL;
+
+       int mcount = mono_class_get_method_count (klass);
+
+       g_assert (result != NULL);
+       if (result->matched) {
+               if (result->i < mcount)
+                       return mono_class_get_method_by_index (from_class, result->i);
+               else if (result->m != NULL) {
+                       // FIXME: metadata-update: hack
+                       // it's from a metadata-update, probably
+                       MonoMethod * m = mono_class_inflate_generic_method_full_checked (
+                               result->m, from_class, mono_class_get_context (from_class), error);
+                       mono_error_assert_ok (error);
+                       g_assert (m != NULL);
+                       g_assert (m->klass == from_class);
+                       g_assert (m->is_inflated);
+                       return m;
+               }
+       }
+
+       g_free (result);
+       return NULL;
+}
+
+MonoMethod*
+mono_unsafe_accessor_find_ctor (MonoClass *in_class, MonoMethodSignature *sig, MonoClass *from_class, MonoError *error)
+{
+       return find_method_in_class_unsafe_accessor (in_class, ".ctor", /*qname*/NULL, /*fqname*/NULL, sig, from_class, TRUE, error);
+}
+
+MonoMethod*
+mono_unsafe_accessor_find_method (MonoClass *in_class, const char *name, MonoMethodSignature *sig, MonoClass *from_class, MonoError *error)
+{
+       // This doesn't work for constructors because find_method explicitly disallows ".ctor" and ".cctor"
+       return find_method_in_class_unsafe_accessor (in_class, name, /*qname*/NULL, /*fqname*/NULL, sig, from_class, TRUE, error);
+}
index 6ed41ef..3052ed1 100644 (file)
@@ -313,6 +313,9 @@ mono_error_set_from_boxed (MonoError *error, const MonoErrorBoxed *from);
 const char*
 mono_error_get_exception_name (MonoError *oerror);
 
+const char*
+mono_error_get_exception_name_space (MonoError *oerror);
+
 void
 mono_error_set_specific (MonoError *error, int error_code, const char *missing_method);
 
index 821fc33..ab81a67 100644 (file)
@@ -209,6 +209,17 @@ mono_error_get_exception_name (MonoError *oerror)
        return error->exception_name;
 }
 
+const char*
+mono_error_get_exception_name_space (MonoError *oerror)
+{
+       MonoErrorInternal *error = (MonoErrorInternal*)oerror;
+
+       if (error->error_code == MONO_ERROR_NONE)
+               return NULL;
+
+       return error->exception_name_space;
+}
+
 /*Return a pointer to the internal error message, might be NULL.
 Caller should not release it.*/
 const char*
index 28d88ca..af69c37 100644 (file)
@@ -43,6 +43,8 @@ static unsafe class UnsafeAccessorsTests
         private void _mvv() {}
 
         // The "init" is important to have here - custom modifier test.
+        // The signature of set_Prop is 
+        // instance void modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) set_Prop ( string 'value')
         private string Prop { get; init; }
 
         // Used to validate ambiguity is handled via custom modifiers.
@@ -300,7 +302,6 @@ static unsafe class UnsafeAccessorsTests
     }
 
     [Fact]
-    [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)]
     public static void Verify_IgnoreCustomModifier()
     {
         Console.WriteLine($"Running {nameof(Verify_IgnoreCustomModifier)}");
@@ -333,7 +334,6 @@ static unsafe class UnsafeAccessorsTests
     }
 
     [Fact]
-    [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)]
     public static void Verify_UnmanagedCallConvBitAreTreatedAsCustomModifiersAndIgnored()
     {
         Console.WriteLine($"Running {nameof(Verify_UnmanagedCallConvBitAreTreatedAsCustomModifiersAndIgnored)}");
@@ -355,7 +355,6 @@ static unsafe class UnsafeAccessorsTests
     }
 
     [Fact]
-    [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)]
     public static void Verify_ManagedUnmanagedFunctionPointersDontMatch()
     {
         Console.WriteLine($"Running {nameof(Verify_ManagedUnmanagedFunctionPointersDontMatch)}");
@@ -393,7 +392,6 @@ static unsafe class UnsafeAccessorsTests
     }
 
     [Fact]
-    [ActiveIssue("https://github.com/dotnet/runtime/issues/89212", TestRuntimes.Mono)]
     public static void Verify_InheritanceMethodResolution()
     {
         Console.WriteLine($"Running {nameof(Verify_InheritanceMethodResolution)}");
@@ -510,7 +508,6 @@ static unsafe class UnsafeAccessorsTests
     }
 
     [Fact]
-    [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)]
     public static void Verify_InvalidTargetUnsafeAccessorAmbiguousMatch()
     {
         Console.WriteLine($"Running {nameof(Verify_InvalidTargetUnsafeAccessorAmbiguousMatch)}");