Skip to content

Commit

Permalink
[class] DIMs in the vtable of abstract classes may be overridden (#82556
Browse files Browse the repository at this point in the history
)

* [class]  treat slot as empty if it's filled in with a DIM

   Classes may override an interface method slot if it was filled in with a DIM, just as if it was empty.

Related to #81882
  • Loading branch information
lambdageek authored Feb 28, 2023
1 parent 366e77d commit 4862343
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 2 deletions.
3 changes: 2 additions & 1 deletion src/mono/mono/metadata/class-setup-vtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -1877,7 +1877,8 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
flags |= MONO_ITF_OVERRIDE_EXPLICITLY_IMPLEMENTED;
if (interface_is_explicitly_implemented_by_class && variant_itf)
flags |= MONO_ITF_OVERRIDE_VARIANT_ITF;
if (vtable [im_slot] == NULL)
// if the slot is emtpy, or it's filled with a DIM, treat it as empty
if (vtable [im_slot] == NULL || m_class_is_interface (vtable [im_slot]->klass))
flags |= MONO_ITF_OVERRIDE_SLOT_EMPTY;
if (check_interface_method_override (klass, im, cm, flags)) {
TRACE_INTERFACE_VTABLE (printf ("[check ok]: ASSIGNING\n"));
Expand Down
2 changes: 2 additions & 0 deletions src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -2686,6 +2686,8 @@ set_interface_map_data_method_object (MonoMethod *method, MonoClass *iclass, int

MonoMethod* foundMethod = m_class_get_vtable (klass) [i + ioffset];

g_assert (foundMethod);

if (mono_class_has_dim_conflicts (klass) && mono_class_is_interface (foundMethod->klass)) {
GSList* conflicts = mono_class_get_dim_conflicts (klass);
GSList* l;
Expand Down
39 changes: 38 additions & 1 deletion src/mono/sample/HelloWorld/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,37 @@

using System;

interface IDefault
{
public void Method1()
{
Console.WriteLine("Interface Method1");
}

public object Method2()
{
Console.WriteLine("Interface Method2");
return null;
}
}

abstract class ClassA : IDefault
{
virtual public void Method1()
{
Console.WriteLine("ClassA Method 1");
}
}

class ClassB : ClassA
{
public virtual object Method2()
{
Console.WriteLine("ClassB Method2");
return null;
}
}

namespace HelloWorld
{
internal class Program
Expand All @@ -14,6 +45,12 @@ private static void Main(string[] args)
Console.WriteLine(typeof(object).Assembly.FullName);
Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly ());
Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);

IDefault c = new ClassB();

c.Method1();
c.Method2();

}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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.Linq;

using Xunit;

namespace LeaveAbstractMethodsNulInVTable
{
interface IDefault
{
public string Method1() => "Interface Method1";

public string Method2() => "Interface Method2";
}

abstract class ClassA : IDefault
{
virtual public string Method1() => "ClassA Method1";
}

class ClassB : ClassA
{
virtual public string Method2() => "ClassB Method2";
}

public class Program
{
public static int Main()
{
IDefault c = new ClassB();

string s1 = c.Method1();
Assert.Equal("ClassA Method1", s1);

string s2 = c.Method2();
Assert.Equal("ClassB Method2", s2);

return 100;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>

0 comments on commit 4862343

Please sign in to comment.