Skip to content

Commit

Permalink
Implementing reabstraction of default interface methods. (#14790)
Browse files Browse the repository at this point in the history
Implementing reabstraction of default interface methods.
Fix #14495
  • Loading branch information
thaystg authored Jun 14, 2019
1 parent 49dc99b commit a81cc67
Show file tree
Hide file tree
Showing 6 changed files with 391 additions and 3 deletions.
30 changes: 28 additions & 2 deletions mono/metadata/class-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,14 @@ print_implemented_interfaces (MonoClass *klass)
}
}

static gboolean
method_is_reabstracted (guint16 flags)
{
if ((flags & METHOD_ATTRIBUTE_ABSTRACT && flags & METHOD_ATTRIBUTE_FINAL))
return TRUE;
return FALSE;
}

/*
* Return the number of virtual methods.
* Even for interfaces we can't simply return the number of methods as all CLR types are allowed to have static methods.
Expand All @@ -1554,17 +1562,23 @@ count_virtual_methods (MonoClass *klass)
mcount = mono_class_get_method_count (klass);
for (i = 0; i < mcount; ++i) {
flags = klass->methods [i]->flags;
if (flags & METHOD_ATTRIBUTE_VIRTUAL)
if ((flags & METHOD_ATTRIBUTE_VIRTUAL)) {
if (method_is_reabstracted (flags))
continue;
++vcount;
}
}
} else {
int first_idx = mono_class_get_first_method_idx (klass);
mcount = mono_class_get_method_count (klass);
for (i = 0; i < mcount; ++i) {
flags = mono_metadata_decode_table_row_col (klass->image, MONO_TABLE_METHOD, first_idx + i, MONO_METHOD_FLAGS);

if (flags & METHOD_ATTRIBUTE_VIRTUAL)
if ((flags & METHOD_ATTRIBUTE_VIRTUAL)) {
if (method_is_reabstracted (flags))
continue;
++vcount;
}
}
}
return vcount;
Expand Down Expand Up @@ -3161,6 +3175,7 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
// it can happen (for injected generic array interfaces) that the same slot is
// processed multiple times (those interfaces have overlapping slots), and it
// will not always be the first pass the one that fills the slot.
// Now it is okay to implement a class that is not abstract and implements a interface that has an abstract method because it's reabstracted
if (!mono_class_is_abstract (klass)) {
for (i = 0; i < klass->interface_offsets_count; i++) {
int ic_offset;
Expand All @@ -3176,6 +3191,8 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o

if (im->flags & METHOD_ATTRIBUTE_STATIC)
continue;
if (im->is_reabstracted == 1)
continue;

TRACE_INTERFACE_VTABLE (printf (" [class is not abstract, checking slot %d for interface '%s'.'%s', method %s, slot check is %d]\n",
im_slot, ic->name_space, ic->name, im->name, (vtable [im_slot] == NULL)));
Expand Down Expand Up @@ -3313,9 +3330,12 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
g_assert (cur_slot <= max_vtsize);

/* Ensure that all vtable slots are filled with concrete instance methods */
// Now it is okay to implement a class that is not abstract and implements a interface that has an abstract method because it's reabstracted
if (!mono_class_is_abstract (klass)) {
for (i = 0; i < cur_slot; ++i) {
if (vtable [i] == NULL || (vtable [i]->flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_STATIC))) {
if (vtable [i]->is_reabstracted == 1)

This comment has been minimized.

Copy link
@marek-safar

marek-safar Sep 7, 2019

Member

This is wrong as vtable [i] == NULL could be null here

continue;
char *type_name = mono_type_get_full_name (klass);
char *method_name = vtable [i] ? mono_method_full_name (vtable [i], TRUE) : g_strdup ("none");
mono_class_set_type_load_failure (klass, "Type %s has invalid vtable method slot %d with method %s", type_name, i, method_name);
Expand Down Expand Up @@ -4994,7 +5014,13 @@ mono_class_setup_methods (MonoClass *klass)
/*Only assign slots to virtual methods as interfaces are allowed to have static methods.*/
for (i = 0; i < count; ++i) {
if (methods [i]->flags & METHOD_ATTRIBUTE_VIRTUAL)
{
if (method_is_reabstracted (methods[i]->flags)) {
methods [i]->is_reabstracted = 1;
continue;
}
methods [i]->slot = slot++;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions mono/metadata/class-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ struct _MonoMethod {
unsigned int is_inflated:1; /* whether we're a MonoMethodInflated */
unsigned int skip_visibility:1; /* whenever to skip JIT visibility checks */
unsigned int verification_success:1; /* whether this method has been verified successfully.*/
unsigned int is_reabstracted:1; /* whenever this is a reabstraction of another interface */
signed int slot : 16;

/*
Expand Down
5 changes: 4 additions & 1 deletion mono/metadata/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -2906,7 +2906,10 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error)
// FIXME: for internal callers maybe it makes sense to do this check at the call site, not
// here?
if (mono_method_has_no_body (method)) {
mono_error_set_bad_image (error, img, "Method has no body");
if (method->is_reabstracted == 1)
mono_error_set_generic_error (error, "System", "EntryPointNotFoundException", "%s", method->name);
else
mono_error_set_bad_image (error, img, "Method has no body");
return NULL;
}

Expand Down
2 changes: 2 additions & 0 deletions mono/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,8 @@ TESTS_IL_SRC= \
dim-sealed.il \
dim-protected-accessibility1.il \
dim-protected-accessibility2.il \
dim-reabstraction.il \
dim-reabstraction-generics.il \
twopassvariance.il \
tailcall-generic-cast-conservestack-il.il \
tailcall-generic-cast-nocrash-il.il \
Expand Down
178 changes: 178 additions & 0 deletions mono/tests/dim-reabstraction-generics.il
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@

// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.ver 4:0:0:0
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
}
.assembly 'dim-reabstraction-generics'
{
.custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::'.ctor'(int32) = (01 00 08 00 00 00 00 00 ) // ........

.custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::'.ctor'() = (
01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.

.custom instance void class [mscorlib]System.Diagnostics.DebuggableAttribute::'.ctor'(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = (01 00 07 01 00 00 00 00 ) // ........

.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module 'dim-reabstraction-generics.exe' // GUID = {198880C7-E25B-497F-AE09-6C555BBBAB42}


.class interface public auto ansi abstract I1`1<T>
{

// method line 1
.method public virtual hidebysig newslot abstract
instance default int32 M1 () cil managed
{
// Method begins at RVA 0x0
} // end of method I1`1::M1

// method line 2
.method public virtual hidebysig newslot
instance default int32 M2 () cil managed
{
// Method begins at RVA 0x2050
// Code size 7 (0x7)
.maxstack 1
.locals init (
int32 V_0)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_0005

IL_0005: ldloc.0
IL_0006: ret
} // end of method I1`1::M2

} // end of class I1`1

.class interface public auto ansi abstract I2`1<T>
implements class I1`1<!0> {

// method line 3
.method private final virtual hidebysig abstract
instance default int32 'I1<T>.M1' () cil managed
{
// Method begins at RVA 0x0
} // end of method I2`1::I1<T>.M1

// method line 4
.method public virtual hidebysig newslot
instance default int32 M2 () cil managed
{
// Method begins at RVA 0x2064
// Code size 11 (0xb)
.maxstack 1
.locals init (
int32 V_0)
IL_0000: nop
IL_0001: ldc.i4 200
IL_0006: stloc.0
IL_0007: br.s IL_0009

IL_0009: ldloc.0
IL_000a: ret
} // end of method I2`1::M2

} // end of class I2`1

.class private auto ansi beforefieldinit Test1
extends [mscorlib]System.Object
implements class I2`1<int32>, class I1`1<int32> {

// method line 5
.method private static hidebysig
default int32 Main () cil managed
{
// Method begins at RVA 0x207c
.entrypoint
// Code size 54 (0x36)
.maxstack 2
.locals init (
class I1`1<int32> V_0,
bool V_1,
int32 V_2,
bool V_3)
IL_0000: nop
IL_0001: newobj instance void class Test1::'.ctor'()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt instance int32 class I1`1<int32>::M1()
IL_000d: ldc.i4.s 0x64
IL_000f: ceq
IL_0011: ldc.i4.0
IL_0012: ceq
IL_0014: stloc.1
IL_0015: ldloc.1
IL_0016: brfalse.s IL_001d

IL_0018: nop
IL_0019: ldc.i4.m1
IL_001a: stloc.2
IL_001b: br.s IL_0034

IL_001d: ldloc.0
IL_001e: callvirt instance int32 class I1`1<int32>::M2()
IL_0023: ldc.i4.0
IL_0024: cgt.un
IL_0026: stloc.3
IL_0027: ldloc.3
IL_0028: brfalse.s IL_0030

IL_002a: nop
IL_002b: ldc.i4.s 0xfffffffe
IL_002d: stloc.2
IL_002e: br.s IL_0034

IL_0030: ldc.i4.0
IL_0031: stloc.2
IL_0032: br.s IL_0034

IL_0034: ldloc.2
IL_0035: ret
} // end of method Test1::Main

// method line 6
.method private final virtual hidebysig newslot
instance default int32 'I1<System.Int32>.M1' () cil managed
{
// Method begins at RVA 0x20c0
.override method instance int32 class I1`1<int32>::M1()
// Code size 8 (0x8)
.maxstack 1
.locals init (
int32 V_0)
IL_0000: nop
IL_0001: ldc.i4.s 0x64
IL_0003: stloc.0
IL_0004: br.s IL_0006

IL_0006: ldloc.0
IL_0007: ret
} // end of method Test1::I1<System.Int32>.M1

// method line 7
.method public hidebysig specialname rtspecialname
instance default void '.ctor' () cil managed
{
// Method begins at RVA 0x20d4
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void object::'.ctor'()
IL_0006: nop
IL_0007: ret
} // end of method Test1::.ctor

} // end of class Test1

Loading

0 comments on commit a81cc67

Please sign in to comment.