Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 87997db

Browse files
[release/7.0-staging] [mono] [imt] Don't increment vt_slot for non-virtual generic methods (#94468)
Backport of #94437 to release/7.0-staging Fixes #93770 * [mono] [imt] Don't increment vt_slot for non-virtual generic methods Interfaces can have static generic methods, for example. They don't have a vt_slot. When building an IMT slot, we need to collect all the interface methods that map to a particular IMT slot along with their implementing vtable entries. To do that, vt_slot starts at the interface offset of a particular interface and keeps incrementing as we iterate over the methods of the interface. It is crtitical that vt_slot is accurate - otherwise we may dispatch the interface call to the wrong virtual method. * [mono][imt] remove dead appdomain code the extra_interfaces argument was used to implement additional interfaces on cross-domain transparent proxy objects. * [mono][imt] fixup ifdefed debug code * Add test case --------- Co-authored-by: Aleksey Kliger <[email protected]>
1 parent 1d62e17 commit 87997db

File tree

3 files changed

+143
-24
lines changed

3 files changed

+143
-24
lines changed

src/mono/mono/metadata/object.c

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,8 +1373,8 @@ print_imt_entry (const char* message, MonoImtBuilderEntry *e, int num) {
13731373
message,
13741374
num,
13751375
method,
1376-
method->klass->name_space,
1377-
method->klass->name,
1376+
m_class_get_name_space (method->klass),
1377+
m_class_get_name (method->klass),
13781378
method->name);
13791379
} else {
13801380
printf (" * %s: NULL\n", message);
@@ -1486,12 +1486,11 @@ get_generic_virtual_entries (MonoMemoryManager *mem_manager, gpointer *vtable_sl
14861486
*
14871487
*/
14881488
static void
1489-
build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_interfaces, int slot_num)
1489+
build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, int slot_num)
14901490
{
14911491
MONO_REQ_GC_NEUTRAL_MODE;
14921492

14931493
int i;
1494-
GSList *list_item;
14951494
guint32 imt_collisions_bitmap = 0;
14961495
MonoImtBuilderEntry **imt_builder = (MonoImtBuilderEntry **)g_calloc (MONO_IMT_SIZE, sizeof (MonoImtBuilderEntry*));
14971496
int method_count = 0;
@@ -1539,8 +1538,10 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_
15391538
}
15401539
method = mono_class_get_method_by_index (iface, method_slot_in_interface);
15411540
if (method->is_generic) {
1542-
has_generic_virtual = TRUE;
1543-
vt_slot ++;
1541+
if (m_method_is_virtual (method)) {
1542+
has_generic_virtual = TRUE;
1543+
vt_slot ++;
1544+
}
15441545
continue;
15451546
}
15461547

