Skip to content

Commit

Permalink
[dim][regression] Explicit interface override (#17583) (#17627)
Browse files Browse the repository at this point in the history
* Precedence order:
-> Override interface method in class
-> Method in class
-> DIM
  • Loading branch information
thaystg committed Oct 31, 2019
1 parent a119807 commit 6ac1ff7
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 43 deletions.
83 changes: 40 additions & 43 deletions mono/metadata/class-init.c
Expand Up @@ -3078,7 +3078,6 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
// Loop on all interface methods...
int mcount = mono_class_get_method_count (ic);
for (im_index = 0; im_index < mcount; im_index++) {
gboolean foundOverrideInClassOrParent = FALSE;
MonoMethod *im = ic->methods [im_index];
int im_slot = ic_offset + im->slot;
MonoMethod *override_im = (override_map != NULL) ? (MonoMethod *)g_hash_table_lookup (override_map, im) : NULL;
Expand All @@ -3088,41 +3087,17 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o

TRACE_INTERFACE_VTABLE (printf ("\tchecking iface method %s\n", mono_method_full_name (im,1)));

int cm_index;
MonoMethod *cm;

// First look for a suitable method among the class methods
for (l = virt_methods; l; l = l->next) {
cm = (MonoMethod *)l->data;
TRACE_INTERFACE_VTABLE (printf (" For slot %d ('%s'.'%s':'%s'), trying method '%s'.'%s':'%s'... [EXPLICIT IMPLEMENTATION = %d][SLOT IS NULL = %d]", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL)));
if (check_interface_method_override (klass, im, cm, TRUE, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL))) {
TRACE_INTERFACE_VTABLE (printf ("[check ok]: ASSIGNING"));
vtable [im_slot] = cm;
foundOverrideInClassOrParent = TRUE;
/* Why do we need this? */
if (cm->slot < 0) {
cm->slot = im_slot;
}
if (conflict_map)
g_hash_table_remove(conflict_map, im);
}
TRACE_INTERFACE_VTABLE (printf ("\n"));
if (mono_class_has_failure (klass)) /*Might be set by check_interface_method_override*/
goto fail;
}

