Merge pull request #2310 from lambdageek/dev/bug-36305
authorAleksey Kliger (λgeek) <akliger@gmail.com>
Thu, 10 Dec 2015 16:13:43 +0000 (11:13 -0500)
committerAleksey Kliger (λgeek) <akliger@gmail.com>
Thu, 10 Dec 2015 16:13:43 +0000 (11:13 -0500)
[reflection] Fix MethodInfo.GetBaseDefinition for open constructed types (close #36305)

mcs/class/corlib/Test/System.Reflection/MethodInfoTest.cs
mono/metadata/icall.c

index 57e2fa6364e557041f8fd5e816e6fb80957620cc..71119b1673a7308b2bc7bf692771aad259bfdca3 100644 (file)
@@ -3,9 +3,11 @@
 //
 // Authors:
 //  Zoltan Varga (vargaz@gmail.com)
+//  Aleksey Kliger (aleksey@xamarin.com)
 //
 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2015 Xamarin, Inc. (http://www.xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -293,6 +295,35 @@ namespace MonoTests.System.Reflection
                        Assert.AreEqual (typeof (GBD_D), typeof (GBD_E).GetMethod ("f").GetBaseDefinition ().DeclaringType);
                }
 
+               class GenericBase<T,H> {
+                       public virtual void f2 () { }
+               }
+
+               class GenericMid<T, U> : GenericBase<T, Action<U>> {
+                       public virtual T f1 () { return default (T); }
+               }
+
+               class GenericChild<T> : GenericMid<T, int> {
+                       public override T f1 () { return default (T); }
+                       public override void f2 () { }
+               }
+
+               [Test]
+               public void GetBaseDefinition_OpenConstructedBaseType () // 36305
+               {
+                       var t = typeof (GenericChild<string>);
+
+                       var mi1 = t.GetMethod ("f1");
+                       var mi1_base = mi1.GetBaseDefinition ();
+
+                       Assert.AreEqual (typeof (GenericMid<string, int>), mi1_base.DeclaringType, "#1");
+
+                       var mi2 = t.GetMethod ("f2");
+                       var mi2_base = mi2.GetBaseDefinition ();
+
+                       Assert.AreEqual (typeof (GenericBase<string, Action<int>>), mi2_base.DeclaringType, "#2");
+               }
+
                class TestInheritedMethodA {
                        private void TestMethod ()
                        {
index c7d069f036d0db566ab3d3dd4fd5a7777a773fb8..eaecf5b2027da4795c132c2f3554dcd9f7fa986b 100644 (file)
@@ -6,10 +6,11 @@
  *   Paolo Molaro (lupus@ximian.com)
  *      Patrik Torstensson (patrik.torstensson@labs2.com)
  *   Marek Safar (marek.safar@gmail.com)
+ *   Aleksey Kliger (aleksey@xamarin.com)
  *
  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
- * Copyright 2011-2014 Xamarin Inc (http://www.xamarin.com).
+ * Copyright 2011-2015 Xamarin Inc (http://www.xamarin.com).
  */
 
 #include <config.h>
@@ -6619,6 +6620,7 @@ ICALL_EXPORT MonoReflectionMethod *
 ves_icall_MonoMethod_get_base_method (MonoReflectionMethod *m, gboolean definition)
 {
        MonoClass *klass, *parent;
+       MonoGenericContext *generic_inst = NULL;
        MonoMethod *method = m->method;
        MonoMethod *result = NULL;
        int slot;
@@ -6636,21 +6638,75 @@ ves_icall_MonoMethod_get_base_method (MonoReflectionMethod *m, gboolean definiti
                return m;
 
        klass = method->klass;
-       if (klass->generic_class)
+       if (klass->generic_class) {
+               generic_inst = mono_class_get_context (klass);
                klass = klass->generic_class->container_class;
+       }
 
        if (definition) {
                /* At the end of the loop, klass points to the eldest class that has this virtual function slot. */
                for (parent = klass->parent; parent != NULL; parent = parent->parent) {
+                       /* on entry, klass is either a plain old non-generic class and generic_inst == NULL
+                          or klass is the generic container class and generic_inst is the instantiation.
+
+                          when we go to the parent, if the parent is an open constructed type, we need to
+                          replace the type parameters by the definitions from the generic_inst, and then take it
+                          apart again into the klass and the generic_inst.
+
+                          For cases like this:
+                          class C<T> : B<T, int> {
+                              public override void Foo () { ... }
+                          }
+                          class B<U,V> : A<HashMap<U,V>> {
+                              public override void Foo () { ... }
+                          }
+                          class A<X> {
+                              public virtual void Foo () { ... }
+                          }
+
+                          if at each iteration the parent isn't open, we can skip inflating it.  if at some
+                          iteration the parent isn't generic (after possible inflation), we set generic_inst to
+                          NULL;
+                       */
+                       MonoGenericContext *parent_inst = NULL;
+                       if (mono_class_is_open_constructed_type (mono_class_get_type (parent))) {
+                               MonoError error;
+                               parent = mono_class_inflate_generic_class_checked (parent, generic_inst, &error);
+                               mono_error_raise_exception(&error);
+                       }
+                       if (parent->generic_class) {
+                               parent_inst = mono_class_get_context (parent);
+                               parent = parent->generic_class->container_class;
+                       }
+
                        mono_class_setup_vtable (parent);
                        if (parent->vtable_size <= slot)
                                break;
                        klass = parent;
+                       generic_inst = parent_inst;
                }
        } else {
                klass = klass->parent;
                if (!klass)
                        return m;
+               if (mono_class_is_open_constructed_type (mono_class_get_type (klass))) {
+                       MonoError error;
+                       klass = mono_class_inflate_generic_class_checked (klass, generic_inst, &error);
+                       mono_error_raise_exception(&error);
+
+                       generic_inst = NULL;
+               }
+               if (klass->generic_class) {
+                       generic_inst = mono_class_get_context (klass);
+                       klass = klass->generic_class->container_class;
+               }
+
+       }
+
+       if (generic_inst) {
+               MonoError error;
+               klass = mono_class_inflate_generic_class_checked (klass, generic_inst, &error);
+               mono_error_raise_exception(&error);
        }
 
        if (klass == method->klass)