[release/6.0] vtable setup fix for generic default interface methods in mono runtime...
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Thu, 10 Mar 2022 20:23:37 +0000 (12:23 -0800)
committerGitHub <noreply@github.com>
Thu, 10 Mar 2022 20:23:37 +0000 (12:23 -0800)
* vtable setup fix for generic default interface methods in mono runtime

When processing the overrides from interface default methods we should
check if the interface class is a generic type definition first and
inflate with the interface class context.

Test case included.

* Update test to ensure that the correct context is used

Co-authored-by: Bill Holmes <bill.holmes@unity3d.com>
Co-authored-by: Alexander Köplinger <alex.koeplinger@outlook.com>
src/mono/mono/metadata/class-setup-vtable.c
src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs [new file with mode: 0644]
src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.csproj [new file with mode: 0644]

index ae1727e..bbd6b6a 100644 (file)
@@ -1742,11 +1742,11 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
                for (int i = 0; i < iface_onum; i++) {
                        MonoMethod *decl = iface_overrides [i*2];
                        MonoMethod *override = iface_overrides [i*2 + 1];
-                       if (decl->is_inflated) {
+                       if (mono_class_is_gtd (override->klass)) {
+                               override = mono_class_inflate_generic_method_full_checked (override, ic, mono_class_get_context (ic), error);
+                       } else if (decl->is_inflated) {
                                override = mono_class_inflate_generic_method_checked (override, mono_method_get_context (decl), error);
                                mono_error_assert_ok (error);
-                       } else if (mono_class_is_gtd (override->klass)) {
-                               override = mono_class_inflate_generic_method_full_checked (override, ic, mono_class_get_context (ic), error);
                        }
                        if (!apply_override (klass, ic, vtable, decl, override, &override_map, &override_class_map, &conflict_map))
                                goto fail;
diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs
new file mode 100644 (file)
index 0000000..ae08369
--- /dev/null
@@ -0,0 +1,81 @@
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+// In GH issue 61244 the mono runtime aborted when inflating the default
+// interface method because the context used was from the base interface.
+
+// The OneArgBaseInterface portion of this test handles the original bug
+// where the base interface has less generic arguments than the derived 
+// interface and the runtime aborts.
+
+// The SecondInterface portion of this test handles an additional scenario
+// where the number of generic arguments are the same in the base and
+// derived interface contexts, but the order is changed (or different.)
+// When this occurs the generic info is incorrect for the inflated method.
+
+class Program
+{
+    static int Main(string[] args)
+    {
+        return new TestClass().DoTest();
+    }
+}
+
+public interface OneArgBaseInterface<T1>
+{
+    int SomeFunc1(T1 someParam1, Type someParam1Type);
+}
+
+public interface TwoArgBaseInterface<T1, T2>
+{
+    int SomeFunc1(T1 someParam1, T2 someParam2, Type someParam1Type, Type someParam2Type);
+}
+
+public interface SecondInterface<TParam2Type, TParam1Type> :  OneArgBaseInterface<TParam1Type>, TwoArgBaseInterface<TParam1Type, TParam2Type>
+{
+    int OneArgBaseInterface<TParam1Type>.SomeFunc1(TParam1Type someParam1, Type someParam1Type)
+    {
+        if (typeof(TParam1Type) != someParam1Type)
+        {
+            Console.WriteLine("Failed => 101");
+            return 101;
+        }
+
+        return 100;
+    }
+
+    int TwoArgBaseInterface<TParam1Type, TParam2Type>.SomeFunc1(TParam1Type someParam1, TParam2Type someParam2, Type someParam1Type, Type someParam2Type)
+    {
+        if (typeof(TParam1Type) != someParam1Type)
+        {
+            Console.WriteLine("Failed => 102");
+            return 102;
+        }
+
+        if (typeof(TParam2Type) != someParam2Type)
+        {
+            Console.WriteLine("Failed => 103");
+            return 103;
+        }
+
+        return 100;
+    }
+}
+
+public class TestClass : SecondInterface<int, string>
+{
+    public int DoTest ()
+    {
+        int ret = (this as OneArgBaseInterface<string>).SomeFunc1("test string", typeof(string));
+        if (ret != 100)
+            return ret;
+
+        ret = (this as TwoArgBaseInterface<string, int>).SomeFunc1("test string", 0, typeof(string), typeof(int));
+        if (ret != 100)
+            return ret;
+
+        Console.WriteLine("Passed => 100");
+        return 100;
+    }
+}
\ No newline at end of file
diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.csproj
new file mode 100644 (file)
index 0000000..42c7a9e
--- /dev/null
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <OutputType>Exe</OutputType>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>