// If the slot is still empty, look in all the inherited virtual methods...
if ((vtable [im_slot] == NULL) && klass->parent != NULL) {
MonoClass *parent = klass->parent;
// Reverse order, so that last added methods are preferred
for (cm_index = parent->vtable_size - 1; cm_index >= 0; cm_index--) {
cm = parent->vtable [cm_index];

TRACE_INTERFACE_VTABLE ((cm != NULL) && printf (" For slot %d ('%s'.'%s':'%s'), trying (ancestor) method '%s'.'%s':'%s'... ", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name));
if ((cm != NULL) && check_interface_method_override (klass, im, cm, FALSE, FALSE, TRUE)) {
TRACE_INTERFACE_VTABLE (printf ("[everything ok]: ASSIGNING"));
if (override_im == NULL || (override_im && MONO_CLASS_IS_INTERFACE_INTERNAL(override_im->klass))) {
int cm_index;
MonoMethod *cm;

// First look for a suitable method among the class methods
for (l = virt_methods; l; l = l->next) {
cm = (MonoMethod *)l->data;
TRACE_INTERFACE_VTABLE (printf (" For slot %d ('%s'.'%s':'%s'), trying method '%s'.'%s':'%s'... [EXPLICIT IMPLEMENTATION = %d][SLOT IS NULL = %d]", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL)));
if (check_interface_method_override (klass, im, cm, TRUE, interface_is_explicitly_implemented_by_class, (vtable [im_slot] == NULL))) {
TRACE_INTERFACE_VTABLE (printf ("[check ok]: ASSIGNING"));
vtable [im_slot] = cm;
foundOverrideInClassOrParent = TRUE;
/* Why do we need this? */
if (cm->slot < 0) {
cm->slot = im_slot;
Expand All @@ -3135,17 +3110,39 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
if (mono_class_has_failure (klass)) /*Might be set by check_interface_method_override*/
goto fail;
}
}

// If the slot is still empty, look in all the inherited virtual methods...
if ((vtable [im_slot] == NULL) && klass->parent != NULL) {
MonoClass *parent = klass->parent;
// Reverse order, so that last added methods are preferred
for (cm_index = parent->vtable_size - 1; cm_index >= 0; cm_index--) {
MonoMethod *cm = parent->vtable [cm_index];

TRACE_INTERFACE_VTABLE ((cm != NULL) && printf (" For slot %d ('%s'.'%s':'%s'), trying (ancestor) method '%s'.'%s':'%s'... ", im_slot, ic->name_space, ic->name, im->name, cm->klass->name_space, cm->klass->name, cm->name));
if ((cm != NULL) && check_interface_method_override (klass, im, cm, FALSE, FALSE, TRUE)) {
TRACE_INTERFACE_VTABLE (printf ("[everything ok]: ASSIGNING"));
vtable [im_slot] = cm;
/* Why do we need this? */
if (cm->slot < 0) {
cm->slot = im_slot;
}
break;
}
if (mono_class_has_failure (klass)) /*Might be set by check_interface_method_override*/
goto fail;
TRACE_INTERFACE_VTABLE ((cm != NULL) && printf ("\n"));
}
}

if (vtable [im_slot] == NULL) {
if (!(im->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
TRACE_INTERFACE_VTABLE (printf (" Using default iface method %s.\n", mono_method_full_name (im, 1)));
vtable [im_slot] = im;
if (vtable [im_slot] == NULL) {
if (!(im->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
TRACE_INTERFACE_VTABLE (printf (" Using default iface method %s.\n", mono_method_full_name (im, 1)));
vtable [im_slot] = im;
}
}
}

if (override_im != NULL && !foundOverrideInClassOrParent)
} else {
g_assert (vtable [im_slot] == override_im);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions mono/tests/Makefile.am
Expand Up @@ -380,6 +380,7 @@ TESTS_CS_SRC= \
params.cs \
reflection.cs \
interface.cs \
interface-2.cs \
iface.cs \
iface2.cs \
iface3.cs \
Expand Down
97 changes: 97 additions & 0 deletions mono/tests/interface-2.cs
@@ -0,0 +1,97 @@
using System;

public interface I1
{
int M1()
{
return 100;
}
}

public interface I2 : I1 { int I1.M1() { return 200; } }

public interface I3 : I1 { int I1.M1() { return 300; } }

public interface I4 : I1 { int I1.M1() { return 400; } }

class Test10 : I1, I2, I3, I4 {
//void I1.M1() { System.Console.WriteLine("I1.I1.M1"); }
public int M1() { return 0; }
}

public interface I1t
{
int M1t()
{
return 100;
}
}

public interface I2t : I1t { int I1t.M1t() { return 200; } }

public interface I3t : I1t { int I1t.M1t() { return 300; } }

public interface I4t : I1t { int I1t.M1t() { return 400; } }

class Test10t : I1t, I2t, I3t, I4t
{
int I1t.M1t() { return 0; }
public int M1t() { return 10; }
}


public interface IName
{
string Name { get; }
}
public interface IOther<T>
{
T other { get; }
string Name { get; }
}
public class Name1 : IName
{
public string Name { get { return "ClassName"; } }
string IName.Name { get { return "InterfaceName"; } }
}
public class Name2 : IName, IOther<int>
{
public string Name { get { return "ClassName"; } }
string IName.Name { get { return "InterfaceName"; } }
public int other { get { return 43; } }
}

public class Test
{
public static int test_0_override()
{
var name1 = new Name1();
var name2 = new Name2();
IName iName1 = name1;
IName iName2 = name2;
if (!iName1.Name.Equals("InterfaceName"))
return 10;
if (!iName2.Name.Equals("InterfaceName"))
return 20;
if (!name1.Name.Equals("ClassName"))
return 30;
if (!name2.Name.Equals("ClassName"))
return 40;
return 0;
}

public static int test_0_dim_override()
{
I1 var = new Test10();
if (var.M1() != 0)
return var.M1();

I1t var2 = new Test10t();
return var2.M1t();
}

public static int Main (string[] args) {
return TestDriver.RunTests (typeof (Test), args);
}

}

0 comments on commit 6ac1ff7

Please sign in to comment.