@@ -1556,23 +1557,6 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_
15561557
}
15571558
}
15581559
}
1559-
if (extra_interfaces) {
1560-
int interface_offset = m_class_get_vtable_size (klass);
1561-
1562-
for (list_item = extra_interfaces; list_item != NULL; list_item=list_item->next) {
1563-
MonoClass* iface = (MonoClass *)list_item->data;
1564-
int method_slot_in_interface;
1565-
int mcount = mono_class_get_method_count (iface);
1566-
for (method_slot_in_interface = 0; method_slot_in_interface < mcount; method_slot_in_interface++) {
1567-
MonoMethod *method = mono_class_get_method_by_index (iface, method_slot_in_interface);
1568-
1569-
if (method->is_generic)
1570-
has_generic_virtual = TRUE;
1571-
add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, interface_offset + method_slot_in_interface, slot_num);
1572-
}
1573-
interface_offset += mcount;
1574-
}
1575-
}
15761560
for (i = 0; i < MONO_IMT_SIZE; ++i) {
15771561
/* overwrite the imt slot only if we're building all the entries or if
15781562
* we're building this specific one
@@ -1672,7 +1656,7 @@ mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot)
16721656
mono_loader_lock ();
16731657
/* we change the slot only if it wasn't changed from the generic imt trampoline already */
16741658
if (!callbacks.imt_entry_inited (vtable, imt_slot))
1675-
build_imt_slots (vtable->klass, vtable, imt, NULL, imt_slot);
1659+
build_imt_slots (vtable->klass, vtable, imt, imt_slot);
16761660
mono_loader_unlock ();
16771661
}
16781662

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Runtime.CompilerServices;
6+
using Xunit;
7+
8+
namespace ReproGH93770;
9+
10+
// By default in Mono every class that implements interfaces has 19
11+
// interface method table slots that are used to dispatch interface
12+
// calls. The methods are assigned to slots based on a hash of some
13+
// metadata. This test tries to create at least one IMT slot that has
14+
// a collision by creating an interface with 20 virtual methods.
15+
//
16+
// The bug is that if the method also has a non-virtual generic
17+
// method, the IMT slot with the collision calls the wrong vtable
18+
// element.
19+
20+
public class ReproGH93770
21+
{
22+
[Fact]
23+
public static int TestEntryPoint()
24+
{
25+
C c = new C();
26+
I1 i1 = c;
27+
Helper(i1);
28+
return 100;
29+
}
30+
[MethodImpl(MethodImplOptions.NoInlining)]
31+
private static void Helper(I1 i1)
32+
{
33+
var e = 0;
34+
var n = i1.M0();
35+
Assert.Equal(e, n);
36+
e++;
37+
n = i1.M1();
38+
Assert.Equal(e, n);
39+
e++;
40+
n = i1.M2();
41+
Assert.Equal(e, n);
42+
e++;
43+
n = i1.M3();
44+
Assert.Equal(e, n);
45+
e++;
46+
n = i1.M4();
47+
Assert.Equal(e, n);
48+
e++;
49+
n = i1.M5();
50+
Assert.Equal(e, n);
51+
e++;
52+
n = i1.M6();
53+
Assert.Equal(e, n);
54+
e++;
55+
n = i1.M7();
56+
Assert.Equal(e, n);
57+
e++;
58+
n = i1.M8();
59+
Assert.Equal(e, n);
60+
e++;
61+
n = i1.M9();
62+
Assert.Equal(e, n);
63+
e++;
64+
n = i1.M10();
65+
Assert.Equal(e, n);
66+
e++;
67+
n = i1.M11();
68+
Assert.Equal(e, n);
69+
e++;
70+
n = i1.M12();
71+
Assert.Equal(e, n);
72+
e++;
73+
n = i1.M13();
74+
Assert.Equal(e, n);
75+
e++;
76+
n = i1.M14();
77+
Assert.Equal(e, n);
78+
e++;
79+
n = i1.M15();
80+
Assert.Equal(e, n);
81+
e++;
82+
n = i1.M16();
83+
Assert.Equal(e, n);
84+
e++;
85+
n = i1.M17();
86+
Assert.Equal(e, n);
87+
e++;
88+
n = i1.M18();
89+
Assert.Equal(e, n);
90+
e++;
91+
n = i1.M19();
92+
Assert.Equal(e, n);
93+
e++;
94+
}
95+
}
96+
97+
public interface I1 {
98+
public static T Id<T> (T t)=> t;
99+
100+
public int M0() => 0;
101+
public int M1() => 1;
102+
public int M2() => 2;
103+
public int M3() => 3;
104+
public int M4() => 4;
105+
106+
public int M5() => 5;
107+
public int M6() => 6;
108+
public int M7() => 7;
109+
public int M8() => 8;
110+
public int M9() => 9;
111+
public int M10() => 10;
112+
public int M11() => 11;
113+
public int M12() => 12;
114+
public int M13() => 13;
115+
public int M14() => 14;
116+
public int M15() => 15;
117+
public int M16() => 16;
118+
public int M17() => 17;
119+
public int M18() => 18;
120+
public int M19() => 19;
121+
122+
}
123+
124+
public class C : I1 {
125+
126+
}
127+
128+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
</PropertyGroup>
4+
<ItemGroup>
5+
<Compile Include="GitHub_93770.cs" />
6+
</ItemGroup>
7+
</Project>

0 commit comments

Comments
 (0)