diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 9cbe2ee44a8350..69bf896c1944d2 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -1430,8 +1430,8 @@ print_imt_entry (const char* message, MonoImtBuilderEntry *e, int num) { message, num, method, - method->klass->name_space, - method->klass->name, + m_class_get_name_space (method->klass), + m_class_get_name (method->klass), method->name); } else { printf (" * %s: NULL\n", message); @@ -1543,12 +1543,11 @@ get_generic_virtual_entries (MonoMemoryManager *mem_manager, gpointer *vtable_sl * */ static void -build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_interfaces, int slot_num) +build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, int slot_num) { MONO_REQ_GC_NEUTRAL_MODE; int i; - GSList *list_item; guint32 imt_collisions_bitmap = 0; MonoImtBuilderEntry **imt_builder = (MonoImtBuilderEntry **)g_calloc (MONO_IMT_SIZE, sizeof (MonoImtBuilderEntry*)); int method_count = 0; @@ -1596,8 +1595,10 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_ } method = mono_class_get_method_by_index (iface, method_slot_in_interface); if (method->is_generic) { - has_generic_virtual = TRUE; - vt_slot ++; + if (m_method_is_virtual (method)) { + has_generic_virtual = TRUE; + vt_slot ++; + } continue; } @@ -1613,23 +1614,6 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_ } } } - if (extra_interfaces) { - int interface_offset = m_class_get_vtable_size (klass); - - for (list_item = extra_interfaces; list_item != NULL; list_item=list_item->next) { - MonoClass* iface = (MonoClass *)list_item->data; - int method_slot_in_interface; - int mcount = mono_class_get_method_count (iface); - for (method_slot_in_interface = 0; method_slot_in_interface < mcount; method_slot_in_interface++) { - MonoMethod *method = mono_class_get_method_by_index (iface, method_slot_in_interface); - - if (method->is_generic) - has_generic_virtual = TRUE; - add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, interface_offset + method_slot_in_interface, slot_num); - } - interface_offset += mcount; - } - } for (i = 0; i < MONO_IMT_SIZE; ++i) { /* overwrite the imt slot only if we're building all the entries or if * we're building this specific one @@ -1729,7 +1713,7 @@ mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot) mono_loader_lock (); /* we change the slot only if it wasn't changed from the generic imt trampoline already */ if (!callbacks.imt_entry_inited (vtable, imt_slot)) - build_imt_slots (vtable->klass, vtable, imt, NULL, imt_slot); + build_imt_slots (vtable->klass, vtable, imt, imt_slot); mono_loader_unlock (); } diff --git a/src/tests/Loader/classloader/regressions/GitHub_93770/GitHub_93770.cs b/src/tests/Loader/classloader/regressions/GitHub_93770/GitHub_93770.cs new file mode 100644 index 00000000000000..7784ed40a0c73d --- /dev/null +++ b/src/tests/Loader/classloader/regressions/GitHub_93770/GitHub_93770.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Xunit; + +namespace ReproGH93770; + +// By default in Mono every class that implements interfaces has 19 +// interface method table slots that are used to dispatch interface +// calls. The methods are assigned to slots based on a hash of some +// metadata. This test tries to create at least one IMT slot that has +// a collision by creating an interface with 20 virtual methods. +// +// The bug is that if the method also has a non-virtual generic +// method, the IMT slot with the collision calls the wrong vtable +// element. + +public class ReproGH93770 +{ + [Fact] + public static int TestEntryPoint() + { + C c = new C(); + I1 i1 = c; + Helper(i1); + return 100; + } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Helper(I1 i1) + { + var e = 0; + var n = i1.M0(); + Assert.Equal(e, n); + e++; + n = i1.M1(); + Assert.Equal(e, n); + e++; + n = i1.M2(); + Assert.Equal(e, n); + e++; + n = i1.M3(); + Assert.Equal(e, n); + e++; + n = i1.M4(); + Assert.Equal(e, n); + e++; + n = i1.M5(); + Assert.Equal(e, n); + e++; + n = i1.M6(); + Assert.Equal(e, n); + e++; + n = i1.M7(); + Assert.Equal(e, n); + e++; + n = i1.M8(); + Assert.Equal(e, n); + e++; + n = i1.M9(); + Assert.Equal(e, n); + e++; + n = i1.M10(); + Assert.Equal(e, n); + e++; + n = i1.M11(); + Assert.Equal(e, n); + e++; + n = i1.M12(); + Assert.Equal(e, n); + e++; + n = i1.M13(); + Assert.Equal(e, n); + e++; + n = i1.M14(); + Assert.Equal(e, n); + e++; + n = i1.M15(); + Assert.Equal(e, n); + e++; + n = i1.M16(); + Assert.Equal(e, n); + e++; + n = i1.M17(); + Assert.Equal(e, n); + e++; + n = i1.M18(); + Assert.Equal(e, n); + e++; + n = i1.M19(); + Assert.Equal(e, n); + e++; + } +} + +public interface I1 { + public static T Id (T t)=> t; + + public int M0() => 0; + public int M1() => 1; + public int M2() => 2; + public int M3() => 3; + public int M4() => 4; + + public int M5() => 5; + public int M6() => 6; + public int M7() => 7; + public int M8() => 8; + public int M9() => 9; + public int M10() => 10; + public int M11() => 11; + public int M12() => 12; + public int M13() => 13; + public int M14() => 14; + public int M15() => 15; + public int M16() => 16; + public int M17() => 17; + public int M18() => 18; + public int M19() => 19; + +} + +public class C : I1 { + +} + + diff --git a/src/tests/Loader/classloader/regressions/GitHub_93770/GitHub_93770.csproj b/src/tests/Loader/classloader/regressions/GitHub_93770/GitHub_93770.csproj new file mode 100644 index 00000000000000..72aaed3e55208c --- /dev/null +++ b/src/tests/Loader/classloader/regressions/GitHub_93770/GitHub_93770.csproj @@ -0,0 +1,7 @@ + + + + + + +