[reflection] mono_method_get_base_method on a GTD should use canonical ginst (mono...
authorAleksey Kliger (λgeek) <alklig@microsoft.com>
Tue, 22 Oct 2019 18:07:44 +0000 (14:07 -0400)
committerGitHub <noreply@github.com>
Tue, 22 Oct 2019 18:07:44 +0000 (14:07 -0400)
* [test] GetCustomAttributes on generic type definition

* [reflection] mono_method_get_base_method on a GTD should use canonical ginst

The issue is in how we pass along the generic instantiation.  So there are two
parts here: we start with a method on a GTD which has to decompose into the GTD
and it's instantiation with its own generic parameters, and then we go up to
its parent and grandparent inflating and decomposing the resulting
instantiation. The instantiations keep passing the last parameter as the number
of parameters shrinks so that the gparam index from the original type is now
out of bounds for the parent and grandparent.  If we mess up, we'll get a
BadImageFormatException from the runtime

Fixes https://github.com/mono/mono/issues/17278

Commit migrated from https://github.com/mono/mono/commit/fb5b2c949ddd2ab09fcddb458584a5facd9ec94e

src/mono/mono/metadata/class.c

index c4a2af8..dcab927 100644 (file)
@@ -6253,7 +6253,17 @@ mono_method_get_base_method (MonoMethod *method, gboolean definition, MonoError
                return method;
 
        klass = method->klass;
-       if (mono_class_is_ginst (klass)) {
+       if (mono_class_is_gtd (klass)) {
+               /* If we get a GTD like Foo`2 replace look instead at its instantiation with its own generic params: Foo`2<!0, !1>. */
+               /* In particular we want generic_inst to be initialized to <!0,
+                * !1> so that we can inflate parent classes correctly as we go
+                * up the class hierarchy. */
+               MonoType *ty = mono_class_gtd_get_canonical_inst (klass);
+               g_assert (ty->type == MONO_TYPE_GENERICINST);
+               MonoGenericClass *gklass = ty->data.generic_class;
+               generic_inst = mono_generic_class_get_context (gklass);
+               klass = gklass->container_class;
+       } else if (mono_class_is_ginst (klass)) {
                generic_inst = mono_class_get_context (klass);
                klass = mono_class_get_generic_class (klass)->container_class;
        }
@@ -6301,6 +6311,10 @@ retry:
                        generic_inst = parent_inst;
                }
        } else {
+               /* When we get here, possibly after a retry, if generic_inst is
+                * set, then the class is must be a gtd */
+               g_assert (generic_inst == NULL || mono_class_is_gtd (klass));
+
                klass = m_class_get_parent (klass);
                if (!klass)
                        return method;
@@ -6320,6 +6334,7 @@ retry:
        if (generic_inst) {
                klass = mono_class_inflate_generic_class_checked (klass, generic_inst, error);
                return_val_if_nok (error, NULL);
+               generic_inst = NULL;
        }
 
        if (klass == method->klass)