Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mono/metadata/Makefile.am2
-rw-r--r--mono/metadata/class-accessors.c2
-rw-r--r--mono/metadata/class-init-internals.h26
-rw-r--r--mono/metadata/class-init.c1879
-rw-r--r--mono/metadata/class-internals.h9
-rw-r--r--mono/metadata/class-setup-vtable.c2157
-rw-r--r--mono/metadata/class.c141
-rw-r--r--mono/metadata/icall.c31
-rw-r--r--mono/metadata/metadata-internals.h3
-rw-r--r--mono/metadata/metadata.c23
-rw-r--r--mono/mini/interp/interp-internals.h1
-rw-r--r--msvc/libmonoruntime-common.targets2
-rw-r--r--msvc/libmonoruntime-common.targets.filters6
13 files changed, 2445 insertions, 1837 deletions
diff --git a/mono/metadata/Makefile.am b/mono/metadata/Makefile.am
index 7949999d565..3210abf4fdf 100644
--- a/mono/metadata/Makefile.am
+++ b/mono/metadata/Makefile.am
@@ -249,9 +249,11 @@ common_sources = \
class-getters.h \
class-init.h \
class-init.c \
+ class-init-internals.h \
class-internals.h \
class-inlines.h \
class-private-definition.h \
+ class-setup-vtable.c \
class-accessors.c \
cominterop.c \
cominterop.h \
diff --git a/mono/metadata/class-accessors.c b/mono/metadata/class-accessors.c
index 1f3533c6556..533065fc65d 100644
--- a/mono/metadata/class-accessors.c
+++ b/mono/metadata/class-accessors.c
@@ -76,6 +76,8 @@ mono_class_get_flags (MonoClass *klass)
/* all arrays are marked serializable and sealed, bug #42779 */
return TYPE_ATTRIBUTE_CLASS | TYPE_ATTRIBUTE_SERIALIZABLE | TYPE_ATTRIBUTE_SEALED | TYPE_ATTRIBUTE_PUBLIC;
case MONO_CLASS_POINTER:
+ if (m_class_get_byval_arg (klass)->type == MONO_TYPE_FNPTR)
+ return TYPE_ATTRIBUTE_SEALED | TYPE_ATTRIBUTE_PUBLIC;
return TYPE_ATTRIBUTE_CLASS | (mono_class_get_flags (m_class_get_element_class (klass)) & TYPE_ATTRIBUTE_VISIBILITY_MASK);
}
g_assert_not_reached ();
diff --git a/mono/metadata/class-init-internals.h b/mono/metadata/class-init-internals.h
new file mode 100644
index 00000000000..ef338fa2c79
--- /dev/null
+++ b/mono/metadata/class-init-internals.h
@@ -0,0 +1,26 @@
+/*
+ * \file Implementation details for class initialization
+ */
+
+#ifndef __MONO_METADATA_CLASS_INIT_INTERNALS_H__
+#define __MONO_METADATA_CLASS_INIT_INTERNALS_H__
+
+#include <mono/metadata/class.h>
+
+void
+mono_class_setup_interface_id_nolock (MonoClass *klass);
+
+int
+mono_class_setup_interface_offsets_internal (MonoClass *klass, int cur_slot, gboolean overwrite);
+
+int
+mono_class_setup_count_virtual_methods (MonoClass *klass);
+
+gboolean
+mono_class_setup_need_stelemref_method (MonoClass *klass);
+
+gboolean
+mono_class_setup_method_has_preserve_base_overrides_attribute (MonoMethod *method);
+
+
+#endif /* __MONO_METADATA_CLASS_INIT_INTERNALS_H__ */
diff --git a/mono/metadata/class-init.c b/mono/metadata/class-init.c
index 23d04fd682a..89179d05a95 100644
--- a/mono/metadata/class-init.c
+++ b/mono/metadata/class-init.c
@@ -9,6 +9,7 @@
*/
#include <config.h>
#include <mono/metadata/class-init.h>
+#include <mono/metadata/class-init-internals.h>
#include <mono/metadata/class-internals.h>
#include <mono/metadata/custom-attrs-internals.h>
#include <mono/metadata/debug-helpers.h>
@@ -33,6 +34,10 @@
#undef REALLY_INCLUDE_CLASS_DEF
#endif
+#ifdef ENABLE_NETCORE
+#define FEATURE_COVARIANT_RETURNS
+#endif
+
gboolean mono_print_vtable = FALSE;
gboolean mono_align_small_structs = FALSE;
#ifdef ENABLE_NETCORE
@@ -50,13 +55,10 @@ static gint32 class_def_count, class_gtd_count, class_ginst_count, class_gparam_
static mono_mutex_t classes_mutex;
static gboolean class_kind_may_contain_generic_instances (MonoTypeKind kind);
-static int setup_interface_offsets (MonoClass *klass, int cur_slot, gboolean overwrite);
-static void mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup);
static void mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gtd);
static int generic_array_methods (MonoClass *klass);
static void setup_generic_array_ifaces (MonoClass *klass, MonoClass *iface, MonoMethod **methods, int pos, GHashTable *cache);
static gboolean class_has_isbyreflike_attribute (MonoClass *klass);
-static void mono_class_setup_interface_id_internal (MonoClass *klass);
/* This TLS variable points to a GSList of classes which have setup_fields () executing */
static MonoNativeTlsKey setup_fields_tls_id;
@@ -786,9 +788,13 @@ class_has_isbyreflike_attribute (MonoClass *klass)
}
-static gboolean G_GNUC_UNUSED
-method_has_preserve_base_overrides_attribute (MonoMethod *method)
+gboolean
+mono_class_setup_method_has_preserve_base_overrides_attribute (MonoMethod *method)
{
+ MonoImage *image = m_class_get_image (method->klass);
+ /* FIXME: implement well known attribute check for dynamic images */
+ if (image_is_dynamic (image))
+ return FALSE;
return method_has_wellknown_attribute (method, "System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute", TRUE);
}
@@ -933,6 +939,48 @@ mono_class_create_generic_inst (MonoGenericClass *gclass)
return klass;
}
+/*
+ * For a composite class like uint32[], uint32*, set MonoClass:cast_class to the corresponding "intermediate type" (for
+ * arrays) or "verification type" (for pointers) in the sense of ECMA I.8.7.3. This will be used by
+ * mono_class_is_assignable_from.
+ *
+ * Assumes MonoClass:cast_class is already set (for example if it's an array of
+ * some enum) and adjusts it.
+ */
+static void
+class_composite_fixup_cast_class (MonoClass *klass, gboolean for_ptr)
+{
+ switch (m_class_get_byval_arg (m_class_get_cast_class (klass))->type) {
+ case MONO_TYPE_BOOLEAN:
+ if (!for_ptr)
+ break;
+ klass->cast_class = mono_defaults.byte_class;
+ break;
+ case MONO_TYPE_I1:
+ klass->cast_class = mono_defaults.byte_class;
+ break;
+ case MONO_TYPE_U2:
+ klass->cast_class = mono_defaults.int16_class;
+ break;
+ case MONO_TYPE_U4:
+#if TARGET_SIZEOF_VOID_P == 4
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+#endif
+ klass->cast_class = mono_defaults.int32_class;
+ break;
+ case MONO_TYPE_U8:
+#if TARGET_SIZEOF_VOID_P == 8
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+#endif
+ klass->cast_class = mono_defaults.int64_class;
+ break;
+ default:
+ break;
+ }
+}
+
static gboolean
class_kind_may_contain_generic_instances (MonoTypeKind kind)
{
@@ -1085,30 +1133,7 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound
else
klass->cast_class = eclass;
- switch (m_class_get_byval_arg (m_class_get_cast_class (klass))->type) {
- case MONO_TYPE_I1:
- klass->cast_class = mono_defaults.byte_class;
- break;
- case MONO_TYPE_U2:
- klass->cast_class = mono_defaults.int16_class;
- break;
- case MONO_TYPE_U4:
-#if TARGET_SIZEOF_VOID_P == 4
- case MONO_TYPE_I:
- case MONO_TYPE_U:
-#endif
- klass->cast_class = mono_defaults.int32_class;
- break;
- case MONO_TYPE_U8:
-#if TARGET_SIZEOF_VOID_P == 8
- case MONO_TYPE_I:
- case MONO_TYPE_U:
-#endif
- klass->cast_class = mono_defaults.int64_class;
- break;
- default:
- break;
- }
+ class_composite_fixup_cast_class (klass, FALSE);
klass->element_class = eclass;
@@ -1323,7 +1348,7 @@ make_generic_param_class (MonoGenericParam *param)
if (mono_class_has_failure (klass->parent))
mono_class_set_type_load_failure (klass, "Failed to setup parent interfaces");
else
- setup_interface_offsets (klass, klass->parent->vtable_size, TRUE);
+ mono_class_setup_interface_offsets_internal (klass, klass->parent->vtable_size, TRUE);
}
return klass;
@@ -1429,9 +1454,15 @@ mono_class_create_ptr (MonoType *type)
result->inited = TRUE;
result->instance_size = MONO_ABI_SIZEOF (MonoObject) + MONO_ABI_SIZEOF (gpointer);
result->min_align = sizeof (gpointer);
- result->cast_class = result->element_class = el_class;
+ result->element_class = el_class;
result->blittable = TRUE;
+ if (el_class->enumtype)
+ result->cast_class = el_class->element_class;
+ else
+ result->cast_class = el_class;
+ class_composite_fixup_cast_class (result, TRUE);
+
result->this_arg.type = result->_byval_arg.type = MONO_TYPE_PTR;
result->this_arg.data.type = result->_byval_arg.data.type = m_class_get_byval_arg (el_class);
result->this_arg.byref = TRUE;
@@ -1533,75 +1564,6 @@ mono_class_create_fnptr (MonoMethodSignature *sig)
}
-static void
-print_implemented_interfaces (MonoClass *klass)
-{
- char *name;
- ERROR_DECL (error);
- GPtrArray *ifaces = NULL;
- int i;
- int ancestor_level = 0;
-
- name = mono_type_get_full_name (klass);
- printf ("Packed interface table for class %s has size %d\n", name, klass->interface_offsets_count);
- g_free (name);
-
- for (i = 0; i < klass->interface_offsets_count; i++) {
- char *ic_name = mono_type_get_full_name (klass->interfaces_packed [i]);
- printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s\n", i,
- klass->interfaces_packed [i]->interface_id,
- klass->interface_offsets_packed [i],
- mono_class_get_method_count (klass->interfaces_packed [i]),
- ic_name);
- g_free (ic_name);
- }
- printf ("Interface flags: ");
- for (i = 0; i <= klass->max_interface_id; i++)
- if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i))
- printf ("(%d,T)", i);
- else
- printf ("(%d,F)", i);
- printf ("\n");
- printf ("Dump interface flags:");
-#ifdef COMPRESSED_INTERFACE_BITMAP
- {
- const uint8_t* p = klass->interface_bitmap;
- i = klass->max_interface_id;
- while (i > 0) {
- printf (" %d x 00 %02X", p [0], p [1]);
- i -= p [0] * 8;
- i -= 8;
- }
- }
-#else
- for (i = 0; i < ((((klass->max_interface_id + 1) >> 3)) + (((klass->max_interface_id + 1) & 7)? 1 :0)); i++)
- printf (" %02X", klass->interface_bitmap [i]);
-#endif
- printf ("\n");
- while (klass != NULL) {
- printf ("[LEVEL %d] Implemented interfaces by class %s:\n", ancestor_level, klass->name);
- ifaces = mono_class_get_implemented_interfaces (klass, error);
- if (!is_ok (error)) {
- printf (" Type failed due to %s\n", mono_error_get_message (error));
- mono_error_cleanup (error);
- } else if (ifaces) {
- for (i = 0; i < ifaces->len; i++) {
- MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i);
- printf (" [UIID %d] interface %s\n", ic->interface_id, ic->name);
- printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i,
- ic->interface_id,
- mono_class_interface_offset (klass, ic),
- mono_class_get_method_count (ic),
- ic->name_space,
- ic->name );
- }
- g_ptr_array_free (ifaces, TRUE);
- }
- ancestor_level ++;
- klass = klass->parent;
- }
-}
-
static gboolean
method_is_reabstracted (guint16 flags)
{
@@ -1610,14 +1572,16 @@ method_is_reabstracted (guint16 flags)
return FALSE;
}
-/*
+/**
+ * mono_class_setup_count_virtual_methods:
+ *
* 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.
* Return -1 on failure.
* FIXME It would be nice if this information could be cached somewhere.
*/
-static int
-count_virtual_methods (MonoClass *klass)
+int
+mono_class_setup_count_virtual_methods (MonoClass *klass)
{
int i, mcount, vcount = 0;
guint32 flags;
@@ -1653,26 +1617,6 @@ count_virtual_methods (MonoClass *klass)
return vcount;
}
-static mono_bool
-set_interface_and_offset (int num_ifaces, MonoClass **interfaces_full, int *interface_offsets_full, MonoClass *ic, int offset, mono_bool force_set)
-{
- int i;
- for (i = 0; i < num_ifaces; ++i) {
- if (interfaces_full [i] && interfaces_full [i]->interface_id == ic->interface_id) {
- if (!force_set)
- return TRUE;
- interface_offsets_full [i] = offset;
- return FALSE;
- }
- if (interfaces_full [i])
- continue;
- interfaces_full [i] = ic;
- interface_offsets_full [i] = offset;
- break;
- }
- return FALSE;
-}
-
#ifdef COMPRESSED_INTERFACE_BITMAP
/*
@@ -1779,1678 +1723,6 @@ mono_class_interface_match (const uint8_t *bitmap, int id)
}
#endif
-/*
- * Return -1 on failure and set klass->has_failure and store a MonoErrorBoxed with the details.
- * LOCKING: Acquires the loader lock.
- */
-static int
-setup_interface_offsets (MonoClass *klass, int cur_slot, gboolean overwrite)
-{
- ERROR_DECL (error);
- MonoClass *k, *ic;
- int i, j, num_ifaces;
- guint32 max_iid;
- MonoClass **interfaces_full = NULL;
- int *interface_offsets_full = NULL;
- GPtrArray *ifaces;
- GPtrArray **ifaces_array = NULL;
- int interface_offsets_count;
- max_iid = 0;
- num_ifaces = interface_offsets_count = 0;
-
- mono_loader_lock ();
-
- mono_class_setup_supertypes (klass);
-
- if (mono_class_is_ginst (klass)) {
- MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
-
- interface_offsets_count = num_ifaces = gklass->interface_offsets_count;
- interfaces_full = (MonoClass **)g_malloc (sizeof (MonoClass*) * num_ifaces);
- interface_offsets_full = (int *)g_malloc (sizeof (int) * num_ifaces);
-
- cur_slot = 0;
- for (int i = 0; i < num_ifaces; ++i) {
- MonoClass *gklass_ic = gklass->interfaces_packed [i];
- MonoClass *inflated = mono_class_inflate_generic_class_checked (gklass_ic, mono_class_get_context(klass), error);
- if (!is_ok (error)) {
- char *name = mono_type_get_full_name (gklass_ic);
- mono_class_set_type_load_failure (klass, "Error calculating interface offset of %s", name);
- g_free (name);
- cur_slot = -1;
- goto end;
- }
-
- mono_class_setup_interface_id_internal (inflated);
-
- interfaces_full [i] = inflated;
- interface_offsets_full [i] = gklass->interface_offsets_packed [i];
-
- int count = count_virtual_methods (inflated);
- if (count == -1) {
- char *name = mono_type_get_full_name (inflated);
- mono_class_set_type_load_failure (klass, "Error calculating interface offset of %s", name);
- g_free (name);
- cur_slot = -1;
- goto end;
- }
-
- cur_slot = MAX (cur_slot, interface_offsets_full [i] + count);
- max_iid = MAX (max_iid, inflated->interface_id);
- }
-
- goto publish;
- }
- /* compute maximum number of slots and maximum interface id */
- max_iid = 0;
- num_ifaces = 0; /* this can include duplicated ones */
- ifaces_array = g_new0 (GPtrArray *, klass->idepth);
- for (j = 0; j < klass->idepth; j++) {
- k = klass->supertypes [j];
- g_assert (k);
- num_ifaces += k->interface_count;
- for (i = 0; i < k->interface_count; i++) {
- ic = k->interfaces [i];
-
- /* A gparam does not have any interface_id set. */
- if (! mono_class_is_gparam (ic))
- mono_class_setup_interface_id_internal (ic);
-
- if (max_iid < ic->interface_id)
- max_iid = ic->interface_id;
- }
- ifaces = mono_class_get_implemented_interfaces (k, error);
- if (!is_ok (error)) {
- char *name = mono_type_get_full_name (k);
- mono_class_set_type_load_failure (klass, "Error getting the interfaces of %s due to %s", name, mono_error_get_message (error));
- g_free (name);
- mono_error_cleanup (error);
- cur_slot = -1;
- goto end;
- }
- if (ifaces) {
- num_ifaces += ifaces->len;
- for (i = 0; i < ifaces->len; ++i) {
- ic = (MonoClass *)g_ptr_array_index (ifaces, i);
- if (max_iid < ic->interface_id)
- max_iid = ic->interface_id;
- }
- ifaces_array [j] = ifaces;
- }
- }
-
- if (MONO_CLASS_IS_INTERFACE_INTERNAL (klass)) {
- num_ifaces++;
- if (max_iid < klass->interface_id)
- max_iid = klass->interface_id;
- }
-
- /* compute vtable offset for interfaces */
- interfaces_full = (MonoClass **)g_malloc0 (sizeof (MonoClass*) * num_ifaces);
- interface_offsets_full = (int *)g_malloc (sizeof (int) * num_ifaces);
-
- for (i = 0; i < num_ifaces; i++)
- interface_offsets_full [i] = -1;
-
- /* skip the current class */
- for (j = 0; j < klass->idepth - 1; j++) {
- k = klass->supertypes [j];
- ifaces = ifaces_array [j];
-
- if (ifaces) {
- for (i = 0; i < ifaces->len; ++i) {
- int io;
- ic = (MonoClass *)g_ptr_array_index (ifaces, i);
-
- /*Force the sharing of interface offsets between parent and subtypes.*/
- io = mono_class_interface_offset (k, ic);
- g_assertf (io >= 0, "class %s parent %s has no offset for iface %s",
- mono_type_get_full_name (klass),
- mono_type_get_full_name (k),
- mono_type_get_full_name (ic));
-
- set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, io, TRUE);
- }
- }
- }
-
- g_assert (klass == klass->supertypes [klass->idepth - 1]);
- ifaces = ifaces_array [klass->idepth - 1];
- if (ifaces) {
- for (i = 0; i < ifaces->len; ++i) {
- int count;
- ic = (MonoClass *)g_ptr_array_index (ifaces, i);
- if (set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, cur_slot, FALSE))
- continue;
- count = count_virtual_methods (ic);
- if (count == -1) {
- char *name = mono_type_get_full_name (ic);
- mono_class_set_type_load_failure (klass, "Error calculating interface offset of %s", name);
- g_free (name);
- cur_slot = -1;
- goto end;
- }
- cur_slot += count;
- }
- }
-
- if (MONO_CLASS_IS_INTERFACE_INTERNAL (klass))
- set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, klass, cur_slot, TRUE);
-
- for (interface_offsets_count = 0, i = 0; i < num_ifaces; i++) {
- if (interface_offsets_full [i] != -1)
- interface_offsets_count ++;
- }
-
-publish:
- /* Publish the data */
- klass->max_interface_id = max_iid;
- /*
- * We might get called multiple times:
- * - mono_class_init_internal ()
- * - mono_class_setup_vtable ().
- * - mono_class_setup_interface_offsets ().
- * mono_class_setup_interface_offsets () passes 0 as CUR_SLOT, so the computed interface offsets will be invalid. This
- * means we have to overwrite those when called from other places (#4440).
- */
- if (klass->interfaces_packed) {
- if (!overwrite)
- g_assert (klass->interface_offsets_count == interface_offsets_count);
- } else {
- uint8_t *bitmap;
- int bsize;
- klass->interface_offsets_count = interface_offsets_count;
- klass->interfaces_packed = (MonoClass **)mono_class_alloc (klass, sizeof (MonoClass*) * interface_offsets_count);
- klass->interface_offsets_packed = (guint16 *)mono_class_alloc (klass, sizeof (guint16) * interface_offsets_count);
- bsize = (sizeof (guint8) * ((max_iid + 1) >> 3)) + (((max_iid + 1) & 7)? 1 :0);
-#ifdef COMPRESSED_INTERFACE_BITMAP
- bitmap = g_malloc0 (bsize);
-#else
- bitmap = (uint8_t *)mono_class_alloc0 (klass, bsize);
-#endif
- for (i = 0; i < interface_offsets_count; i++) {
- guint32 id = interfaces_full [i]->interface_id;
- bitmap [id >> 3] |= (1 << (id & 7));
- klass->interfaces_packed [i] = interfaces_full [i];
- klass->interface_offsets_packed [i] = interface_offsets_full [i];
- }
-#ifdef COMPRESSED_INTERFACE_BITMAP
- i = mono_compress_bitmap (NULL, bitmap, bsize);
- klass->interface_bitmap = mono_class_alloc0 (klass, i);
- mono_compress_bitmap (klass->interface_bitmap, bitmap, bsize);
- g_free (bitmap);
-#else
- klass->interface_bitmap = bitmap;
-#endif
- }
-end:
- mono_loader_unlock ();
-
- g_free (interfaces_full);
- g_free (interface_offsets_full);
- if (ifaces_array) {
- for (i = 0; i < klass->idepth; i++) {
- ifaces = ifaces_array [i];
- if (ifaces)
- g_ptr_array_free (ifaces, TRUE);
- }
- g_free (ifaces_array);
- }
-
- //printf ("JUST DONE: ");
- //print_implemented_interfaces (klass);
-
- return cur_slot;
-}
-
-/*
- * Setup interface offsets for interfaces.
- * Initializes:
- * - klass->max_interface_id
- * - klass->interface_offsets_count
- * - klass->interfaces_packed
- * - klass->interface_offsets_packed
- * - klass->interface_bitmap
- *
- * This function can fail @class.
- *
- */
-void
-mono_class_setup_interface_offsets (MonoClass *klass)
-{
- /* NOTE: This function is only correct for interfaces.
- *
- * It assumes that klass's interfaces can be assigned offsets starting
- * from 0. That assumption is incorrect for classes and valuetypes.
- */
- g_assert (MONO_CLASS_IS_INTERFACE_INTERNAL (klass) && !mono_class_is_ginst (klass));
- setup_interface_offsets (klass, 0, FALSE);
-}
-
-#define DEBUG_INTERFACE_VTABLE_CODE 0
-#define TRACE_INTERFACE_VTABLE_CODE 0
-#define VERIFY_INTERFACE_VTABLE_CODE 0
-#define VTABLE_SELECTOR (1)
-
-#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE)
-#define DEBUG_INTERFACE_VTABLE(stmt) do {\
- if (!(VTABLE_SELECTOR)) break; \
- stmt;\
-} while (0)
-#else
-#define DEBUG_INTERFACE_VTABLE(stmt)
-#endif
-
-#if TRACE_INTERFACE_VTABLE_CODE
-#define TRACE_INTERFACE_VTABLE(stmt) do {\
- if (!(VTABLE_SELECTOR)) break; \
- stmt;\
-} while (0)
-#else
-#define TRACE_INTERFACE_VTABLE(stmt)
-#endif
-
-#if VERIFY_INTERFACE_VTABLE_CODE
-#define VERIFY_INTERFACE_VTABLE(stmt) do {\
- if (!(VTABLE_SELECTOR)) break; \
- stmt;\
-} while (0)
-#else
-#define VERIFY_INTERFACE_VTABLE(stmt)
-#endif
-
-
-#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE)
-static char*
-mono_signature_get_full_desc (MonoMethodSignature *sig, gboolean include_namespace)
-{
- int i;
- char *result;
- GString *res = g_string_new ("");
-
- g_string_append_c (res, '(');
- for (i = 0; i < sig->param_count; ++i) {
- if (i > 0)
- g_string_append_c (res, ',');
- mono_type_get_desc (res, sig->params [i], include_namespace);
- }
- g_string_append (res, ")=>");
- if (sig->ret != NULL) {
- mono_type_get_desc (res, sig->ret, include_namespace);
- } else {
- g_string_append (res, "NULL");
- }
- result = res->str;
- g_string_free (res, FALSE);
- return result;
-}
-static void
-print_method_signatures (MonoMethod *im, MonoMethod *cm) {
- char *im_sig = mono_signature_get_full_desc (mono_method_signature_internal (im), TRUE);
- char *cm_sig = mono_signature_get_full_desc (mono_method_signature_internal (cm), TRUE);
- printf ("(IM \"%s\", CM \"%s\")", im_sig, cm_sig);
- g_free (im_sig);
- g_free (cm_sig);
-
-}
-
-#endif
-
-static gboolean
-is_wcf_hack_disabled (void)
-{
- static char disabled;
- if (!disabled)
- disabled = g_hasenv ("MONO_DISABLE_WCF_HACK") ? 1 : 2;
- return disabled == 1;
-}
-
-static gboolean
-check_interface_method_override (MonoClass *klass, MonoMethod *im, MonoMethod *cm, gboolean require_newslot, gboolean interface_is_explicitly_implemented_by_class, gboolean slot_is_empty)
-{
- MonoMethodSignature *cmsig, *imsig;
- if (strcmp (im->name, cm->name) == 0) {
- if (! (cm->flags & METHOD_ATTRIBUTE_PUBLIC)) {
- TRACE_INTERFACE_VTABLE (printf ("[PUBLIC CHECK FAILED]"));
- return FALSE;
- }
- if (! slot_is_empty) {
- if (require_newslot) {
- if (! interface_is_explicitly_implemented_by_class) {
- TRACE_INTERFACE_VTABLE (printf ("[NOT EXPLICIT IMPLEMENTATION IN FULL SLOT REFUSED]"));
- return FALSE;
- }
- if (! (cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) {
- TRACE_INTERFACE_VTABLE (printf ("[NEWSLOT CHECK FAILED]"));
- return FALSE;
- }
- } else {
- TRACE_INTERFACE_VTABLE (printf ("[FULL SLOT REFUSED]"));
- }
- }
- cmsig = mono_method_signature_internal (cm);
- imsig = mono_method_signature_internal (im);
- if (!cmsig || !imsig) {
- mono_class_set_type_load_failure (klass, "Could not resolve the signature of a virtual method");
- return FALSE;
- }
-
- if (! mono_metadata_signature_equal (cmsig, imsig)) {
- TRACE_INTERFACE_VTABLE (printf ("[SIGNATURE CHECK FAILED "));
- TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm));
- TRACE_INTERFACE_VTABLE (printf ("]"));
- return FALSE;
- }
- TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS]"));
- if (mono_security_core_clr_enabled ())
- mono_security_core_clr_check_override (klass, cm, im);
-
- TRACE_INTERFACE_VTABLE (printf ("[NAME CHECK OK]"));
- if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) {
- char *body_name = mono_method_full_name (cm, TRUE);
- char *decl_name = mono_method_full_name (im, TRUE);
- mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
- g_free (body_name);
- g_free (decl_name);
- return FALSE;
- }
-
- return TRUE;
- } else {
- MonoClass *ic = im->klass;
- const char *ic_name_space = ic->name_space;
- const char *ic_name = ic->name;
- char *subname;
-
- if (! require_newslot) {
- TRACE_INTERFACE_VTABLE (printf ("[INJECTED METHOD REFUSED]"));
- return FALSE;
- }
- if (cm->klass->rank == 0) {
- TRACE_INTERFACE_VTABLE (printf ("[RANK CHECK FAILED]"));
- return FALSE;
- }
- cmsig = mono_method_signature_internal (cm);
- imsig = mono_method_signature_internal (im);
- if (!cmsig || !imsig) {
- mono_class_set_type_load_failure (klass, "Could not resolve the signature of a virtual method");
- return FALSE;
- }
-
- if (! mono_metadata_signature_equal (cmsig, imsig)) {
- TRACE_INTERFACE_VTABLE (printf ("[(INJECTED) SIGNATURE CHECK FAILED "));
- TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm));
- TRACE_INTERFACE_VTABLE (printf ("]"));
- return FALSE;
- }
- if (mono_class_get_image (ic) != mono_defaults.corlib) {
- TRACE_INTERFACE_VTABLE (printf ("[INTERFACE CORLIB CHECK FAILED]"));
- return FALSE;
- }
- if ((ic_name_space == NULL) || (strcmp (ic_name_space, "System.Collections.Generic") != 0)) {
- TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAMESPACE CHECK FAILED]"));
- return FALSE;
- }
- if ((ic_name == NULL) || ((strcmp (ic_name, "IEnumerable`1") != 0) && (strcmp (ic_name, "ICollection`1") != 0) && (strcmp (ic_name, "IList`1") != 0) && (strcmp (ic_name, "IReadOnlyList`1") != 0) && (strcmp (ic_name, "IReadOnlyCollection`1") != 0))) {
- TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAME CHECK FAILED]"));
- return FALSE;
- }
-
- subname = (char*)strstr (cm->name, ic_name_space);
- if (subname != cm->name) {
- TRACE_INTERFACE_VTABLE (printf ("[ACTUAL NAMESPACE CHECK FAILED]"));
- return FALSE;
- }
- subname += strlen (ic_name_space);
- if (subname [0] != '.') {
- TRACE_INTERFACE_VTABLE (printf ("[FIRST DOT CHECK FAILED]"));
- return FALSE;
- }
- subname ++;
- if (strstr (subname, ic_name) != subname) {
- TRACE_INTERFACE_VTABLE (printf ("[ACTUAL CLASS NAME CHECK FAILED]"));
- return FALSE;
- }
- subname += strlen (ic_name);
- if (subname [0] != '.') {
- TRACE_INTERFACE_VTABLE (printf ("[SECOND DOT CHECK FAILED]"));
- return FALSE;
- }
- subname ++;
- if (strcmp (subname, im->name) != 0) {
- TRACE_INTERFACE_VTABLE (printf ("[METHOD NAME CHECK FAILED]"));
- return FALSE;
- }
-
- TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS (INJECTED CASE)]"));
- if (mono_security_core_clr_enabled ())
- mono_security_core_clr_check_override (klass, cm, im);
-
- TRACE_INTERFACE_VTABLE (printf ("[INJECTED INTERFACE CHECK OK]"));
- if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) {
- char *body_name = mono_method_full_name (cm, TRUE);
- char *decl_name = mono_method_full_name (im, TRUE);
- mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
- g_free (body_name);
- g_free (decl_name);
- return FALSE;
- }
-
- return TRUE;
- }
-}
-
-#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE)
-static void
-foreach_override (gpointer key, gpointer value, gpointer user_data)
-{
- MonoMethod *method = key;
- MonoMethod *override = value;
-
- char *method_name = mono_method_get_full_name (method);
- char *override_name = mono_method_get_full_name (override);
- printf (" Method '%s' has override '%s'\n", method_name, override_name);
- g_free (method_name);
- g_free (override_name);
-}
-
-static void
-print_overrides (GHashTable *override_map, const char *message)
-{
- if (override_map) {
- printf ("Override map \"%s\" START:\n", message);
- g_hash_table_foreach (override_map, foreach_override, NULL);
- printf ("Override map \"%s\" END.\n", message);
- } else {
- printf ("Override map \"%s\" EMPTY.\n", message);
- }
-}
-
-static void
-print_vtable_full (MonoClass *klass, MonoMethod** vtable, int size, int first_non_interface_slot, const char *message, gboolean print_interfaces)
-{
- char *full_name = mono_type_full_name (m_class_get_byval_arg (klass));
- int i;
- int parent_size;
-
- printf ("*** Vtable for class '%s' at \"%s\" (size %d)\n", full_name, message, size);
-
- if (print_interfaces) {
- print_implemented_interfaces (klass);
- printf ("* Interfaces for class '%s' done.\nStarting vtable (size %d):\n", full_name, size);
- }
-
- if (klass->parent) {
- parent_size = klass->parent->vtable_size;
- } else {
- parent_size = 0;
- }
- for (i = 0; i < size; ++i) {
- MonoMethod *cm = vtable [i];
- char *cm_name = cm ? mono_method_full_name (cm, TRUE) : g_strdup ("nil");
- char newness = (i < parent_size) ? 'O' : ((i < first_non_interface_slot) ? 'I' : 'N');
-
- printf (" [%c][%03d][INDEX %03d] %s [%p]\n", newness, i, cm ? cm->slot : - 1, cm_name, cm);
- g_free (cm_name);
- }
-
- g_free (full_name);
-}
-#endif
-
-#if VERIFY_INTERFACE_VTABLE_CODE
-static int
-mono_method_try_get_vtable_index (MonoMethod *method)
-{
- if (method->is_inflated && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
- MonoMethodInflated *imethod = (MonoMethodInflated*)method;
- if (imethod->declaring->is_generic)
- return imethod->declaring->slot;
- }
- return method->slot;
-}
-
-static void
-mono_class_verify_vtable (MonoClass *klass)
-{
- int i, count;
- char *full_name = mono_type_full_name (m_class_get_byval_arg (klass));
-
- printf ("*** Verifying VTable of class '%s' \n", full_name);
- g_free (full_name);
- full_name = NULL;
-
- if (!klass->methods)
- return;
-
- count = mono_class_get_method_count (klass);
- for (i = 0; i < count; ++i) {
- MonoMethod *cm = klass->methods [i];
- int slot;
-
- if (!(cm->flags & METHOD_ATTRIBUTE_VIRTUAL))
- continue;
-
- g_free (full_name);
- full_name = mono_method_full_name (cm, TRUE);
-
- slot = mono_method_try_get_vtable_index (cm);
- if (slot >= 0) {
- if (slot >= klass->vtable_size) {
- printf ("\tInvalid method %s at index %d with vtable of length %d\n", full_name, slot, klass->vtable_size);
- continue;
- }
-
- if (slot >= 0 && klass->vtable [slot] != cm && (klass->vtable [slot])) {
- char *other_name = klass->vtable [slot] ? mono_method_full_name (klass->vtable [slot], TRUE) : g_strdup ("[null value]");
- printf ("\tMethod %s has slot %d but vtable has %s on it\n", full_name, slot, other_name);
- g_free (other_name);
- }
- } else
- printf ("\tVirtual method %s does n't have an assigned slot\n", full_name);
- }
- g_free (full_name);
-}
-#endif
-
-static MonoMethod*
-mono_method_get_method_definition (MonoMethod *method)
-{
- while (method->is_inflated)
- method = ((MonoMethodInflated*)method)->declaring;
- return method;
-}
-
-static gboolean
-verify_class_overrides (MonoClass *klass, MonoMethod **overrides, int onum)
-{
- int i;
-
- for (i = 0; i < onum; ++i) {
- MonoMethod *decl = overrides [i * 2];
- MonoMethod *body = overrides [i * 2 + 1];
-
- if (mono_class_get_generic_type_definition (body->klass) != mono_class_get_generic_type_definition (klass)) {
- mono_class_set_type_load_failure (klass, "Method belongs to a different class than the declared one");
- return FALSE;
- }
-
- if (!(body->flags & METHOD_ATTRIBUTE_VIRTUAL) || (body->flags & METHOD_ATTRIBUTE_STATIC)) {
- if (body->flags & METHOD_ATTRIBUTE_STATIC)
- mono_class_set_type_load_failure (klass, "Method must not be static to override a base type");
- else
- mono_class_set_type_load_failure (klass, "Method must be virtual to override a base type");
- return FALSE;
- }
-
- if (!(decl->flags & METHOD_ATTRIBUTE_VIRTUAL) || (decl->flags & METHOD_ATTRIBUTE_STATIC)) {
- if (body->flags & METHOD_ATTRIBUTE_STATIC)
- mono_class_set_type_load_failure (klass, "Cannot override a static method in a base type");
- else
- mono_class_set_type_load_failure (klass, "Cannot override a non virtual method in a base type");
- return FALSE;
- }
-
- if (!mono_class_is_assignable_from_slow (decl->klass, klass)) {
- mono_class_set_type_load_failure (klass, "Method overrides a class or interface that is not extended or implemented by this type");
- return FALSE;
- }
-
- body = mono_method_get_method_definition (body);
- decl = mono_method_get_method_definition (decl);
-
- if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (body, decl, NULL)) {
- char *body_name = mono_method_full_name (body, TRUE);
- char *decl_name = mono_method_full_name (decl, TRUE);
- mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
- g_free (body_name);
- g_free (decl_name);
- return FALSE;
- }
- }
- return TRUE;
-}
-
-/*Checks if @klass has @parent as one of it's parents type gtd
- *
- * For example:
- * Foo<T>
- * Bar<T> : Foo<Bar<Bar<T>>>
- *
- */
-static gboolean
-mono_class_has_gtd_parent (MonoClass *klass, MonoClass *parent)
-{
- klass = mono_class_get_generic_type_definition (klass);
- parent = mono_class_get_generic_type_definition (parent);
- mono_class_setup_supertypes (klass);
- mono_class_setup_supertypes (parent);
-
- return klass->idepth >= parent->idepth &&
- mono_class_get_generic_type_definition (klass->supertypes [parent->idepth - 1]) == parent;
-}
-
-gboolean
-mono_class_check_vtable_constraints (MonoClass *klass, GList *in_setup)
-{
- MonoGenericInst *ginst;
- int i;
-
- if (!mono_class_is_ginst (klass)) {
- mono_class_setup_vtable_full (klass, in_setup);
- return !mono_class_has_failure (klass);
- }
-
- mono_class_setup_vtable_full (mono_class_get_generic_type_definition (klass), in_setup);
- if (mono_class_set_type_load_failure_causedby_class (klass, mono_class_get_generic_class (klass)->container_class, "Failed to load generic definition vtable"))
- return FALSE;
-
- ginst = mono_class_get_generic_class (klass)->context.class_inst;
- for (i = 0; i < ginst->type_argc; ++i) {
- MonoClass *arg;
- if (ginst->type_argv [i]->type != MONO_TYPE_GENERICINST)
- continue;
- arg = mono_class_from_mono_type_internal (ginst->type_argv [i]);
- /*Those 2 will be checked by mono_class_setup_vtable itself*/
- if (mono_class_has_gtd_parent (klass, arg) || mono_class_has_gtd_parent (arg, klass))
- continue;
- if (!mono_class_check_vtable_constraints (arg, in_setup)) {
- mono_class_set_type_load_failure (klass, "Failed to load generic parameter %d", i);
- return FALSE;
- }
- }
- return TRUE;
-}
-
-/*
- * mono_class_setup_vtable:
- *
- * Creates the generic vtable of CLASS.
- * Initializes the following fields in MonoClass:
- * - vtable
- * - vtable_size
- * Plus all the fields initialized by setup_interface_offsets ().
- * If there is an error during vtable construction, klass->has_failure
- * is set and details are stored in a MonoErrorBoxed.
- *
- * LOCKING: Acquires the loader lock.
- */
-void
-mono_class_setup_vtable (MonoClass *klass)
-{
- mono_class_setup_vtable_full (klass, NULL);
-}
-
-static void
-mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup)
-{
- ERROR_DECL (error);
- MonoMethod **overrides = NULL;
- MonoGenericContext *context;
- guint32 type_token;
- int onum = 0;
-
- if (klass->vtable)
- return;
-
- if (MONO_CLASS_IS_INTERFACE_INTERNAL (klass)) {
- /* This sets method->slot for all methods if this is an interface */
- mono_class_setup_methods (klass);
- return;
- }
-
- if (mono_class_has_failure (klass))
- return;
-
- if (g_list_find (in_setup, klass))
- return;
-
- mono_loader_lock ();
-
- if (klass->vtable) {
- mono_loader_unlock ();
- return;
- }
-
- UnlockedIncrement (&mono_stats.generic_vtable_count);
- in_setup = g_list_prepend (in_setup, klass);
-
- if (mono_class_is_ginst (klass)) {
- if (!mono_class_check_vtable_constraints (klass, in_setup)) {
- mono_loader_unlock ();
- g_list_remove (in_setup, klass);
- return;
- }
-
- context = mono_class_get_context (klass);
- type_token = mono_class_get_generic_class (klass)->container_class->type_token;
- } else {
- context = (MonoGenericContext *) mono_class_try_get_generic_container (klass); //FIXME is this a case of a try?
- type_token = klass->type_token;
- }
-
- if (image_is_dynamic (klass->image)) {
- /* Generic instances can have zero method overrides without causing any harm.
- * This is true since we don't do layout all over again for them, we simply inflate
- * the layout of the parent.
- */
- mono_reflection_get_dynamic_overrides (klass, &overrides, &onum, error);
- if (!is_ok (error)) {
- mono_class_set_type_load_failure (klass, "Could not load list of method overrides due to %s", mono_error_get_message (error));
- goto done;
- }
- } else {
- /* The following call fails if there are missing methods in the type */
- /* FIXME it's probably a good idea to avoid this for generic instances. */
- mono_class_get_overrides_full (klass->image, type_token, &overrides, &onum, context, error);
- if (!is_ok (error)) {
- mono_class_set_type_load_failure (klass, "Could not load list of method overrides due to %s", mono_error_get_message (error));
- goto done;
- }
- }
-
- mono_class_setup_vtable_general (klass, overrides, onum, in_setup);
-
-done:
- g_free (overrides);
- mono_error_cleanup (error);
-
- mono_loader_unlock ();
- g_list_remove (in_setup, klass);
-
- return;
-}
-
-static gboolean
-mono_class_need_stelemref_method (MonoClass *klass)
-{
- return klass->rank == 1 && MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (m_class_get_element_class (klass)));
-}
-
-static int
-apply_override (MonoClass *klass, MonoClass *override_class, MonoMethod **vtable, MonoMethod *decl, MonoMethod *override,
- GHashTable **override_map, GHashTable **override_class_map, GHashTable **conflict_map)
-{
- int dslot;
- dslot = mono_method_get_vtable_slot (decl);
- if (dslot == -1) {
- mono_class_set_type_load_failure (klass, "");
- return FALSE;
- }
-
- dslot += mono_class_interface_offset (klass, decl->klass);
-
- //check if the override comes from an interface and the overrided method is from a class, if this is the case it shouldn't be changed
- if (vtable [dslot] && vtable [dslot]->klass && MONO_CLASS_IS_INTERFACE_INTERNAL (override->klass) && !MONO_CLASS_IS_INTERFACE_INTERNAL (vtable [dslot]->klass))
- return TRUE;
-
- vtable [dslot] = override;
- if (!MONO_CLASS_IS_INTERFACE_INTERNAL (override->klass)) {
- /*
- * If override from an interface, then it is an override of a default interface method,
- * don't override its slot.
- */
- vtable [dslot]->slot = dslot;
- }
-
- if (!*override_map) {
- *override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
- *override_class_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
- }
- GHashTable *map = *override_map;
- GHashTable *class_map = *override_class_map;
-
- MonoMethod *prev_override = (MonoMethod*)g_hash_table_lookup (map, decl);
- MonoClass *prev_override_class = (MonoClass*)g_hash_table_lookup (class_map, decl);
-
- g_hash_table_insert (map, decl, override);
- g_hash_table_insert (class_map, decl, override_class);
-
- /* Collect potentially conflicting overrides which are introduced by default interface methods */
- if (prev_override) {
- ERROR_DECL (error);
-
- /*
- * The override methods are part of the generic definition, need to inflate them so their
- * parent class becomes the actual interface/class containing the override, i.e.
- * IFace<T> in:
- * class Foo<T> : IFace<T>
- * This is needed so the mono_class_is_assignable_from_internal () calls in the
- * conflict resolution work.
- */
- if (mono_class_is_ginst (override_class)) {
- override = mono_class_inflate_generic_method_checked (override, &mono_class_get_generic_class (override_class)->context, error);
- mono_error_assert_ok (error);
- }
-
- if (mono_class_is_ginst (prev_override_class)) {
- prev_override = mono_class_inflate_generic_method_checked (prev_override, &mono_class_get_generic_class (prev_override_class)->context, error);
- mono_error_assert_ok (error);
- }
-
- if (!*conflict_map)
- *conflict_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
- GHashTable *cmap = *conflict_map;
- GSList *entries = (GSList*)g_hash_table_lookup (cmap, decl);
- if (!(decl->flags & METHOD_ATTRIBUTE_ABSTRACT))
- entries = g_slist_prepend (entries, decl);
- entries = g_slist_prepend (entries, prev_override);
- entries = g_slist_prepend (entries, override);
-
- g_hash_table_insert (cmap, decl, entries);
- }
-
- return TRUE;
-}
-
-static void
-handle_dim_conflicts (MonoMethod **vtable, MonoClass *klass, GHashTable *conflict_map)
-{
- GHashTableIter iter;
- MonoMethod *decl;
- GSList *entries, *l, *l2;
- GSList *dim_conflicts = NULL;
-
- g_hash_table_iter_init (&iter, conflict_map);
- while (g_hash_table_iter_next (&iter, (gpointer*)&decl, (gpointer*)&entries)) {
- /*
- * Iterate over the candidate methods, remove ones whose class is less concrete than the
- * class of another one.
- */
- /* This is O(n^2), but that shouldn't be a problem in practice */
- for (l = entries; l; l = l->next) {
- for (l2 = entries; l2; l2 = l2->next) {
- MonoMethod *m1 = (MonoMethod*)l->data;
- MonoMethod *m2 = (MonoMethod*)l2->data;
- if (!m1 || !m2 || m1 == m2)
- continue;
- if (mono_class_is_assignable_from_internal (m1->klass, m2->klass))
- l->data = NULL;
- else if (mono_class_is_assignable_from_internal (m2->klass, m1->klass))
- l2->data = NULL;
- }
- }
- int nentries = 0;
- MonoMethod *impl = NULL;
- for (l = entries; l; l = l->next) {
- if (l->data) {
- nentries ++;
- impl = (MonoMethod*)l->data;
- }
- }
- if (nentries > 1) {
- /* If more than one method is left, we have a conflict */
- if (decl->is_inflated)
- decl = ((MonoMethodInflated*)decl)->declaring;
- dim_conflicts = g_slist_prepend (dim_conflicts, decl);
- /*
- for (l = entries; l; l = l->next) {
- if (l->data)
- printf ("%s %s %s\n", mono_class_full_name (klass), mono_method_full_name (decl, TRUE), mono_method_full_name (l->data, TRUE));
- }
- */
- } else {
- /*
- * Use the implementing method computed above instead of the already
- * computed one, which depends on interface ordering.
- */
- int ic_offset = mono_class_interface_offset (klass, decl->klass);
- int im_slot = ic_offset + decl->slot;
- vtable [im_slot] = impl;
- }
- g_slist_free (entries);
- }
- if (dim_conflicts) {
- mono_loader_lock ();
- klass->has_dim_conflicts = 1;
- mono_loader_unlock ();
-
- /*
- * Exceptions are thrown at method call time and only for the methods which have
- * conflicts, so just save them in the class.
- */
-
- /* Make a copy of the list from the class mempool */
- GSList *conflicts = (GSList*)mono_class_alloc0 (klass, g_slist_length (dim_conflicts) * sizeof (GSList));
- int i = 0;
- for (l = dim_conflicts; l; l = l->next) {
- conflicts [i].data = l->data;
- conflicts [i].next = &conflicts [i + 1];
- i ++;
- }
- conflicts [i - 1].next = NULL;
-
- mono_class_set_dim_conflicts (klass, conflicts);
- g_slist_free (dim_conflicts);
- }
-}
-
-static void
-print_unimplemented_interface_method_info (MonoClass *klass, MonoClass *ic, MonoMethod *im, int im_slot, MonoMethod **overrides, int onum)
-{
- int index, mcount;
- char *method_signature;
- char *type_name;
-
- for (index = 0; index < onum; ++index) {
- mono_trace_warning (MONO_TRACE_TYPE, " at slot %d: %s (%d) overrides %s (%d)", im_slot, overrides [index*2+1]->name,
- overrides [index*2+1]->slot, overrides [index*2]->name, overrides [index*2]->slot);
- }
- method_signature = mono_signature_get_desc (mono_method_signature_internal (im), FALSE);
- type_name = mono_type_full_name (m_class_get_byval_arg (klass));
- mono_trace_warning (MONO_TRACE_TYPE, "no implementation for interface method %s::%s(%s) in class %s",
- mono_type_get_name (m_class_get_byval_arg (ic)), im->name, method_signature, type_name);
- g_free (method_signature);
- g_free (type_name);
- mono_class_setup_methods (klass);
- if (mono_class_has_failure (klass)) {
- char *name = mono_type_get_full_name (klass);
- mono_trace_warning (MONO_TRACE_TYPE, "CLASS %s failed to resolve methods", name);
- g_free (name);
- return;
- }
- mcount = mono_class_get_method_count (klass);
- for (index = 0; index < mcount; ++index) {
- MonoMethod *cm = klass->methods [index];
- method_signature = mono_signature_get_desc (mono_method_signature_internal (cm), TRUE);
-
- mono_trace_warning (MONO_TRACE_TYPE, "METHOD %s(%s)", cm->name, method_signature);
- g_free (method_signature);
- }
-}
-
-/*
- * mono_class_get_virtual_methods:
- *
- * Iterate over the virtual methods of KLASS.
- *
- * LOCKING: Assumes the loader lock is held (because of the klass->methods check).
- */
-static MonoMethod*
-mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter)
-{
- // FIXME move state to caller
- gboolean static_iter = FALSE;
-
- if (!iter)
- return NULL;
-
- /*
- * If the lowest bit of the iterator is 1, this is an iterator for static metadata,
- * and the upper bits contain an index. Otherwise, the iterator is a pointer into
- * klass->methods.
- */
- if ((gsize)(*iter) & 1)
- static_iter = TRUE;
- /* Use the static metadata only if klass->methods is not yet initialized */
- if (!static_iter && !(klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)))
- static_iter = TRUE;
-
- if (!static_iter) {
- MonoMethod** methodptr;
-
- if (!*iter) {
- mono_class_setup_methods (klass);
- /*
- * We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places.
- * FIXME we should better report this error to the caller
- */
- if (!klass->methods)
- return NULL;
- /* start from the first */
- methodptr = &klass->methods [0];
- } else {
- methodptr = (MonoMethod **)*iter;
- methodptr++;
- }
- if (*iter)
- g_assert ((guint64)(*iter) > 0x100);
- int mcount = mono_class_get_method_count (klass);
- while (methodptr < &klass->methods [mcount]) {
- if (*methodptr && ((*methodptr)->flags & METHOD_ATTRIBUTE_VIRTUAL))
- break;
- methodptr ++;
- }
- if (methodptr < &klass->methods [mcount]) {
- *iter = methodptr;
- return *methodptr;
- } else {
- return NULL;
- }
- } else {
- /* Search directly in metadata to avoid calling setup_methods () */
- MonoMethod *res = NULL;
- int i, start_index;
-
- if (!*iter) {
- start_index = 0;
- } else {
- start_index = GPOINTER_TO_UINT (*iter) >> 1;
- }
-
- int first_idx = mono_class_get_first_method_idx (klass);
- int mcount = mono_class_get_method_count (klass);
- for (i = start_index; i < mcount; ++i) {
- guint32 flags;
-
- /* first_idx points into the methodptr table */
- flags = mono_metadata_decode_table_row_col (klass->image, MONO_TABLE_METHOD, first_idx + i, MONO_METHOD_FLAGS);
-
- if (flags & METHOD_ATTRIBUTE_VIRTUAL)
- break;
- }
-
- if (i < mcount) {
- ERROR_DECL (error);
- res = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | (first_idx + i + 1), klass, NULL, error);
- mono_error_cleanup (error); /* FIXME don't swallow the error */
-
- /* Add 1 here so the if (*iter) check fails */
- *iter = GUINT_TO_POINTER (((i + 1) << 1) | 1);
- return res;
- } else {
- return NULL;
- }
- }
-}
-
-static void
-print_vtable_layout_result (MonoClass *klass, MonoMethod **vtable, int cur_slot)
-{
- int i, icount = 0;
-
- print_implemented_interfaces (klass);
-
- for (i = 0; i <= klass->max_interface_id; i++)
- if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i))
- icount++;
-
- printf ("VTable %s (vtable entries = %d, interfaces = %d)\n", mono_type_full_name (m_class_get_byval_arg (klass)),
- klass->vtable_size, icount);
-
- for (i = 0; i < cur_slot; ++i) {
- MonoMethod *cm;
-
- cm = vtable [i];
- if (cm) {
- printf (" slot assigned: %03d, slot index: %03d %s\n", i, cm->slot,
- mono_method_get_full_name (cm));
- } else {
- printf (" slot assigned: %03d, <null>\n", i);
- }
- }
-
-
- if (icount) {
- printf ("Interfaces %s.%s (max_iid = %d)\n", klass->name_space,
- klass->name, klass->max_interface_id);
-
- for (i = 0; i < klass->interface_count; i++) {
- MonoClass *ic = klass->interfaces [i];
- printf (" slot offset: %03d, method count: %03d, iid: %03d %s\n",
- mono_class_interface_offset (klass, ic),
- count_virtual_methods (ic), ic->interface_id, mono_type_full_name (m_class_get_byval_arg (ic)));
- }
-
- for (MonoClass *k = klass->parent; k ; k = k->parent) {
- for (i = 0; i < k->interface_count; i++) {
- MonoClass *ic = k->interfaces [i];
- printf (" parent slot offset: %03d, method count: %03d, iid: %03d %s\n",
- mono_class_interface_offset (klass, ic),
- count_virtual_methods (ic), ic->interface_id, mono_type_full_name (m_class_get_byval_arg (ic)));
- }
- }
- }
-}
-
-/*
- * LOCKING: this is supposed to be called with the loader lock held.
- */
-static int
-setup_class_vtsize (MonoClass *klass, GList *in_setup, int *cur_slot, int *stelemref_slot, MonoError *error)
-{
- GPtrArray *ifaces = NULL;
- int i, max_vtsize = 0;
- ifaces = mono_class_get_implemented_interfaces (klass, error);
- if (!is_ok (error)) {
- char *name = mono_type_get_full_name (klass);
- mono_class_set_type_load_failure (klass, "Could not resolve %s interfaces due to %s", name, mono_error_get_message (error));
- g_free (name);
- mono_error_cleanup (error);
- return -1;
- } else if (ifaces) {
- for (i = 0; i < ifaces->len; i++) {
- MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i);
- max_vtsize += mono_class_get_method_count (ic);
- }
- g_ptr_array_free (ifaces, TRUE);
- ifaces = NULL;
- }
-
- if (klass->parent) {
- mono_class_init_internal (klass->parent);
- mono_class_setup_vtable_full (klass->parent, in_setup);
-
- if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Parent class failed to load"))
- return -1;
-
- max_vtsize += klass->parent->vtable_size;
- *cur_slot = klass->parent->vtable_size;
- }
-
- max_vtsize += mono_class_get_method_count (klass);
-
- /*Array have a slot for stelemref*/
- if (mono_class_need_stelemref_method (klass)) {
- *stelemref_slot = *cur_slot;
- ++max_vtsize;
- ++*cur_slot;
- }
- return max_vtsize;
-}
-
-/*
- * LOCKING: this is supposed to be called with the loader lock held.
- */
-static void
-mono_class_setup_vtable_ginst (MonoClass *klass, GList *in_setup)
-{
- ERROR_DECL (error);
- int i;
- MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
- MonoMethod **tmp;
-
- mono_class_setup_vtable_full (gklass, in_setup);
- if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Could not load generic definition"))
- return;
-
- tmp = (MonoMethod **)mono_class_alloc0 (klass, sizeof (gpointer) * gklass->vtable_size);
- klass->vtable_size = gklass->vtable_size;
- for (i = 0; i < gklass->vtable_size; ++i)
- if (gklass->vtable [i]) {
- MonoMethod *inflated = mono_class_inflate_generic_method_full_checked (gklass->vtable [i], klass, mono_class_get_context (klass), error);
- if (!is_ok (error)) {
- char *name = mono_type_get_full_name (klass);
- mono_class_set_type_load_failure (klass, "VTable setup of type %s failed due to: %s", name, mono_error_get_message (error));
- mono_error_cleanup (error);
- g_free (name);
- return;
- }
- tmp [i] = inflated;
- tmp [i]->slot = gklass->vtable [i]->slot;
- }
- mono_memory_barrier ();
- klass->vtable = tmp;
-
- mono_loader_lock ();
- klass->has_dim_conflicts = gklass->has_dim_conflicts;
- mono_loader_unlock ();
-
- /* Have to set method->slot for abstract virtual methods */
- if (klass->methods && gklass->methods) {
- int mcount = mono_class_get_method_count (klass);
- for (i = 0; i < mcount; ++i)
- if (klass->methods [i]->slot == -1)
- klass->methods [i]->slot = gklass->methods [i]->slot;
- }
-
- if (mono_print_vtable)
- print_vtable_layout_result (klass, klass->vtable, gklass->vtable_size);
-
-}
-
-/*
- * LOCKING: this is supposed to be called with the loader lock held.
- */
-void
-mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int onum, GList *in_setup)
-{
- ERROR_DECL (error);
- MonoClass *k, *ic;
- MonoMethod **vtable = NULL;
- int i, max_vtsize = 0, cur_slot = 0;
- GHashTable *override_map = NULL;
- GHashTable *override_class_map = NULL;
- GHashTable *conflict_map = NULL;
- MonoMethod *cm;
-#if (DEBUG_INTERFACE_VTABLE_CODE|TRACE_INTERFACE_VTABLE_CODE)
- int first_non_interface_slot;
-#endif
- GSList *virt_methods = NULL, *l;
- int stelemref_slot = 0;
-
- if (klass->vtable)
- return;
-
- if (overrides && !verify_class_overrides (klass, overrides, onum))
- return;
-
- max_vtsize = setup_class_vtsize (klass, in_setup, &cur_slot, &stelemref_slot, error);
- if (max_vtsize == -1)
- return;
-
- cur_slot = setup_interface_offsets (klass, cur_slot, TRUE);
- if (cur_slot == -1) /*setup_interface_offsets fails the type.*/
- return;
-
- DEBUG_INTERFACE_VTABLE (first_non_interface_slot = cur_slot);
-
- /* Optimized version for generic instances */
- if (mono_class_is_ginst (klass)) {
- mono_class_setup_vtable_ginst (klass, in_setup);
- return;
- }
-
- vtable = (MonoMethod **)g_malloc0 (sizeof (gpointer) * max_vtsize);
-
- if (klass->parent && klass->parent->vtable_size)
- memcpy (vtable, klass->parent->vtable, sizeof (gpointer) * klass->parent->vtable_size);
-
- /*Array have a slot for stelemref*/
- if (mono_class_need_stelemref_method (klass)) {
- MonoMethod *method = mono_marshal_get_virtual_stelemref (klass);
- if (!method->slot)
- method->slot = stelemref_slot;
- else
- g_assert (method->slot == stelemref_slot);
-
- vtable [stelemref_slot] = method;
- }
-
- TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER INHERITING PARENT VTABLE", TRUE));
-
- /* Process overrides from interface default methods */
- // FIXME: Ordering between interfaces
- for (int ifindex = 0; ifindex < klass->interface_offsets_count; ifindex++) {
- ic = klass->interfaces_packed [ifindex];
-
- mono_class_setup_methods (ic);
- if (mono_class_has_failure (ic))
- goto fail;
-
- MonoMethod **iface_overrides;
- int iface_onum;
- mono_class_get_overrides_full (ic->image, ic->type_token, &iface_overrides, &iface_onum, mono_class_get_context (ic), error);
- goto_if_nok (error, fail);
- 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) {
- 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;
- }
- g_free (iface_overrides);
- }
-
- /* override interface methods */
- for (i = 0; i < onum; i++) {
- MonoMethod *decl = overrides [i*2];
- MonoMethod *override = overrides [i*2 + 1];
- if (MONO_CLASS_IS_INTERFACE_INTERNAL (decl->klass)) {
- if (!apply_override (klass, klass, vtable, decl, override, &override_map, &override_class_map, &conflict_map))
- goto fail;
- }
- }
-
- TRACE_INTERFACE_VTABLE (print_overrides (override_map, "AFTER OVERRIDING INTERFACE METHODS"));
- TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER OVERRIDING INTERFACE METHODS", FALSE));
-
- /*
- * Create a list of virtual methods to avoid calling
- * mono_class_get_virtual_methods () which is slow because of the metadata
- * optimization.
- */
- {
- gpointer iter = NULL;
- MonoMethod *cm;
-
- virt_methods = NULL;
- while ((cm = mono_class_get_virtual_methods (klass, &iter))) {
- virt_methods = g_slist_prepend (virt_methods, cm);
- }
- if (mono_class_has_failure (klass))
- goto fail;
- }
-
- // Loop on all implemented interfaces...
- for (i = 0; i < klass->interface_offsets_count; i++) {
- MonoClass *parent = klass->parent;
- int ic_offset;
- gboolean interface_is_explicitly_implemented_by_class;
- int im_index;
-
- ic = klass->interfaces_packed [i];
- ic_offset = mono_class_interface_offset (klass, ic);
-
- mono_class_setup_methods (ic);
- if (mono_class_has_failure (ic))
- goto fail;
-
- // Check if this interface is explicitly implemented (instead of just inherited)
- if (parent != NULL) {
- int implemented_interfaces_index;
- interface_is_explicitly_implemented_by_class = FALSE;
- for (implemented_interfaces_index = 0; implemented_interfaces_index < klass->interface_count; implemented_interfaces_index++) {
- if (ic == klass->interfaces [implemented_interfaces_index]) {
- interface_is_explicitly_implemented_by_class = TRUE;
- break;
- }
- }
- } else {
- interface_is_explicitly_implemented_by_class = TRUE;
- }
-
- // Loop on all interface methods...
- int mcount = mono_class_get_method_count (ic);
- for (im_index = 0; im_index < mcount; im_index++) {
- 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;
-
- if (im->flags & METHOD_ATTRIBUTE_STATIC)
- continue;
-
- TRACE_INTERFACE_VTABLE (printf ("\tchecking iface method %s\n", mono_method_full_name (im,1)));
-
- 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;
- /* Why do we need this? */
- if (cm->slot < 0) {
- cm->slot = im_slot;
- }
- if (conflict_map)
- g_hash_table_remove(conflict_map, im);
- break;
- }
- 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--) {
- 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;
- }
- }
- } else {
- g_assert (vtable [im_slot] == override_im);
- }
- }
- }
-
- // If the class is not abstract, check that all its interface slots are full.
- // The check is done here and not directly at the end of the loop above because
- // 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;
- int im_index;
-
- ic = klass->interfaces_packed [i];
- ic_offset = mono_class_interface_offset (klass, ic);
-
- int mcount = mono_class_get_method_count (ic);
- for (im_index = 0; im_index < mcount; im_index++) {
- MonoMethod *im = ic->methods [im_index];
- int im_slot = ic_offset + im->slot;
-
- 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)));
- if (vtable [im_slot] == NULL) {
- print_unimplemented_interface_method_info (klass, ic, im, im_slot, overrides, onum);
- goto fail;
- }
- }
- }
- }
-
- TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER SETTING UP INTERFACE METHODS", FALSE));
- for (l = virt_methods; l; l = l->next) {
- cm = (MonoMethod *)l->data;
- /*
- * If the method is REUSE_SLOT, we must check in the
- * base class for a method to override.
- */
- if (!(cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) {
- int slot = -1;
- for (k = klass->parent; k ; k = k->parent) {
- gpointer k_iter;
- MonoMethod *m1;
-
- k_iter = NULL;
- while ((m1 = mono_class_get_virtual_methods (k, &k_iter))) {
- MonoMethodSignature *cmsig, *m1sig;
-
- cmsig = mono_method_signature_internal (cm);
- m1sig = mono_method_signature_internal (m1);
-
- if (!cmsig || !m1sig) /* FIXME proper error message, use signature_checked? */
- goto fail;
-
- if (!strcmp(cm->name, m1->name) &&
- mono_metadata_signature_equal (cmsig, m1sig)) {
-
- if (mono_security_core_clr_enabled ())
- mono_security_core_clr_check_override (klass, cm, m1);
-
- slot = mono_method_get_vtable_slot (m1);
- if (slot == -1)
- goto fail;
-
- if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, m1, NULL)) {
- char *body_name = mono_method_full_name (cm, TRUE);
- char *decl_name = mono_method_full_name (m1, TRUE);
- mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
- g_free (body_name);
- g_free (decl_name);
- goto fail;
- }
-
- g_assert (cm->slot < max_vtsize);
- if (!override_map)
- override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
- TRACE_INTERFACE_VTABLE (printf ("adding base class override from %s [%p] to %s [%p]\n",
- mono_method_full_name (m1, 1), m1,
- mono_method_full_name (cm, 1), cm));
- g_hash_table_insert (override_map, m1, cm);
- break;
- }
- }
- if (mono_class_has_failure (k))
- goto fail;
-
- if (slot >= 0)
- break;
- }
- if (slot >= 0)
- cm->slot = slot;
- }
-
- /*Non final newslot methods must be given a non-interface vtable slot*/
- if ((cm->flags & METHOD_ATTRIBUTE_NEW_SLOT) && !(cm->flags & METHOD_ATTRIBUTE_FINAL) && cm->slot >= 0)
- cm->slot = -1;
-
- if (cm->slot < 0)
- cm->slot = cur_slot++;
-
- if (!(cm->flags & METHOD_ATTRIBUTE_ABSTRACT))
- vtable [cm->slot] = cm;
- }
-
- /* override non interface methods */
- for (i = 0; i < onum; i++) {
- MonoMethod *decl = overrides [i*2];
- if (!MONO_CLASS_IS_INTERFACE_INTERNAL (decl->klass)) {
- g_assert (decl->slot != -1);
- vtable [decl->slot] = overrides [i*2 + 1];
- overrides [i * 2 + 1]->slot = decl->slot;
- if (!override_map)
- override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
- TRACE_INTERFACE_VTABLE (printf ("adding explicit override from %s [%p] to %s [%p]\n",
- mono_method_full_name (decl, 1), decl,
- mono_method_full_name (overrides [i * 2 + 1], 1), overrides [i * 2 + 1]));
- g_hash_table_insert (override_map, decl, overrides [i * 2 + 1]);
-
- if (mono_security_core_clr_enabled ())
- mono_security_core_clr_check_override (klass, vtable [decl->slot], decl);
- }
- }
-
- /*
- * If a method occupies more than one place in the vtable, and it is
- * overriden, then change the other occurrences too.
- */
- if (override_map) {
- MonoMethod *cm;
-
- for (i = 0; i < max_vtsize; ++i)
- if (vtable [i]) {
- TRACE_INTERFACE_VTABLE (printf ("checking slot %d method %s[%p] for overrides\n", i, mono_method_full_name (vtable [i], 1), vtable [i]));
-
- cm = (MonoMethod *)g_hash_table_lookup (override_map, vtable [i]);
- if (cm)
- vtable [i] = cm;
- }
-
- g_hash_table_destroy (override_map);
- override_map = NULL;
- }
-
- if (override_class_map)
- g_hash_table_destroy (override_class_map);
-
- if (conflict_map) {
- handle_dim_conflicts (vtable, klass, conflict_map);
- g_hash_table_destroy (conflict_map);
- }
-
- g_slist_free (virt_methods);
- virt_methods = NULL;
-
- 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] != NULL && vtable [i]->is_reabstracted == 1)
- 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);
- g_free (type_name);
- g_free (method_name);
-
- if (mono_print_vtable)
- print_vtable_layout_result (klass, vtable, cur_slot);
-
- g_free (vtable);
- return;
- }
- }
- }
-
- if (mono_class_is_ginst (klass)) {
- MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
-
- mono_class_init_internal (gklass);
-
- klass->vtable_size = MAX (gklass->vtable_size, cur_slot);
- } else {
- /* Check that the vtable_size value computed in mono_class_init_internal () is correct */
- if (klass->vtable_size)
- g_assert (cur_slot == klass->vtable_size);
- klass->vtable_size = cur_slot;
- }
-
- /* Try to share the vtable with our parent. */
- if (klass->parent && (klass->parent->vtable_size == klass->vtable_size) && (memcmp (klass->parent->vtable, vtable, sizeof (gpointer) * klass->vtable_size) == 0)) {
- mono_memory_barrier ();
- klass->vtable = klass->parent->vtable;
- } else {
- MonoMethod **tmp = (MonoMethod **)mono_class_alloc0 (klass, sizeof (gpointer) * klass->vtable_size);
- memcpy (tmp, vtable, sizeof (gpointer) * klass->vtable_size);
- mono_memory_barrier ();
- klass->vtable = tmp;
- }
-
- DEBUG_INTERFACE_VTABLE (print_vtable_full (klass, klass->vtable, klass->vtable_size, first_non_interface_slot, "FINALLY", FALSE));
- if (mono_print_vtable)
- print_vtable_layout_result (klass, vtable, cur_slot);
-
- g_free (vtable);
-
- VERIFY_INTERFACE_VTABLE (mono_class_verify_vtable (klass));
- return;
-
-fail:
- {
- char *name = mono_type_get_full_name (klass);
- if (!is_ok (error))
- mono_class_set_type_load_failure (klass, "VTable setup of type %s failed due to: %s", name, mono_error_get_message (error));
- else
- mono_class_set_type_load_failure (klass, "VTable setup of type %s failed", name);
- mono_error_cleanup (error);
- g_free (name);
- if (mono_print_vtable)
- print_vtable_layout_result (klass, vtable, cur_slot);
-
-
- g_free (vtable);
- if (override_map)
- g_hash_table_destroy (override_map);
- if (virt_methods)
- g_slist_free (virt_methods);
- }
-}
-
static char*
concat_two_strings_with_zero (MonoImage *image, const char *s1, const char *s2)
{
@@ -4498,9 +2770,8 @@ mono_class_init_internal (MonoClass *klass)
if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic Type Definition failed to init"))
goto leave;
- mono_loader_lock ();
- mono_class_setup_interface_id_internal (klass);
- mono_loader_unlock ();
+ if (MONO_CLASS_IS_INTERFACE_INTERNAL (klass))
+ mono_class_setup_interface_id (klass);
}
if (klass->parent && !klass->parent->inited)
@@ -4614,7 +2885,7 @@ mono_class_init_internal (MonoClass *klass)
goto leave;
g_assert (klass->parent->vtable_size);
first_iface_slot = klass->parent->vtable_size;
- if (mono_class_need_stelemref_method (klass))
+ if (mono_class_setup_need_stelemref_method (klass))
++first_iface_slot;
}
@@ -4650,7 +2921,7 @@ mono_class_init_internal (MonoClass *klass)
mono_loader_unlock ();
locked = FALSE;
- setup_interface_offsets (klass, first_iface_slot, TRUE);
+ mono_class_setup_interface_offsets_internal (klass, first_iface_slot, TRUE);
if (mono_security_core_clr_enabled ())
mono_security_core_clr_check_inheritance (klass);
@@ -4804,8 +3075,8 @@ mono_class_setup_parent (MonoClass *klass, MonoClass *parent)
}
/* Locking: must be called with the loader lock held. */
-static void
-mono_class_setup_interface_id_internal (MonoClass *klass)
+void
+mono_class_setup_interface_id_nolock (MonoClass *klass)
{
if (!MONO_CLASS_IS_INTERFACE_INTERNAL (klass) || klass->interface_id)
return;
@@ -4951,7 +3222,7 @@ mono_class_setup_mono_type (MonoClass *klass)
klass->this_arg.type = (MonoTypeEnum)t;
}
- mono_class_setup_interface_id_internal (klass);
+ mono_class_setup_interface_id_nolock (klass);
}
static MonoMethod*
@@ -5418,7 +3689,7 @@ mono_class_setup_interface_id (MonoClass *klass)
{
g_assert (MONO_CLASS_IS_INTERFACE_INTERNAL (klass));
mono_loader_lock ();
- mono_class_setup_interface_id_internal (klass);
+ mono_class_setup_interface_id_nolock (klass);
mono_loader_unlock ();
}
diff --git a/mono/metadata/class-internals.h b/mono/metadata/class-internals.h
index 8794fee5d92..05963326b1b 100644
--- a/mono/metadata/class-internals.h
+++ b/mono/metadata/class-internals.h
@@ -75,7 +75,8 @@ struct _MonoMethod {
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;
+ unsigned int is_covariant_override_impl:1; /* whether this is an override with a signature different from its declared method */
+ signed int slot : 15;
/*
* If is_generic is TRUE, the generic_container is stored in image->property_hash,
@@ -1248,6 +1249,9 @@ mono_class_vtable_checked (MonoDomain *domain, MonoClass *klass, MonoError *erro
void
mono_class_is_assignable_from_checked (MonoClass *klass, MonoClass *oklass, gboolean *result, MonoError *error);
+void
+mono_class_signature_is_assignable_from (MonoClass *klass, MonoClass *oklass, gboolean *result, MonoError *error);
+
gboolean
mono_class_is_assignable_from_slow (MonoClass *target, MonoClass *candidate);
@@ -1263,6 +1267,9 @@ mono_class_is_subclass_of_internal (MonoClass *klass, MonoClass *klassc, gboolea
mono_bool
mono_class_is_assignable_from_internal (MonoClass *klass, MonoClass *oklass);
+gboolean
+mono_byref_type_is_assignable_from (MonoType *type, MonoType *ctype, gboolean signature_assignment);
+
gboolean mono_is_corlib_image (MonoImage *image);
MonoType*
diff --git a/mono/metadata/class-setup-vtable.c b/mono/metadata/class-setup-vtable.c
new file mode 100644
index 00000000000..91f1c5d4045
--- /dev/null
+++ b/mono/metadata/class-setup-vtable.c
@@ -0,0 +1,2157 @@
+/**
+ * \file MonoClass vtable setup
+ *
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
+ * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
+ * Copyright 2018 Microsoft
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+#include <config.h>
+#include <mono/metadata/class-init.h>
+#include <mono/metadata/class-init-internals.h>
+#include <mono/metadata/class-internals.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/security-core-clr.h>
+#include <mono/metadata/security-manager.h>
+#include <mono/utils/mono-logger-internals.h>
+#include <mono/utils/unlocked.h>
+#ifdef MONO_CLASS_DEF_PRIVATE
+/* Class initialization gets to see the fields of MonoClass */
+#define REALLY_INCLUDE_CLASS_DEF 1
+#include <mono/metadata/class-private-definition.h>
+#undef REALLY_INCLUDE_CLASS_DEF
+#endif
+
+#ifdef ENABLE_NETCORE
+#define FEATURE_COVARIANT_RETURNS
+#endif
+
+static void mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup);
+
+static void
+print_implemented_interfaces (MonoClass *klass)
+{
+ char *name;
+ ERROR_DECL (error);
+ GPtrArray *ifaces = NULL;
+ int i;
+ int ancestor_level = 0;
+
+ name = mono_type_get_full_name (klass);
+ printf ("Packed interface table for class %s has size %d\n", name, klass->interface_offsets_count);
+ g_free (name);
+
+ for (i = 0; i < klass->interface_offsets_count; i++) {
+ char *ic_name = mono_type_get_full_name (klass->interfaces_packed [i]);
+ printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s\n", i,
+ klass->interfaces_packed [i]->interface_id,
+ klass->interface_offsets_packed [i],
+ mono_class_get_method_count (klass->interfaces_packed [i]),
+ ic_name);
+ g_free (ic_name);
+ }
+ printf ("Interface flags: ");
+ for (i = 0; i <= klass->max_interface_id; i++)
+ if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i))
+ printf ("(%d,T)", i);
+ else
+ printf ("(%d,F)", i);
+ printf ("\n");
+ printf ("Dump interface flags:");
+#ifdef COMPRESSED_INTERFACE_BITMAP
+ {
+ const uint8_t* p = klass->interface_bitmap;
+ i = klass->max_interface_id;
+ while (i > 0) {
+ printf (" %d x 00 %02X", p [0], p [1]);
+ i -= p [0] * 8;
+ i -= 8;
+ }
+ }
+#else
+ for (i = 0; i < ((((klass->max_interface_id + 1) >> 3)) + (((klass->max_interface_id + 1) & 7)? 1 :0)); i++)
+ printf (" %02X", klass->interface_bitmap [i]);
+#endif
+ printf ("\n");
+ while (klass != NULL) {
+ printf ("[LEVEL %d] Implemented interfaces by class %s:\n", ancestor_level, klass->name);
+ ifaces = mono_class_get_implemented_interfaces (klass, error);
+ if (!is_ok (error)) {
+ printf (" Type failed due to %s\n", mono_error_get_message (error));
+ mono_error_cleanup (error);
+ } else if (ifaces) {
+ for (i = 0; i < ifaces->len; i++) {
+ MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i);
+ printf (" [UIID %d] interface %s\n", ic->interface_id, ic->name);
+ printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i,
+ ic->interface_id,
+ mono_class_interface_offset (klass, ic),
+ mono_class_get_method_count (ic),
+ ic->name_space,
+ ic->name );
+ }
+ g_ptr_array_free (ifaces, TRUE);
+ }
+ ancestor_level ++;
+ klass = klass->parent;
+ }
+}
+
+static mono_bool
+set_interface_and_offset (int num_ifaces, MonoClass **interfaces_full, int *interface_offsets_full, MonoClass *ic, int offset, mono_bool force_set)
+{
+ int i;
+ for (i = 0; i < num_ifaces; ++i) {
+ if (interfaces_full [i] && interfaces_full [i]->interface_id == ic->interface_id) {
+ if (!force_set)
+ return TRUE;
+ interface_offsets_full [i] = offset;
+ return FALSE;
+ }
+ if (interfaces_full [i])
+ continue;
+ interfaces_full [i] = ic;
+ interface_offsets_full [i] = offset;
+ break;
+ }
+ return FALSE;
+}
+
+/**
+ * mono_class_setup_interface_offsets_internal:
+ *
+ * Do not call this function outside of class creation.
+ *
+ * Return -1 on failure and set klass->has_failure and store a MonoErrorBoxed with the details.
+ * LOCKING: Acquires the loader lock.
+ */
+int
+mono_class_setup_interface_offsets_internal (MonoClass *klass, int cur_slot, gboolean overwrite)
+{
+ ERROR_DECL (error);
+ MonoClass *k, *ic;
+ int i, j, num_ifaces;
+ guint32 max_iid;
+ MonoClass **interfaces_full = NULL;
+ int *interface_offsets_full = NULL;
+ GPtrArray *ifaces;
+ GPtrArray **ifaces_array = NULL;
+ int interface_offsets_count;
+ max_iid = 0;
+ num_ifaces = interface_offsets_count = 0;
+
+ mono_loader_lock ();
+
+ mono_class_setup_supertypes (klass);
+
+ if (mono_class_is_ginst (klass)) {
+ MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
+
+ interface_offsets_count = num_ifaces = gklass->interface_offsets_count;
+ interfaces_full = (MonoClass **)g_malloc (sizeof (MonoClass*) * num_ifaces);
+ interface_offsets_full = (int *)g_malloc (sizeof (int) * num_ifaces);
+
+ cur_slot = 0;
+ for (int i = 0; i < num_ifaces; ++i) {
+ MonoClass *gklass_ic = gklass->interfaces_packed [i];
+ MonoClass *inflated = mono_class_inflate_generic_class_checked (gklass_ic, mono_class_get_context(klass), error);
+ if (!is_ok (error)) {
+ char *name = mono_type_get_full_name (gklass_ic);
+ mono_class_set_type_load_failure (klass, "Error calculating interface offset of %s", name);
+ g_free (name);
+ cur_slot = -1;
+ goto end;
+ }
+
+ mono_class_setup_interface_id_nolock (inflated);
+
+ interfaces_full [i] = inflated;
+ interface_offsets_full [i] = gklass->interface_offsets_packed [i];
+
+ int count = mono_class_setup_count_virtual_methods (inflated);
+ if (count == -1) {
+ char *name = mono_type_get_full_name (inflated);
+ mono_class_set_type_load_failure (klass, "Error calculating interface offset of %s", name);
+ g_free (name);
+ cur_slot = -1;
+ goto end;
+ }
+
+ cur_slot = MAX (cur_slot, interface_offsets_full [i] + count);
+ max_iid = MAX (max_iid, inflated->interface_id);
+ }
+
+ goto publish;
+ }
+ /* compute maximum number of slots and maximum interface id */
+ max_iid = 0;
+ num_ifaces = 0; /* this can include duplicated ones */
+ ifaces_array = g_new0 (GPtrArray *, klass->idepth);
+ for (j = 0; j < klass->idepth; j++) {
+ k = klass->supertypes [j];
+ g_assert (k);
+ num_ifaces += k->interface_count;
+ for (i = 0; i < k->interface_count; i++) {
+ ic = k->interfaces [i];
+
+ /* A gparam does not have any interface_id set. */
+ if (! mono_class_is_gparam (ic))
+ mono_class_setup_interface_id_nolock (ic);
+
+ if (max_iid < ic->interface_id)
+ max_iid = ic->interface_id;
+ }
+ ifaces = mono_class_get_implemented_interfaces (k, error);
+ if (!is_ok (error)) {
+ char *name = mono_type_get_full_name (k);
+ mono_class_set_type_load_failure (klass, "Error getting the interfaces of %s due to %s", name, mono_error_get_message (error));
+ g_free (name);
+ mono_error_cleanup (error);
+ cur_slot = -1;
+ goto end;
+ }
+ if (ifaces) {
+ num_ifaces += ifaces->len;
+ for (i = 0; i < ifaces->len; ++i) {
+ ic = (MonoClass *)g_ptr_array_index (ifaces, i);
+ if (max_iid < ic->interface_id)
+ max_iid = ic->interface_id;
+ }
+ ifaces_array [j] = ifaces;
+ }
+ }
+
+ if (MONO_CLASS_IS_INTERFACE_INTERNAL (klass)) {
+ num_ifaces++;
+ if (max_iid < klass->interface_id)
+ max_iid = klass->interface_id;
+ }
+
+ /* compute vtable offset for interfaces */
+ interfaces_full = (MonoClass **)g_malloc0 (sizeof (MonoClass*) * num_ifaces);
+ interface_offsets_full = (int *)g_malloc (sizeof (int) * num_ifaces);
+
+ for (i = 0; i < num_ifaces; i++)
+ interface_offsets_full [i] = -1;
+
+ /* skip the current class */
+ for (j = 0; j < klass->idepth - 1; j++) {
+ k = klass->supertypes [j];
+ ifaces = ifaces_array [j];
+
+ if (ifaces) {
+ for (i = 0; i < ifaces->len; ++i) {
+ int io;
+ ic = (MonoClass *)g_ptr_array_index (ifaces, i);
+
+ /*Force the sharing of interface offsets between parent and subtypes.*/
+ io = mono_class_interface_offset (k, ic);
+ g_assertf (io >= 0, "class %s parent %s has no offset for iface %s",
+ mono_type_get_full_name (klass),
+ mono_type_get_full_name (k),
+ mono_type_get_full_name (ic));
+
+ set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, io, TRUE);
+ }
+ }
+ }
+
+ g_assert (klass == klass->supertypes [klass->idepth - 1]);
+ ifaces = ifaces_array [klass->idepth - 1];
+ if (ifaces) {
+ for (i = 0; i < ifaces->len; ++i) {
+ int count;
+ ic = (MonoClass *)g_ptr_array_index (ifaces, i);
+ if (set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, ic, cur_slot, FALSE))
+ continue;
+ count = mono_class_setup_count_virtual_methods (ic);
+ if (count == -1) {
+ char *name = mono_type_get_full_name (ic);
+ mono_class_set_type_load_failure (klass, "Error calculating interface offset of %s", name);
+ g_free (name);
+ cur_slot = -1;
+ goto end;
+ }
+ cur_slot += count;
+ }
+ }
+
+ if (MONO_CLASS_IS_INTERFACE_INTERNAL (klass))
+ set_interface_and_offset (num_ifaces, interfaces_full, interface_offsets_full, klass, cur_slot, TRUE);
+
+ for (interface_offsets_count = 0, i = 0; i < num_ifaces; i++) {
+ if (interface_offsets_full [i] != -1)
+ interface_offsets_count ++;
+ }
+
+publish:
+ /* Publish the data */
+ klass->max_interface_id = max_iid;
+ /*
+ * We might get called multiple times:
+ * - mono_class_init_internal ()
+ * - mono_class_setup_vtable ().
+ * - mono_class_setup_interface_offsets ().
+ * mono_class_setup_interface_offsets () passes 0 as CUR_SLOT, so the computed interface offsets will be invalid. This
+ * means we have to overwrite those when called from other places (#4440).
+ */
+ if (klass->interfaces_packed) {
+ if (!overwrite)
+ g_assert (klass->interface_offsets_count == interface_offsets_count);
+ } else {
+ uint8_t *bitmap;
+ int bsize;
+ klass->interface_offsets_count = interface_offsets_count;
+ klass->interfaces_packed = (MonoClass **)mono_class_alloc (klass, sizeof (MonoClass*) * interface_offsets_count);
+ klass->interface_offsets_packed = (guint16 *)mono_class_alloc (klass, sizeof (guint16) * interface_offsets_count);
+ bsize = (sizeof (guint8) * ((max_iid + 1) >> 3)) + (((max_iid + 1) & 7)? 1 :0);
+#ifdef COMPRESSED_INTERFACE_BITMAP
+ bitmap = g_malloc0 (bsize);
+#else
+ bitmap = (uint8_t *)mono_class_alloc0 (klass, bsize);
+#endif
+ for (i = 0; i < interface_offsets_count; i++) {
+ guint32 id = interfaces_full [i]->interface_id;
+ bitmap [id >> 3] |= (1 << (id & 7));
+ klass->interfaces_packed [i] = interfaces_full [i];
+ klass->interface_offsets_packed [i] = interface_offsets_full [i];
+ }
+#ifdef COMPRESSED_INTERFACE_BITMAP
+ i = mono_compress_bitmap (NULL, bitmap, bsize);
+ klass->interface_bitmap = mono_class_alloc0 (klass, i);
+ mono_compress_bitmap (klass->interface_bitmap, bitmap, bsize);
+ g_free (bitmap);
+#else
+ klass->interface_bitmap = bitmap;
+#endif
+ }
+end:
+ mono_loader_unlock ();
+
+ g_free (interfaces_full);
+ g_free (interface_offsets_full);
+ if (ifaces_array) {
+ for (i = 0; i < klass->idepth; i++) {
+ ifaces = ifaces_array [i];
+ if (ifaces)
+ g_ptr_array_free (ifaces, TRUE);
+ }
+ g_free (ifaces_array);
+ }
+
+ //printf ("JUST DONE: ");
+ //print_implemented_interfaces (klass);
+
+ return cur_slot;
+}
+
+/*
+ * Setup interface offsets for interfaces.
+ * Initializes:
+ * - klass->max_interface_id
+ * - klass->interface_offsets_count
+ * - klass->interfaces_packed
+ * - klass->interface_offsets_packed
+ * - klass->interface_bitmap
+ *
+ * This function can fail @class.
+ *
+ */
+void
+mono_class_setup_interface_offsets (MonoClass *klass)
+{
+ /* NOTE: This function is only correct for interfaces.
+ *
+ * It assumes that klass's interfaces can be assigned offsets starting
+ * from 0. That assumption is incorrect for classes and valuetypes.
+ */
+ g_assert (MONO_CLASS_IS_INTERFACE_INTERNAL (klass) && !mono_class_is_ginst (klass));
+ mono_class_setup_interface_offsets_internal (klass, 0, FALSE);
+}
+
+
+#define DEBUG_INTERFACE_VTABLE_CODE 0
+#define TRACE_INTERFACE_VTABLE_CODE 0
+#define VERIFY_INTERFACE_VTABLE_CODE 0
+#define VTABLE_SELECTOR (1)
+
+#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE)
+#define DEBUG_INTERFACE_VTABLE(stmt) do {\
+ if (!(VTABLE_SELECTOR)) break; \
+ stmt;\
+} while (0)
+#else
+#define DEBUG_INTERFACE_VTABLE(stmt)
+#endif
+
+#if TRACE_INTERFACE_VTABLE_CODE
+#define TRACE_INTERFACE_VTABLE(stmt) do {\
+ if (!(VTABLE_SELECTOR)) break; \
+ stmt;\
+} while (0)
+#else
+#define TRACE_INTERFACE_VTABLE(stmt)
+#endif
+
+#if VERIFY_INTERFACE_VTABLE_CODE
+#define VERIFY_INTERFACE_VTABLE(stmt) do {\
+ if (!(VTABLE_SELECTOR)) break; \
+ stmt;\
+} while (0)
+#else
+#define VERIFY_INTERFACE_VTABLE(stmt)
+#endif
+
+
+#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE)
+static char*
+mono_signature_get_full_desc (MonoMethodSignature *sig, gboolean include_namespace)
+{
+ int i;
+ char *result;
+ GString *res = g_string_new ("");
+
+ g_string_append_c (res, '(');
+ for (i = 0; i < sig->param_count; ++i) {
+ if (i > 0)
+ g_string_append_c (res, ',');
+ mono_type_get_desc (res, sig->params [i], include_namespace);
+ }
+ g_string_append (res, ")=>");
+ if (sig->ret != NULL) {
+ mono_type_get_desc (res, sig->ret, include_namespace);
+ } else {
+ g_string_append (res, "NULL");
+ }
+ result = res->str;
+ g_string_free (res, FALSE);
+ return result;
+}
+static void
+print_method_signatures (MonoMethod *im, MonoMethod *cm) {
+ char *im_sig = mono_signature_get_full_desc (mono_method_signature_internal (im), TRUE);
+ char *cm_sig = mono_signature_get_full_desc (mono_method_signature_internal (cm), TRUE);
+ printf ("(IM \"%s\", CM \"%s\")", im_sig, cm_sig);
+ g_free (im_sig);
+ g_free (cm_sig);
+
+}
+
+#endif
+
+static gboolean
+is_wcf_hack_disabled (void)
+{
+ static char disabled;
+ if (!disabled)
+ disabled = g_hasenv ("MONO_DISABLE_WCF_HACK") ? 1 : 2;
+ return disabled == 1;
+}
+
+static gboolean
+check_interface_method_override (MonoClass *klass, MonoMethod *im, MonoMethod *cm, gboolean require_newslot, gboolean interface_is_explicitly_implemented_by_class, gboolean slot_is_empty)
+{
+ MonoMethodSignature *cmsig, *imsig;
+ if (strcmp (im->name, cm->name) == 0) {
+ if (! (cm->flags & METHOD_ATTRIBUTE_PUBLIC)) {
+ TRACE_INTERFACE_VTABLE (printf ("[PUBLIC CHECK FAILED]"));
+ return FALSE;
+ }
+ if (! slot_is_empty) {
+ if (require_newslot) {
+ if (! interface_is_explicitly_implemented_by_class) {
+ TRACE_INTERFACE_VTABLE (printf ("[NOT EXPLICIT IMPLEMENTATION IN FULL SLOT REFUSED]"));
+ return FALSE;
+ }
+ if (! (cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) {
+ TRACE_INTERFACE_VTABLE (printf ("[NEWSLOT CHECK FAILED]"));
+ return FALSE;
+ }
+ } else {
+ TRACE_INTERFACE_VTABLE (printf ("[FULL SLOT REFUSED]"));
+ }
+ }
+ cmsig = mono_method_signature_internal (cm);
+ imsig = mono_method_signature_internal (im);
+ if (!cmsig || !imsig) {
+ mono_class_set_type_load_failure (klass, "Could not resolve the signature of a virtual method");
+ return FALSE;
+ }
+
+ if (! mono_metadata_signature_equal (cmsig, imsig)) {
+ TRACE_INTERFACE_VTABLE (printf ("[SIGNATURE CHECK FAILED "));
+ TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm));
+ TRACE_INTERFACE_VTABLE (printf ("]"));
+ return FALSE;
+ }
+ TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS]"));
+ if (mono_security_core_clr_enabled ())
+ mono_security_core_clr_check_override (klass, cm, im);
+
+ TRACE_INTERFACE_VTABLE (printf ("[NAME CHECK OK]"));
+ if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) {
+ char *body_name = mono_method_full_name (cm, TRUE);
+ char *decl_name = mono_method_full_name (im, TRUE);
+ mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
+ g_free (body_name);
+ g_free (decl_name);
+ return FALSE;
+ }
+
+ return TRUE;
+ } else {
+ MonoClass *ic = im->klass;
+ const char *ic_name_space = ic->name_space;
+ const char *ic_name = ic->name;
+ char *subname;
+
+ if (! require_newslot) {
+ TRACE_INTERFACE_VTABLE (printf ("[INJECTED METHOD REFUSED]"));
+ return FALSE;
+ }
+ if (cm->klass->rank == 0) {
+ TRACE_INTERFACE_VTABLE (printf ("[RANK CHECK FAILED]"));
+ return FALSE;
+ }
+ cmsig = mono_method_signature_internal (cm);
+ imsig = mono_method_signature_internal (im);
+ if (!cmsig || !imsig) {
+ mono_class_set_type_load_failure (klass, "Could not resolve the signature of a virtual method");
+ return FALSE;
+ }
+
+ if (! mono_metadata_signature_equal (cmsig, imsig)) {
+ TRACE_INTERFACE_VTABLE (printf ("[(INJECTED) SIGNATURE CHECK FAILED "));
+ TRACE_INTERFACE_VTABLE (print_method_signatures (im, cm));
+ TRACE_INTERFACE_VTABLE (printf ("]"));
+ return FALSE;
+ }
+ if (mono_class_get_image (ic) != mono_defaults.corlib) {
+ TRACE_INTERFACE_VTABLE (printf ("[INTERFACE CORLIB CHECK FAILED]"));
+ return FALSE;
+ }
+ if ((ic_name_space == NULL) || (strcmp (ic_name_space, "System.Collections.Generic") != 0)) {
+ TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAMESPACE CHECK FAILED]"));
+ return FALSE;
+ }
+ if ((ic_name == NULL) || ((strcmp (ic_name, "IEnumerable`1") != 0) && (strcmp (ic_name, "ICollection`1") != 0) && (strcmp (ic_name, "IList`1") != 0) && (strcmp (ic_name, "IReadOnlyList`1") != 0) && (strcmp (ic_name, "IReadOnlyCollection`1") != 0))) {
+ TRACE_INTERFACE_VTABLE (printf ("[INTERFACE NAME CHECK FAILED]"));
+ return FALSE;
+ }
+
+ subname = (char*)strstr (cm->name, ic_name_space);
+ if (subname != cm->name) {
+ TRACE_INTERFACE_VTABLE (printf ("[ACTUAL NAMESPACE CHECK FAILED]"));
+ return FALSE;
+ }
+ subname += strlen (ic_name_space);
+ if (subname [0] != '.') {
+ TRACE_INTERFACE_VTABLE (printf ("[FIRST DOT CHECK FAILED]"));
+ return FALSE;
+ }
+ subname ++;
+ if (strstr (subname, ic_name) != subname) {
+ TRACE_INTERFACE_VTABLE (printf ("[ACTUAL CLASS NAME CHECK FAILED]"));
+ return FALSE;
+ }
+ subname += strlen (ic_name);
+ if (subname [0] != '.') {
+ TRACE_INTERFACE_VTABLE (printf ("[SECOND DOT CHECK FAILED]"));
+ return FALSE;
+ }
+ subname ++;
+ if (strcmp (subname, im->name) != 0) {
+ TRACE_INTERFACE_VTABLE (printf ("[METHOD NAME CHECK FAILED]"));
+ return FALSE;
+ }
+
+ TRACE_INTERFACE_VTABLE (printf ("[SECURITY CHECKS (INJECTED CASE)]"));
+ if (mono_security_core_clr_enabled ())
+ mono_security_core_clr_check_override (klass, cm, im);
+
+ TRACE_INTERFACE_VTABLE (printf ("[INJECTED INTERFACE CHECK OK]"));
+ if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, im, NULL)) {
+ char *body_name = mono_method_full_name (cm, TRUE);
+ char *decl_name = mono_method_full_name (im, TRUE);
+ mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
+ g_free (body_name);
+ g_free (decl_name);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+#if (TRACE_INTERFACE_VTABLE_CODE|DEBUG_INTERFACE_VTABLE_CODE)
+static void
+foreach_override (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoMethod *method = key;
+ MonoMethod *override = value;
+
+ char *method_name = mono_method_get_full_name (method);
+ char *override_name = mono_method_get_full_name (override);
+ printf (" Method '%s' has override '%s'\n", method_name, override_name);
+ g_free (method_name);
+ g_free (override_name);
+}
+
+static void
+print_overrides (GHashTable *override_map, const char *message)
+{
+ if (override_map) {
+ printf ("Override map \"%s\" START:\n", message);
+ g_hash_table_foreach (override_map, foreach_override, NULL);
+ printf ("Override map \"%s\" END.\n", message);
+ } else {
+ printf ("Override map \"%s\" EMPTY.\n", message);
+ }
+}
+
+static void
+print_vtable_full (MonoClass *klass, MonoMethod** vtable, int size, int first_non_interface_slot, const char *message, gboolean print_interfaces)
+{
+ char *full_name = mono_type_full_name (m_class_get_byval_arg (klass));
+ int i;
+ int parent_size;
+
+ printf ("*** Vtable for class '%s' at \"%s\" (size %d)\n", full_name, message, size);
+
+ if (print_interfaces) {
+ print_implemented_interfaces (klass);
+ printf ("* Interfaces for class '%s' done.\nStarting vtable (size %d):\n", full_name, size);
+ }
+
+ if (klass->parent) {
+ parent_size = klass->parent->vtable_size;
+ } else {
+ parent_size = 0;
+ }
+ for (i = 0; i < size; ++i) {
+ MonoMethod *cm = vtable [i];
+ char *cm_name = cm ? mono_method_full_name (cm, TRUE) : g_strdup ("nil");
+ char newness = (i < parent_size) ? 'O' : ((i < first_non_interface_slot) ? 'I' : 'N');
+
+ printf (" [%c][%03d][INDEX %03d] %s [%p]\n", newness, i, cm ? cm->slot : - 1, cm_name, cm);
+ g_free (cm_name);
+ }
+
+ g_free (full_name);
+}
+#endif
+
+#if VERIFY_INTERFACE_VTABLE_CODE
+static int
+mono_method_try_get_vtable_index (MonoMethod *method)
+{
+ if (method->is_inflated && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
+ MonoMethodInflated *imethod = (MonoMethodInflated*)method;
+ if (imethod->declaring->is_generic)
+ return imethod->declaring->slot;
+ }
+ return method->slot;
+}
+
+static void
+mono_class_verify_vtable (MonoClass *klass)
+{
+ int i, count;
+ char *full_name = mono_type_full_name (m_class_get_byval_arg (klass));
+
+ printf ("*** Verifying VTable of class '%s' \n", full_name);
+ g_free (full_name);
+ full_name = NULL;
+
+ if (!klass->methods)
+ return;
+
+ count = mono_class_get_method_count (klass);
+ for (i = 0; i < count; ++i) {
+ MonoMethod *cm = klass->methods [i];
+ int slot;
+
+ if (!(cm->flags & METHOD_ATTRIBUTE_VIRTUAL))
+ continue;
+
+ g_free (full_name);
+ full_name = mono_method_full_name (cm, TRUE);
+
+ slot = mono_method_try_get_vtable_index (cm);
+ if (slot >= 0) {
+ if (slot >= klass->vtable_size) {
+ printf ("\tInvalid method %s at index %d with vtable of length %d\n", full_name, slot, klass->vtable_size);
+ continue;
+ }
+
+ if (slot >= 0 && klass->vtable [slot] != cm && (klass->vtable [slot])) {
+ char *other_name = klass->vtable [slot] ? mono_method_full_name (klass->vtable [slot], TRUE) : g_strdup ("[null value]");
+ printf ("\tMethod %s has slot %d but vtable has %s on it\n", full_name, slot, other_name);
+ g_free (other_name);
+ }
+ } else
+ printf ("\tVirtual method %s does n't have an assigned slot\n", full_name);
+ }
+ g_free (full_name);
+}
+#endif
+
+static MonoMethod*
+mono_method_get_method_definition (MonoMethod *method)
+{
+ while (method->is_inflated)
+ method = ((MonoMethodInflated*)method)->declaring;
+ return method;
+}
+
+static gboolean
+verify_class_overrides (MonoClass *klass, MonoMethod **overrides, int onum)
+{
+ int i;
+
+ for (i = 0; i < onum; ++i) {
+ MonoMethod *decl = overrides [i * 2];
+ MonoMethod *body = overrides [i * 2 + 1];
+
+ if (mono_class_get_generic_type_definition (body->klass) != mono_class_get_generic_type_definition (klass)) {
+ mono_class_set_type_load_failure (klass, "Method belongs to a different class than the declared one");
+ return FALSE;
+ }
+
+ if (!(body->flags & METHOD_ATTRIBUTE_VIRTUAL) || (body->flags & METHOD_ATTRIBUTE_STATIC)) {
+ if (body->flags & METHOD_ATTRIBUTE_STATIC)
+ mono_class_set_type_load_failure (klass, "Method must not be static to override a base type");
+ else
+ mono_class_set_type_load_failure (klass, "Method must be virtual to override a base type");
+ return FALSE;
+ }
+
+ if (!(decl->flags & METHOD_ATTRIBUTE_VIRTUAL) || (decl->flags & METHOD_ATTRIBUTE_STATIC)) {
+ if (body->flags & METHOD_ATTRIBUTE_STATIC)
+ mono_class_set_type_load_failure (klass, "Cannot override a static method in a base type");
+ else
+ mono_class_set_type_load_failure (klass, "Cannot override a non virtual method in a base type");
+ return FALSE;
+ }
+
+ if (!mono_class_is_assignable_from_slow (decl->klass, klass)) {
+ mono_class_set_type_load_failure (klass, "Method overrides a class or interface that is not extended or implemented by this type");
+ return FALSE;
+ }
+
+ body = mono_method_get_method_definition (body);
+ decl = mono_method_get_method_definition (decl);
+
+ if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (body, decl, NULL)) {
+ char *body_name = mono_method_full_name (body, TRUE);
+ char *decl_name = mono_method_full_name (decl, TRUE);
+ mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
+ g_free (body_name);
+ g_free (decl_name);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*Checks if @klass has @parent as one of it's parents type gtd
+ *
+ * For example:
+ * Foo<T>
+ * Bar<T> : Foo<Bar<Bar<T>>>
+ *
+ */
+static gboolean
+mono_class_has_gtd_parent (MonoClass *klass, MonoClass *parent)
+{
+ klass = mono_class_get_generic_type_definition (klass);
+ parent = mono_class_get_generic_type_definition (parent);
+ mono_class_setup_supertypes (klass);
+ mono_class_setup_supertypes (parent);
+
+ return klass->idepth >= parent->idepth &&
+ mono_class_get_generic_type_definition (klass->supertypes [parent->idepth - 1]) == parent;
+}
+
+gboolean
+mono_class_check_vtable_constraints (MonoClass *klass, GList *in_setup)
+{
+ MonoGenericInst *ginst;
+ int i;
+
+ if (!mono_class_is_ginst (klass)) {
+ mono_class_setup_vtable_full (klass, in_setup);
+ return !mono_class_has_failure (klass);
+ }
+
+ mono_class_setup_vtable_full (mono_class_get_generic_type_definition (klass), in_setup);
+ if (mono_class_set_type_load_failure_causedby_class (klass, mono_class_get_generic_class (klass)->container_class, "Failed to load generic definition vtable"))
+ return FALSE;
+
+ ginst = mono_class_get_generic_class (klass)->context.class_inst;
+ for (i = 0; i < ginst->type_argc; ++i) {
+ MonoClass *arg;
+ if (ginst->type_argv [i]->type != MONO_TYPE_GENERICINST)
+ continue;
+ arg = mono_class_from_mono_type_internal (ginst->type_argv [i]);
+ /*Those 2 will be checked by mono_class_setup_vtable itself*/
+ if (mono_class_has_gtd_parent (klass, arg) || mono_class_has_gtd_parent (arg, klass))
+ continue;
+ if (!mono_class_check_vtable_constraints (arg, in_setup)) {
+ mono_class_set_type_load_failure (klass, "Failed to load generic parameter %d", i);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * mono_class_setup_vtable:
+ *
+ * Creates the generic vtable of CLASS.
+ * Initializes the following fields in MonoClass:
+ * - vtable
+ * - vtable_size
+ * Plus all the fields initialized by setup_interface_offsets ().
+ * If there is an error during vtable construction, klass->has_failure
+ * is set and details are stored in a MonoErrorBoxed.
+ *
+ * LOCKING: Acquires the loader lock.
+ */
+void
+mono_class_setup_vtable (MonoClass *klass)
+{
+ mono_class_setup_vtable_full (klass, NULL);
+}
+
+static void
+mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup)
+{
+ ERROR_DECL (error);
+ MonoMethod **overrides = NULL;
+ MonoGenericContext *context;
+ guint32 type_token;
+ int onum = 0;
+
+ if (klass->vtable)
+ return;
+
+ if (MONO_CLASS_IS_INTERFACE_INTERNAL (klass)) {
+ /* This sets method->slot for all methods if this is an interface */
+ mono_class_setup_methods (klass);
+ return;
+ }
+
+ if (mono_class_has_failure (klass))
+ return;
+
+ if (g_list_find (in_setup, klass))
+ return;
+
+ mono_loader_lock ();
+
+ if (klass->vtable) {
+ mono_loader_unlock ();
+ return;
+ }
+
+ UnlockedIncrement (&mono_stats.generic_vtable_count);
+ in_setup = g_list_prepend (in_setup, klass);
+
+ if (mono_class_is_ginst (klass)) {
+ if (!mono_class_check_vtable_constraints (klass, in_setup)) {
+ mono_loader_unlock ();
+ g_list_remove (in_setup, klass);
+ return;
+ }
+
+ context = mono_class_get_context (klass);
+ type_token = mono_class_get_generic_class (klass)->container_class->type_token;
+ } else {
+ context = (MonoGenericContext *) mono_class_try_get_generic_container (klass); //FIXME is this a case of a try?
+ type_token = klass->type_token;
+ }
+
+ if (image_is_dynamic (klass->image)) {
+ /* Generic instances can have zero method overrides without causing any harm.
+ * This is true since we don't do layout all over again for them, we simply inflate
+ * the layout of the parent.
+ */
+ mono_reflection_get_dynamic_overrides (klass, &overrides, &onum, error);
+ if (!is_ok (error)) {
+ mono_class_set_type_load_failure (klass, "Could not load list of method overrides due to %s", mono_error_get_message (error));
+ goto done;
+ }
+ } else {
+ /* The following call fails if there are missing methods in the type */
+ /* FIXME it's probably a good idea to avoid this for generic instances. */
+ mono_class_get_overrides_full (klass->image, type_token, &overrides, &onum, context, error);
+ if (!is_ok (error)) {
+ mono_class_set_type_load_failure (klass, "Could not load list of method overrides due to %s", mono_error_get_message (error));
+ goto done;
+ }
+ }
+
+ mono_class_setup_vtable_general (klass, overrides, onum, in_setup);
+
+done:
+ g_free (overrides);
+ mono_error_cleanup (error);
+
+ mono_loader_unlock ();
+ g_list_remove (in_setup, klass);
+
+ return;
+}
+
+gboolean
+mono_class_setup_need_stelemref_method (MonoClass *klass)
+{
+ return klass->rank == 1 && MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (m_class_get_element_class (klass)));
+}
+
+static int
+apply_override (MonoClass *klass, MonoClass *override_class, MonoMethod **vtable, MonoMethod *decl, MonoMethod *override,
+ GHashTable **override_map, GHashTable **override_class_map, GHashTable **conflict_map)
+{
+ int dslot;
+ dslot = mono_method_get_vtable_slot (decl);
+ if (dslot == -1) {
+ mono_class_set_type_load_failure (klass, "");
+ return FALSE;
+ }
+
+ dslot += mono_class_interface_offset (klass, decl->klass);
+
+ //check if the override comes from an interface and the overrided method is from a class, if this is the case it shouldn't be changed
+ if (vtable [dslot] && vtable [dslot]->klass && MONO_CLASS_IS_INTERFACE_INTERNAL (override->klass) && !MONO_CLASS_IS_INTERFACE_INTERNAL (vtable [dslot]->klass))
+ return TRUE;
+
+ vtable [dslot] = override;
+ if (!MONO_CLASS_IS_INTERFACE_INTERNAL (override->klass)) {
+ /*
+ * If override from an interface, then it is an override of a default interface method,
+ * don't override its slot.
+ */
+ vtable [dslot]->slot = dslot;
+ }
+
+ if (!*override_map) {
+ *override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ *override_class_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ }
+ GHashTable *map = *override_map;
+ GHashTable *class_map = *override_class_map;
+
+ MonoMethod *prev_override = (MonoMethod*)g_hash_table_lookup (map, decl);
+ MonoClass *prev_override_class = (MonoClass*)g_hash_table_lookup (class_map, decl);
+
+ g_hash_table_insert (map, decl, override);
+ g_hash_table_insert (class_map, decl, override_class);
+
+ /* Collect potentially conflicting overrides which are introduced by default interface methods */
+ if (prev_override) {
+ ERROR_DECL (error);
+
+ /*
+ * The override methods are part of the generic definition, need to inflate them so their
+ * parent class becomes the actual interface/class containing the override, i.e.
+ * IFace<T> in:
+ * class Foo<T> : IFace<T>
+ * This is needed so the mono_class_is_assignable_from_internal () calls in the
+ * conflict resolution work.
+ */
+ if (mono_class_is_ginst (override_class)) {
+ override = mono_class_inflate_generic_method_checked (override, &mono_class_get_generic_class (override_class)->context, error);
+ mono_error_assert_ok (error);
+ }
+
+ if (mono_class_is_ginst (prev_override_class)) {
+ prev_override = mono_class_inflate_generic_method_checked (prev_override, &mono_class_get_generic_class (prev_override_class)->context, error);
+ mono_error_assert_ok (error);
+ }
+
+ if (!*conflict_map)
+ *conflict_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ GHashTable *cmap = *conflict_map;
+ GSList *entries = (GSList*)g_hash_table_lookup (cmap, decl);
+ if (!(decl->flags & METHOD_ATTRIBUTE_ABSTRACT))
+ entries = g_slist_prepend (entries, decl);
+ entries = g_slist_prepend (entries, prev_override);
+ entries = g_slist_prepend (entries, override);
+
+ g_hash_table_insert (cmap, decl, entries);
+ }
+
+ return TRUE;
+}
+
+static void
+handle_dim_conflicts (MonoMethod **vtable, MonoClass *klass, GHashTable *conflict_map)
+{
+ GHashTableIter iter;
+ MonoMethod *decl;
+ GSList *entries, *l, *l2;
+ GSList *dim_conflicts = NULL;
+
+ g_hash_table_iter_init (&iter, conflict_map);
+ while (g_hash_table_iter_next (&iter, (gpointer*)&decl, (gpointer*)&entries)) {
+ /*
+ * Iterate over the candidate methods, remove ones whose class is less concrete than the
+ * class of another one.
+ */
+ /* This is O(n^2), but that shouldn't be a problem in practice */
+ for (l = entries; l; l = l->next) {
+ for (l2 = entries; l2; l2 = l2->next) {
+ MonoMethod *m1 = (MonoMethod*)l->data;
+ MonoMethod *m2 = (MonoMethod*)l2->data;
+ if (!m1 || !m2 || m1 == m2)
+ continue;
+ if (mono_class_is_assignable_from_internal (m1->klass, m2->klass))
+ l->data = NULL;
+ else if (mono_class_is_assignable_from_internal (m2->klass, m1->klass))
+ l2->data = NULL;
+ }
+ }
+ int nentries = 0;
+ MonoMethod *impl = NULL;
+ for (l = entries; l; l = l->next) {
+ if (l->data) {
+ nentries ++;
+ impl = (MonoMethod*)l->data;
+ }
+ }
+ if (nentries > 1) {
+ /* If more than one method is left, we have a conflict */
+ if (decl->is_inflated)
+ decl = ((MonoMethodInflated*)decl)->declaring;
+ dim_conflicts = g_slist_prepend (dim_conflicts, decl);
+ /*
+ for (l = entries; l; l = l->next) {
+ if (l->data)
+ printf ("%s %s %s\n", mono_class_full_name (klass), mono_method_full_name (decl, TRUE), mono_method_full_name (l->data, TRUE));
+ }
+ */
+ } else {
+ /*
+ * Use the implementing method computed above instead of the already
+ * computed one, which depends on interface ordering.
+ */
+ int ic_offset = mono_class_interface_offset (klass, decl->klass);
+ int im_slot = ic_offset + decl->slot;
+ vtable [im_slot] = impl;
+ }
+ g_slist_free (entries);
+ }
+ if (dim_conflicts) {
+ mono_loader_lock ();
+ klass->has_dim_conflicts = 1;
+ mono_loader_unlock ();
+
+ /*
+ * Exceptions are thrown at method call time and only for the methods which have
+ * conflicts, so just save them in the class.
+ */
+
+ /* Make a copy of the list from the class mempool */
+ GSList *conflicts = (GSList*)mono_class_alloc0 (klass, g_slist_length (dim_conflicts) * sizeof (GSList));
+ int i = 0;
+ for (l = dim_conflicts; l; l = l->next) {
+ conflicts [i].data = l->data;
+ conflicts [i].next = &conflicts [i + 1];
+ i ++;
+ }
+ conflicts [i - 1].next = NULL;
+
+ mono_class_set_dim_conflicts (klass, conflicts);
+ g_slist_free (dim_conflicts);
+ }
+}
+
+static void
+print_unimplemented_interface_method_info (MonoClass *klass, MonoClass *ic, MonoMethod *im, int im_slot, MonoMethod **overrides, int onum)
+{
+ int index, mcount;
+ char *method_signature;
+ char *type_name;
+
+ for (index = 0; index < onum; ++index) {
+ mono_trace_warning (MONO_TRACE_TYPE, " at slot %d: %s (%d) overrides %s (%d)", im_slot, overrides [index*2+1]->name,
+ overrides [index*2+1]->slot, overrides [index*2]->name, overrides [index*2]->slot);
+ }
+ method_signature = mono_signature_get_desc (mono_method_signature_internal (im), FALSE);
+ type_name = mono_type_full_name (m_class_get_byval_arg (klass));
+ mono_trace_warning (MONO_TRACE_TYPE, "no implementation for interface method %s::%s(%s) in class %s",
+ mono_type_get_name (m_class_get_byval_arg (ic)), im->name, method_signature, type_name);
+ g_free (method_signature);
+ g_free (type_name);
+ mono_class_setup_methods (klass);
+ if (mono_class_has_failure (klass)) {
+ char *name = mono_type_get_full_name (klass);
+ mono_trace_warning (MONO_TRACE_TYPE, "CLASS %s failed to resolve methods", name);
+ g_free (name);
+ return;
+ }
+ mcount = mono_class_get_method_count (klass);
+ for (index = 0; index < mcount; ++index) {
+ MonoMethod *cm = klass->methods [index];
+ method_signature = mono_signature_get_desc (mono_method_signature_internal (cm), TRUE);
+
+ mono_trace_warning (MONO_TRACE_TYPE, "METHOD %s(%s)", cm->name, method_signature);
+ g_free (method_signature);
+ }
+}
+
+/*
+ * mono_class_get_virtual_methods:
+ *
+ * Iterate over the virtual methods of KLASS.
+ *
+ * LOCKING: Assumes the loader lock is held (because of the klass->methods check).
+ */
+static MonoMethod*
+mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter)
+{
+ // FIXME move state to caller
+ gboolean static_iter = FALSE;
+
+ if (!iter)
+ return NULL;
+
+ /*
+ * If the lowest bit of the iterator is 1, this is an iterator for static metadata,
+ * and the upper bits contain an index. Otherwise, the iterator is a pointer into
+ * klass->methods.
+ */
+ if ((gsize)(*iter) & 1)
+ static_iter = TRUE;
+ /* Use the static metadata only if klass->methods is not yet initialized */
+ if (!static_iter && !(klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)))
+ static_iter = TRUE;
+
+ if (!static_iter) {
+ MonoMethod** methodptr;
+
+ if (!*iter) {
+ mono_class_setup_methods (klass);
+ /*
+ * We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places.
+ * FIXME we should better report this error to the caller
+ */
+ if (!klass->methods)
+ return NULL;
+ /* start from the first */
+ methodptr = &klass->methods [0];
+ } else {
+ methodptr = (MonoMethod **)*iter;
+ methodptr++;
+ }
+ if (*iter)
+ g_assert ((guint64)(*iter) > 0x100);
+ int mcount = mono_class_get_method_count (klass);
+ while (methodptr < &klass->methods [mcount]) {
+ if (*methodptr && ((*methodptr)->flags & METHOD_ATTRIBUTE_VIRTUAL))
+ break;
+ methodptr ++;
+ }
+ if (methodptr < &klass->methods [mcount]) {
+ *iter = methodptr;
+ return *methodptr;
+ } else {
+ return NULL;
+ }
+ } else {
+ /* Search directly in metadata to avoid calling setup_methods () */
+ MonoMethod *res = NULL;
+ int i, start_index;
+
+ if (!*iter) {
+ start_index = 0;
+ } else {
+ start_index = GPOINTER_TO_UINT (*iter) >> 1;
+ }
+
+ int first_idx = mono_class_get_first_method_idx (klass);
+ int mcount = mono_class_get_method_count (klass);
+ for (i = start_index; i < mcount; ++i) {
+ guint32 flags;
+
+ /* first_idx points into the methodptr table */
+ flags = mono_metadata_decode_table_row_col (klass->image, MONO_TABLE_METHOD, first_idx + i, MONO_METHOD_FLAGS);
+
+ if (flags & METHOD_ATTRIBUTE_VIRTUAL)
+ break;
+ }
+
+ if (i < mcount) {
+ ERROR_DECL (error);
+ res = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | (first_idx + i + 1), klass, NULL, error);
+ mono_error_cleanup (error); /* FIXME don't swallow the error */
+
+ /* Add 1 here so the if (*iter) check fails */
+ *iter = GUINT_TO_POINTER (((i + 1) << 1) | 1);
+ return res;
+ } else {
+ return NULL;
+ }
+ }
+}
+
+static void
+print_vtable_layout_result (MonoClass *klass, MonoMethod **vtable, int cur_slot)
+{
+ int i, icount = 0;
+
+ print_implemented_interfaces (klass);
+
+ for (i = 0; i <= klass->max_interface_id; i++)
+ if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i))
+ icount++;
+
+ printf ("VTable %s (vtable entries = %d, interfaces = %d)\n", mono_type_full_name (m_class_get_byval_arg (klass)),
+ klass->vtable_size, icount);
+
+ for (i = 0; i < cur_slot; ++i) {
+ MonoMethod *cm;
+
+ cm = vtable [i];
+ if (cm) {
+ printf (" slot assigned: %03d, slot index: %03d %s\n", i, cm->slot,
+ mono_method_get_full_name (cm));
+ } else {
+ printf (" slot assigned: %03d, <null>\n", i);
+ }
+ }
+
+
+ if (icount) {
+ printf ("Interfaces %s.%s (max_iid = %d)\n", klass->name_space,
+ klass->name, klass->max_interface_id);
+
+ for (i = 0; i < klass->interface_count; i++) {
+ MonoClass *ic = klass->interfaces [i];
+ printf (" slot offset: %03d, method count: %03d, iid: %03d %s\n",
+ mono_class_interface_offset (klass, ic),
+ mono_class_setup_count_virtual_methods (ic), ic->interface_id, mono_type_full_name (m_class_get_byval_arg (ic)));
+ }
+
+ for (MonoClass *k = klass->parent; k ; k = k->parent) {
+ for (i = 0; i < k->interface_count; i++) {
+ MonoClass *ic = k->interfaces [i];
+ printf (" parent slot offset: %03d, method count: %03d, iid: %03d %s\n",
+ mono_class_interface_offset (klass, ic),
+ mono_class_setup_count_virtual_methods (ic), ic->interface_id, mono_type_full_name (m_class_get_byval_arg (ic)));
+ }
+ }
+ }
+}
+
+/*
+ * LOCKING: this is supposed to be called with the loader lock held.
+ */
+static int
+setup_class_vtsize (MonoClass *klass, GList *in_setup, int *cur_slot, int *stelemref_slot, MonoError *error)
+{
+ GPtrArray *ifaces = NULL;
+ int i, max_vtsize = 0;
+ ifaces = mono_class_get_implemented_interfaces (klass, error);
+ if (!is_ok (error)) {
+ char *name = mono_type_get_full_name (klass);
+ mono_class_set_type_load_failure (klass, "Could not resolve %s interfaces due to %s", name, mono_error_get_message (error));
+ g_free (name);
+ mono_error_cleanup (error);
+ return -1;
+ } else if (ifaces) {
+ for (i = 0; i < ifaces->len; i++) {
+ MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i);
+ max_vtsize += mono_class_get_method_count (ic);
+ }
+ g_ptr_array_free (ifaces, TRUE);
+ ifaces = NULL;
+ }
+
+ if (klass->parent) {
+ mono_class_init_internal (klass->parent);
+ mono_class_setup_vtable_full (klass->parent, in_setup);
+
+ if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Parent class failed to load"))
+ return -1;
+
+ max_vtsize += klass->parent->vtable_size;
+ *cur_slot = klass->parent->vtable_size;
+ }
+
+ max_vtsize += mono_class_get_method_count (klass);
+
+ /*Array have a slot for stelemref*/
+ if (mono_class_setup_need_stelemref_method (klass)) {
+ *stelemref_slot = *cur_slot;
+ ++max_vtsize;
+ ++*cur_slot;
+ }
+ return max_vtsize;
+}
+
+/*
+ * LOCKING: this is supposed to be called with the loader lock held.
+ */
+static void
+mono_class_setup_vtable_ginst (MonoClass *klass, GList *in_setup)
+{
+ ERROR_DECL (error);
+ int i;
+ MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
+ MonoMethod **tmp;
+
+ mono_class_setup_vtable_full (gklass, in_setup);
+ if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Could not load generic definition"))
+ return;
+
+ tmp = (MonoMethod **)mono_class_alloc0 (klass, sizeof (gpointer) * gklass->vtable_size);
+ klass->vtable_size = gklass->vtable_size;
+ for (i = 0; i < gklass->vtable_size; ++i)
+ if (gklass->vtable [i]) {
+ MonoMethod *inflated = mono_class_inflate_generic_method_full_checked (gklass->vtable [i], klass, mono_class_get_context (klass), error);
+ if (!is_ok (error)) {
+ char *name = mono_type_get_full_name (klass);
+ mono_class_set_type_load_failure (klass, "VTable setup of type %s failed due to: %s", name, mono_error_get_message (error));
+ mono_error_cleanup (error);
+ g_free (name);
+ return;
+ }
+ tmp [i] = inflated;
+ tmp [i]->slot = gklass->vtable [i]->slot;
+ }
+ mono_memory_barrier ();
+ klass->vtable = tmp;
+
+ mono_loader_lock ();
+ klass->has_dim_conflicts = gklass->has_dim_conflicts;
+ mono_loader_unlock ();
+
+ /* Have to set method->slot for abstract virtual methods */
+ if (klass->methods && gklass->methods) {
+ int mcount = mono_class_get_method_count (klass);
+ for (i = 0; i < mcount; ++i)
+ if (klass->methods [i]->slot == -1)
+ klass->methods [i]->slot = gklass->methods [i]->slot;
+ }
+
+ if (mono_print_vtable)
+ print_vtable_layout_result (klass, klass->vtable, gklass->vtable_size);
+
+}
+
+#ifdef FEATURE_COVARIANT_RETURNS
+/*
+ * vtable_slot_has_preserve_base_overrides_attribute:
+ *
+ * Needs to walk up the class hierarchy looking for the methods in this slot to
+ * see if any are tagged with PreserveBaseOverrideAtrribute.
+ */
+static gboolean
+vtable_slot_has_preserve_base_overrides_attribute (MonoClass *klass, int slot, MonoClass **out_klass)
+{
+ /*
+ * FIXME: it's slow to do this loop every time. A faster way would be
+ * to hang a boolean in the image of the parent class if it or any of
+ * its ancestors have the attribute.
+ */
+
+ for (; klass; klass = klass->parent) {
+ if (slot >= klass->vtable_size)
+ break;
+ MonoMethod *method = klass->vtable [slot];
+
+ /* FIXME: for abstract classes, do we put abstract methods here? */
+ if (method && mono_class_setup_method_has_preserve_base_overrides_attribute (method)) {
+ if (out_klass)
+ *out_klass = klass;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * is_ok_for_covariant_ret:
+ *
+ * Returns TRUE if the given pair of types are a valid return type for the covariant returns feature.
+ *
+ * This is the CanCastTo relation from ECMA (impl type can be cast to the decl
+ * type), except that we don't allow a valuetype to be cast to one of its
+ * implemented interfaces, and we don't allow T to Nullable<T>.
+ */
+static gboolean
+is_ok_for_covariant_ret (MonoType *type_impl, MonoType *type_decl)
+{
+ if (type_impl->byref ^ type_decl->byref)
+ return FALSE;
+
+ if (type_impl->byref) {
+ return mono_byref_type_is_assignable_from (type_decl, type_impl, TRUE);
+ }
+ /* method declared to return an interface, impl returns a value type that implements the interface */
+ if (!mono_type_is_reference (type_impl) && mono_type_is_reference (type_decl))
+ return FALSE;
+
+
+ TRACE_INTERFACE_VTABLE (do {
+ char *decl_str = mono_type_full_name (type_decl);
+ char *impl_str = mono_type_full_name (type_impl);
+ printf ("Checking if %s is assignable from %s", decl_str, impl_str);
+ g_free (decl_str);
+ g_free (impl_str);
+ } while (0));
+
+ MonoClass *class_impl = mono_class_from_mono_type_internal (type_impl);
+ MonoClass *class_decl = mono_class_from_mono_type_internal (type_decl);
+
+ /* Also disallow overriding a Nullable<T> return with an impl that
+ * returns T */
+ if (mono_class_is_nullable (class_decl) &&
+ mono_class_get_nullable_param_internal (class_decl) == class_impl)
+ return FALSE;
+
+
+ ERROR_DECL (local_error);
+ gboolean result = FALSE;
+ mono_class_signature_is_assignable_from (class_decl, class_impl, &result, local_error);
+ mono_error_cleanup (local_error);
+ return result;
+}
+
+/**
+ * signature_is_subsumed:
+ * \param impl_method method that implements the override. defined in \p klass
+ * \param decl_method method that is being overridden.
+ *
+ * Check that \p impl_method has a signature that is subsumed by the signature of \p decl_method. That is,
+ * that the argument types all match and that the return type of \p impl_method is castable to the return type of \p decl_method, except that
+ * both must not be valuetypes or interfaces.
+ *
+ * Returns \c TRUE if \p impl_method is subsumed by \p decl_method, or FALSE otherwise. On error sets \p error and returns \c FALSE
+ *
+ * Note that \p decl_method may not be the explicitly overridden method as specified by an .override in the class where \p impl_method is defined,
+ * but rather some method on an ancestor class that is itself a previous override.
+ *
+ * class C {
+ * public virtual C Foo ();
+ * }
+ * class B : C {
+ * public virtual newslot B Foo () {
+ * .PreserveBaseOverrideAttribute;
+ * .override C C::Foo ();
+ * }
+ * }
+ * class A : B {
+ * public virtual newslot C Foo () {
+ * .override C C::Foo (); // type load error - it should be a a subclass of B because of B's override of C::Foo
+ * }
+ * }
+ */
+static gboolean
+signature_is_subsumed (MonoMethod *impl_method, MonoMethod *decl_method, MonoError *error)
+{
+ MonoMethodSignature *impl_sig = mono_method_signature_internal (impl_method);
+ MonoMethodSignature *decl_sig = mono_method_signature_internal (decl_method);
+
+ if (mono_metadata_signature_equal (impl_sig, decl_sig))
+ return TRUE;
+
+ if (!mono_metadata_signature_equal_no_ret (impl_sig, decl_sig))
+ return FALSE;
+
+ MonoType *impl_ret = impl_sig->ret;
+ MonoType *decl_ret_0 = decl_sig->ret;
+
+ /*
+ * Comparing return types of generic methods:
+ *
+ * Suppose decl is a generic method RetTy0`2[!!0,!!1] Method<T1,T2> (DeclArgTys...)
+ * we need to check that:
+ *
+ * 1. impl is a generic method with the same number of type arguments
+ * ImplRetTy Method<S1,S2> (ImplArgTys...)
+ * (because the number of generic arguments isn't allowed to change)
+ * 2. Inflate RetTy0`2[T1,T2] with S1,S2 for T1,T2 to get RetTy0`2[S1,S2]
+ * 3. Compare if ImplRetTy is assignable to RetTy0`2[S1,S2]
+ *
+ * (For example the decl method might return IReadOnlyDictionary<K,V>
+ * Foo<K,V>() and the impl method might be SortedList<A,B> Foo<A,B>(),
+ * so we want to check that SortedList<A,B> is assignable to
+ * IReadOnlyDictionary<A,B>. If we naively check SortedList<A,B>
+ * against IReadOnlyDictionary<K,V> it will fail because the
+ * implementation of the assignable relation won't consider K and A
+ * equal)
+ *
+ * It's possible that actually mono_metadata_signature_equal will work
+ * (because it does a signature check which considers !!0 equal to !!0
+ * even if they come from different generic containers), so if the
+ * return types are identical, we won't even get here, but for the
+ * return type assignable checking, we need to inflate RetTy0 properly.
+ */
+
+ /* We only inflate the return type, not the entire method signature,
+ * because the signature_equal_no_ret check above can compare
+ * corresponding positional type parameters even if they come from
+ * different generic methods. So since the arguments aren't allowed to
+ * vary at all, we know they were already identical and we only need to
+ * compare return types.
+ */
+
+ /* both have to be non-generic, or both generic */
+ if ((impl_method->is_generic || decl_method->is_generic) &&
+ !!impl_method->is_generic != !!decl_method->is_generic)
+ return FALSE;
+
+ MonoType *decl_ret;
+ MonoType *alloc_decl_ret = NULL;
+ if (!impl_method->is_generic) {
+ decl_ret = decl_ret_0;
+ } else {
+ g_assert (decl_method->is_generic);
+
+ MonoGenericContainer *impl_container = mono_method_get_generic_container (impl_method);
+ MonoGenericContainer *decl_container = mono_method_get_generic_container (decl_method);
+
+ g_assert (decl_container != NULL);
+ g_assert (impl_container != NULL);
+
+ if (impl_container->type_argc != decl_container->type_argc)
+ return FALSE;
+
+ /* inflate decl's return type with the type parameters of impl */
+ alloc_decl_ret = mono_class_inflate_generic_type_checked (decl_ret_0, &impl_container->context, error);
+ return_val_if_nok (error, FALSE);
+ decl_ret = alloc_decl_ret;
+ }
+
+ gboolean result = is_ok_for_covariant_ret (impl_ret, decl_ret);
+ if (alloc_decl_ret)
+ mono_metadata_free_type (alloc_decl_ret);
+ return result;
+}
+
+static gboolean
+check_signature_covariant (MonoClass *klass, MonoMethod *impl, MonoMethod *decl)
+{
+ TRACE_INTERFACE_VTABLE (printf (" checking covariant signature compatability on behalf of %s: '%s' overriding '%s'\n", mono_type_full_name (m_class_get_byval_arg (klass)), mono_method_full_name (impl, 1), mono_method_full_name (decl, 1)));
+ ERROR_DECL (local_error);
+ gboolean subsumed = signature_is_subsumed (impl, decl, local_error);
+ if (!is_ok (local_error) || !subsumed) {
+ const gboolean print_sig = TRUE;
+ const gboolean print_ret_type = TRUE;
+ char *decl_method_name = mono_method_get_name_full (decl, print_sig, print_ret_type, MONO_TYPE_NAME_FORMAT_IL);
+ char *impl_method_name = mono_method_get_name_full (impl, print_sig, print_ret_type, MONO_TYPE_NAME_FORMAT_IL);
+ const char *msg;
+ if (!is_ok (local_error)) {
+ msg = mono_error_get_message (local_error);
+ } else {
+ msg = "but with an incompatible signature";
+ }
+ mono_class_set_type_load_failure (klass, "Method '%s' overrides method '%s', %s",
+ impl_method_name, decl_method_name, msg);
+ mono_error_cleanup (local_error);
+ g_free (decl_method_name);
+ g_free (impl_method_name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * check_vtable_covariant_override_impls:
+ * \param klass the class
+ * \param vtable the provisional vtable
+ * \param vtable_size the number of slots in the vtable
+ *
+ * Given a provisional vtable for a class, check that any methods that come from \p klass that have the \c
+ * MonoMethod:is_covariant_override_impl bit have the most specific signature of any method in that slot in the ancestor
+ * classes. This checks that if the current class is overriding a method, it is doing so with the most specific
+ * signature.
+ *
+ * Because classes can override methods either explicitly using an .override directive or implicitly by matching the
+ * signature of some virtual method of some ancestor class, you could get into a situation where a class incorrectly
+ * overrides a method with a less specific signature.
+ *
+ * For example:
+ *
+ * class A {
+ * public virtual A Foo ();
+ * }
+ * class B : A {
+ * public override B Foo (); // covariant return override
+ * }
+ * class C : B {
+ * public override A Foo (); // incorrect
+ * }
+ */
+static gboolean
+check_vtable_covariant_override_impls (MonoClass *klass, MonoMethod **vtable, int vtable_size)
+{
+ MonoClass *parent_class = klass->parent;
+ if (!parent_class)
+ return TRUE;
+
+ /* we only need to check the slots that the parent class has, too. Everything else is new. */
+ for (int slot = 0; slot < parent_class->vtable_size; ++slot) {
+ MonoMethod *impl = vtable[slot];
+ if (!impl || !impl->is_covariant_override_impl || impl->klass != klass)
+ continue;
+ MonoMethod *last_checked_prev_override = NULL;
+ for (MonoClass *cur_class = parent_class; cur_class ; cur_class = cur_class->parent) {
+ if (slot >= cur_class->vtable_size)
+ break;
+ MonoMethod *prev_impl = cur_class->vtable[slot];
+
+ if (prev_impl != last_checked_prev_override) {
+ /*
+ * the new impl should be subsumed by the prior one, ie this
+ * newest leaf class provides the most specific implementation
+ * of the method of any of its ancestor classes.
+ */
+ /*
+ * but as we go up the inheritance hierarchy only check if the
+ * prev_impl method is changing. If it's the same one in the
+ * slot the whole time, don't bother checking it over and
+ * over.
+ */
+ /* FIXME: do we need to check all the way up? can we just check the most derived one? */
+ if (!check_signature_covariant (klass, impl, prev_impl))
+ return FALSE;
+ last_checked_prev_override = prev_impl;
+ }
+ }
+ }
+ return TRUE;
+}
+#endif /* FEATURE_COVARIANT_RETURNS */
+
+/*
+ * LOCKING: this is supposed to be called with the loader lock held.
+ */
+void
+mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int onum, GList *in_setup)
+{
+ ERROR_DECL (error);
+ MonoClass *k, *ic;
+ MonoMethod **vtable = NULL;
+ int i, max_vtsize = 0, cur_slot = 0;
+ GHashTable *override_map = NULL;
+ GHashTable *override_class_map = NULL;
+ GHashTable *conflict_map = NULL;
+ MonoMethod *cm;
+#if (DEBUG_INTERFACE_VTABLE_CODE|TRACE_INTERFACE_VTABLE_CODE)
+ int first_non_interface_slot;
+#endif
+ GSList *virt_methods = NULL, *l;
+ int stelemref_slot = 0;
+
+ if (klass->vtable)
+ return;
+
+ if (overrides && !verify_class_overrides (klass, overrides, onum))
+ return;
+
+ max_vtsize = setup_class_vtsize (klass, in_setup, &cur_slot, &stelemref_slot, error);
+ if (max_vtsize == -1)
+ return;
+
+ cur_slot = mono_class_setup_interface_offsets_internal (klass, cur_slot, TRUE);
+ if (cur_slot == -1) /*setup_interface_offsets fails the type.*/
+ return;
+
+ DEBUG_INTERFACE_VTABLE (first_non_interface_slot = cur_slot);
+
+ /* Optimized version for generic instances */
+ if (mono_class_is_ginst (klass)) {
+ mono_class_setup_vtable_ginst (klass, in_setup);
+ return;
+ }
+
+ vtable = (MonoMethod **)g_malloc0 (sizeof (gpointer) * max_vtsize);
+
+ if (klass->parent && klass->parent->vtable_size)
+ memcpy (vtable, klass->parent->vtable, sizeof (gpointer) * klass->parent->vtable_size);
+
+ /*Array have a slot for stelemref*/
+ if (mono_class_setup_need_stelemref_method (klass)) {
+ MonoMethod *method = mono_marshal_get_virtual_stelemref (klass);
+ if (!method->slot)
+ method->slot = stelemref_slot;
+ else
+ g_assert (method->slot == stelemref_slot);
+
+ vtable [stelemref_slot] = method;
+ }
+
+ TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER INHERITING PARENT VTABLE", TRUE));
+
+ /* Process overrides from interface default methods */
+ // FIXME: Ordering between interfaces
+ for (int ifindex = 0; ifindex < klass->interface_offsets_count; ifindex++) {
+ ic = klass->interfaces_packed [ifindex];
+
+ mono_class_setup_methods (ic);
+ if (mono_class_has_failure (ic))
+ goto fail;
+
+ MonoMethod **iface_overrides;
+ int iface_onum;
+ mono_class_get_overrides_full (ic->image, ic->type_token, &iface_overrides, &iface_onum, mono_class_get_context (ic), error);
+ goto_if_nok (error, fail);
+ 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) {
+ 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;
+ }
+ g_free (iface_overrides);
+ }
+
+ /* override interface methods */
+ for (i = 0; i < onum; i++) {
+ MonoMethod *decl = overrides [i*2];
+ MonoMethod *override = overrides [i*2 + 1];
+ if (MONO_CLASS_IS_INTERFACE_INTERNAL (decl->klass)) {
+ if (!apply_override (klass, klass, vtable, decl, override, &override_map, &override_class_map, &conflict_map))
+ goto fail;
+ }
+ }
+
+ TRACE_INTERFACE_VTABLE (print_overrides (override_map, "AFTER OVERRIDING INTERFACE METHODS"));
+ TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER OVERRIDING INTERFACE METHODS", FALSE));
+
+ /*
+ * Create a list of virtual methods to avoid calling
+ * mono_class_get_virtual_methods () which is slow because of the metadata
+ * optimization.
+ */
+ {
+ gpointer iter = NULL;
+ MonoMethod *cm;
+
+ virt_methods = NULL;
+ while ((cm = mono_class_get_virtual_methods (klass, &iter))) {
+ virt_methods = g_slist_prepend (virt_methods, cm);
+ }
+ if (mono_class_has_failure (klass))
+ goto fail;
+ }
+
+ // Loop on all implemented interfaces...
+ for (i = 0; i < klass->interface_offsets_count; i++) {
+ MonoClass *parent = klass->parent;
+ int ic_offset;
+ gboolean interface_is_explicitly_implemented_by_class;
+ int im_index;
+
+ ic = klass->interfaces_packed [i];
+ ic_offset = mono_class_interface_offset (klass, ic);
+
+ mono_class_setup_methods (ic);
+ if (mono_class_has_failure (ic))
+ goto fail;
+
+ // Check if this interface is explicitly implemented (instead of just inherited)
+ if (parent != NULL) {
+ int implemented_interfaces_index;
+ interface_is_explicitly_implemented_by_class = FALSE;
+ for (implemented_interfaces_index = 0; implemented_interfaces_index < klass->interface_count; implemented_interfaces_index++) {
+ if (ic == klass->interfaces [implemented_interfaces_index]) {
+ interface_is_explicitly_implemented_by_class = TRUE;
+ break;
+ }
+ }
+ } else {
+ interface_is_explicitly_implemented_by_class = TRUE;
+ }
+
+ // Loop on all interface methods...
+ int mcount = mono_class_get_method_count (ic);
+ for (im_index = 0; im_index < mcount; im_index++) {
+ 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;
+
+ if (im->flags & METHOD_ATTRIBUTE_STATIC)
+ continue;
+
+ TRACE_INTERFACE_VTABLE (printf ("\tchecking iface method %s\n", mono_method_full_name (im,1)));
+
+ 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;
+ /* Why do we need this? */
+ if (cm->slot < 0) {
+ cm->slot = im_slot;
+ }
+ if (conflict_map)
+ g_hash_table_remove(conflict_map, im);
+ break;
+ }
+ 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--) {
+ 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;
+ }
+ }
+ } else {
+ g_assert (vtable [im_slot] == override_im);
+ }
+ }
+ }
+
+ // If the class is not abstract, check that all its interface slots are full.
+ // The check is done here and not directly at the end of the loop above because
+ // 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;
+ int im_index;
+
+ ic = klass->interfaces_packed [i];
+ ic_offset = mono_class_interface_offset (klass, ic);
+
+ int mcount = mono_class_get_method_count (ic);
+ for (im_index = 0; im_index < mcount; im_index++) {
+ MonoMethod *im = ic->methods [im_index];
+ int im_slot = ic_offset + im->slot;
+
+ 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)));
+ if (vtable [im_slot] == NULL) {
+ print_unimplemented_interface_method_info (klass, ic, im, im_slot, overrides, onum);
+ goto fail;
+ }
+ }
+ }
+ }
+
+ TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER SETTING UP INTERFACE METHODS", FALSE));
+ for (l = virt_methods; l; l = l->next) {
+ cm = (MonoMethod *)l->data;
+ /*
+ * If the method is REUSE_SLOT, we must check in the
+ * base class for a method to override.
+ */
+ if (!(cm->flags & METHOD_ATTRIBUTE_NEW_SLOT)) {
+ int slot = -1;
+ for (k = klass->parent; k ; k = k->parent) {
+ gpointer k_iter;
+ MonoMethod *m1;
+
+ k_iter = NULL;
+ while ((m1 = mono_class_get_virtual_methods (k, &k_iter))) {
+ MonoMethodSignature *cmsig, *m1sig;
+
+ cmsig = mono_method_signature_internal (cm);
+ m1sig = mono_method_signature_internal (m1);
+
+ if (!cmsig || !m1sig) /* FIXME proper error message, use signature_checked? */
+ goto fail;
+
+ if (!strcmp(cm->name, m1->name) &&
+ mono_metadata_signature_equal (cmsig, m1sig)) {
+
+ if (mono_security_core_clr_enabled ())
+ mono_security_core_clr_check_override (klass, cm, m1);
+
+ slot = mono_method_get_vtable_slot (m1);
+ if (slot == -1)
+ goto fail;
+
+#ifdef FEATURE_COVARIANT_RETURNS
+ if (vtable[slot] && vtable[slot]->is_covariant_override_impl) {
+ TRACE_INTERFACE_VTABLE (printf (" in class %s, implicit override %s overrides %s in slot %d, which contained %s which had the covariant return bit\n", mono_type_full_name (m_class_get_byval_arg (klass)), mono_method_full_name (cm, 1), mono_method_full_name (m1, 1), slot, mono_method_full_name (vtable[slot], 1)));
+ /* Mark the current method as overriding a covariant return method; after the explicit overloads are applied, if this method is still in its slot in the vtable, we will check that it's the most specific implementation */
+ cm->is_covariant_override_impl = TRUE;
+ }
+#endif /* FEATURE_COVARIANT_RETURNS */
+
+ if (is_wcf_hack_disabled () && !mono_method_can_access_method_full (cm, m1, NULL)) {
+ char *body_name = mono_method_full_name (cm, TRUE);
+ char *decl_name = mono_method_full_name (m1, TRUE);
+ mono_class_set_type_load_failure (klass, "Method %s overrides method '%s' which is not accessible", body_name, decl_name);
+ g_free (body_name);
+ g_free (decl_name);
+ goto fail;
+ }
+
+ g_assert (cm->slot < max_vtsize);
+ if (!override_map)
+ override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ TRACE_INTERFACE_VTABLE (printf ("adding base class override from %s [%p] to %s [%p]\n",
+ mono_method_full_name (m1, 1), m1,
+ mono_method_full_name (cm, 1), cm));
+ g_hash_table_insert (override_map, m1, cm);
+ break;
+ }
+ }
+ if (mono_class_has_failure (k))
+ goto fail;
+
+ if (slot >= 0)
+ break;
+ }
+ if (slot >= 0)
+ cm->slot = slot;
+ }
+
+ /*Non final newslot methods must be given a non-interface vtable slot*/
+ if ((cm->flags & METHOD_ATTRIBUTE_NEW_SLOT) && !(cm->flags & METHOD_ATTRIBUTE_FINAL) && cm->slot >= 0)
+ cm->slot = -1;
+
+ if (cm->slot < 0)
+ cm->slot = cur_slot++;
+
+ if (!(cm->flags & METHOD_ATTRIBUTE_ABSTRACT))
+ vtable [cm->slot] = cm;
+ }
+
+ /* override non interface methods */
+ for (i = 0; i < onum; i++) {
+ MonoMethod *decl = overrides [i*2];
+ MonoMethod *impl = overrides [i*2 + 1];
+ if (!MONO_CLASS_IS_INTERFACE_INTERNAL (decl->klass)) {
+ g_assert (decl->slot != -1);
+ MonoMethod *prev_impl = vtable [decl->slot];
+ vtable [decl->slot] = impl;
+#ifdef FEATURE_COVARIANT_RETURNS
+ gboolean impl_newslot = (impl->flags & METHOD_ATTRIBUTE_NEW_SLOT) != 0;
+ /* covariant returns generate an explicit override impl with a newslot flag. respect it. */
+ if (!impl_newslot)
+ impl->slot = decl->slot;
+#endif
+ if (!override_map)
+ override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ TRACE_INTERFACE_VTABLE (printf ("adding explicit override from %s [%p] to %s [%p]\n",
+ mono_method_full_name (decl, 1), decl,
+ mono_method_full_name (impl, 1), impl));
+ g_hash_table_insert (override_map, decl, impl);
+
+#ifdef FEATURE_COVARIANT_RETURNS
+ MonoClass *impl_class = impl->klass;
+ gboolean has_preserve_base_overrides =
+ mono_class_setup_method_has_preserve_base_overrides_attribute (impl) ||
+ (klass->parent &&
+ vtable_slot_has_preserve_base_overrides_attribute (klass->parent, decl->slot, &impl_class) &&
+ impl_class != decl->klass);
+
+ if (impl_class == decl->klass) {
+ TRACE_INTERFACE_VTABLE (printf ("preserve base overrides attribute is on slot %d is on the decl method %s; not adding any more overrides\n", decl->slot, mono_method_full_name (decl, 1)));
+ }
+
+ if (has_preserve_base_overrides) {
+ g_assert (impl_class != NULL);
+ g_assert (impl_class == klass || decl->slot < impl_class->vtable_size);
+ TRACE_INTERFACE_VTABLE (do {
+ MonoMethod *impl_with_attr = impl_class == klass ? impl : impl_class->vtable [decl->slot];
+ printf ("override decl [slot %d] %s in class %s has method %s in this slot and it has the preserve base overrides attribute. overridden by %s\n", decl->slot, mono_method_full_name (decl, 1), mono_type_full_name (m_class_get_byval_arg (impl_class)), mono_method_full_name (impl_with_attr, 1), mono_method_full_name (impl, 1));
+ } while (0));
+ }
+
+ /* Historically, mono didn't do a signature equivalence check for explicit overrides, but we need one for covariant returns */
+ /* If the previous impl in the slot had the covariant signature bit set, or if the signature of the proposed impl doesn't match the signature of the previous impl, check for covariance */
+ if (prev_impl != NULL &&
+ (prev_impl->is_covariant_override_impl ||
+ !mono_metadata_signature_equal (mono_method_signature_internal (impl), mono_method_signature_internal (prev_impl)))) {
+ impl->is_covariant_override_impl = TRUE;
+ }
+
+ /* if we saw the attribute or if we think we need to check impl sigs, we will need to traverse the class hierarchy. */
+ if (has_preserve_base_overrides) {
+ for (MonoClass *cur_class = klass->parent; cur_class ; cur_class = cur_class->parent) {
+ if (decl->slot >= cur_class->vtable_size)
+ break;
+ prev_impl = cur_class->vtable[decl->slot];
+ g_hash_table_insert (override_map, prev_impl, impl);
+ TRACE_INTERFACE_VTABLE (do {
+ char *full_name = mono_type_full_name (m_class_get_byval_arg (cur_class));
+ printf (" slot %d of %s was %s adding override to %s\n", decl->slot, full_name, mono_method_full_name (prev_impl, 1), mono_method_full_name (impl, 1));
+ g_free (full_name);
+ } while (0));
+ }
+ }
+#endif /* FEATURE_COVARIANT_RETURNS */
+
+ if (mono_security_core_clr_enabled ())
+ mono_security_core_clr_check_override (klass, impl, decl);
+ }
+ }
+
+ TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER OVERRIDING NON-INTERFACE METHODS", FALSE));
+
+#ifdef FEATURE_COVARIANT_RETURNS
+ /*
+ * for each vtable slot that has one of the methods from klass that has the is_covariant_override_impl bit set,
+ * check that it is subsumed by the methods in each ancestor class.
+ *
+ * This check has to be done after both implicit and explicit non-interface method overrides are applied to the
+ * vtable. If it's done inline earlier, we could erroneously check an implicit override that should actually be
+ * ignored (because a more specific explicit override is applied).
+ */
+ if (!check_vtable_covariant_override_impls (klass, vtable, cur_slot))
+ goto fail;
+#endif
+
+ /*
+ * If a method occupies more than one place in the vtable, and it is
+ * overriden, then change the other occurrences too.
+ */
+ if (override_map) {
+ MonoMethod *cm;
+
+ for (i = 0; i < max_vtsize; ++i)
+ if (vtable [i]) {
+ TRACE_INTERFACE_VTABLE (printf ("checking slot %d method %s[%p] for overrides\n", i, mono_method_full_name (vtable [i], 1), vtable [i]));
+
+ cm = (MonoMethod *)g_hash_table_lookup (override_map, vtable [i]);
+ if (cm)
+ vtable [i] = cm;
+ }
+
+ g_hash_table_destroy (override_map);
+ override_map = NULL;
+ }
+
+ if (override_class_map)
+ g_hash_table_destroy (override_class_map);
+
+ if (conflict_map) {
+ handle_dim_conflicts (vtable, klass, conflict_map);
+ g_hash_table_destroy (conflict_map);
+ }
+
+ g_slist_free (virt_methods);
+ virt_methods = NULL;
+
+ 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] != NULL && vtable [i]->is_reabstracted == 1)
+ 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);
+ g_free (type_name);
+ g_free (method_name);
+
+ if (mono_print_vtable)
+ print_vtable_layout_result (klass, vtable, cur_slot);
+
+ g_free (vtable);
+ return;
+ }
+ }
+ }
+
+ if (mono_class_is_ginst (klass)) {
+ MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
+
+ mono_class_init_internal (gklass);
+
+ klass->vtable_size = MAX (gklass->vtable_size, cur_slot);
+ } else {
+ /* Check that the vtable_size value computed in mono_class_init_internal () is correct */
+ if (klass->vtable_size)
+ g_assert (cur_slot == klass->vtable_size);
+ klass->vtable_size = cur_slot;
+ }
+
+ /* Try to share the vtable with our parent. */
+ if (klass->parent && (klass->parent->vtable_size == klass->vtable_size) && (memcmp (klass->parent->vtable, vtable, sizeof (gpointer) * klass->vtable_size) == 0)) {
+ mono_memory_barrier ();
+ klass->vtable = klass->parent->vtable;
+ } else {
+ MonoMethod **tmp = (MonoMethod **)mono_class_alloc0 (klass, sizeof (gpointer) * klass->vtable_size);
+ memcpy (tmp, vtable, sizeof (gpointer) * klass->vtable_size);
+ mono_memory_barrier ();
+ klass->vtable = tmp;
+ }
+
+ DEBUG_INTERFACE_VTABLE (print_vtable_full (klass, klass->vtable, klass->vtable_size, first_non_interface_slot, "FINALLY", FALSE));
+ if (mono_print_vtable)
+ print_vtable_layout_result (klass, vtable, cur_slot);
+
+ g_free (vtable);
+
+ VERIFY_INTERFACE_VTABLE (mono_class_verify_vtable (klass));
+ return;
+
+fail:
+ {
+ char *name = mono_type_get_full_name (klass);
+ if (!is_ok (error))
+ mono_class_set_type_load_failure (klass, "VTable setup of type %s failed due to: %s", name, mono_error_get_message (error));
+ else
+ mono_class_set_type_load_failure (klass, "VTable setup of type %s failed", name);
+ mono_error_cleanup (error);
+ g_free (name);
+ if (mono_print_vtable)
+ print_vtable_layout_result (klass, vtable, cur_slot);
+
+
+ g_free (vtable);
+ if (override_map)
+ g_hash_table_destroy (override_map);
+ if (virt_methods)
+ g_slist_free (virt_methods);
+ }
+}
diff --git a/mono/metadata/class.c b/mono/metadata/class.c
index b4cd6a7e7a4..a1bd6326c14 100644
--- a/mono/metadata/class.c
+++ b/mono/metadata/class.c
@@ -3614,6 +3614,64 @@ mono_gparam_is_assignable_from (MonoClass *target, MonoClass *candidate)
return FALSE;
}
+static MonoType*
+mono_type_get_underlying_type_ignore_byref (MonoType *type)
+{
+ if (type->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (type->data.klass))
+ return mono_class_enum_basetype_internal (type->data.klass);
+ if (type->type == MONO_TYPE_GENERICINST && m_class_is_enumtype (type->data.generic_class->container_class))
+ return mono_class_enum_basetype_internal (type->data.generic_class->container_class);
+ return type;
+}
+
+/**
+ * mono_byref_type_is_assignable_from:
+ * \param type The type assignee
+ * \param ctype The type being assigned
+ * \param signature_assignment whether this is a signature assginment check according to ECMA rules, or reflection
+ *
+ * Given two byref types, returns \c TRUE if values of the second type are assignable to locations of the first type.
+ *
+ * The \p signature_assignment parameter affects comparing T& and U& where T and U are both reference types. Reflection
+ * does an IsAssignableFrom check for T and U here, but ECMA I.8.7.2 says that the verification types of T and U must be
+ * identical. If \p signature_assignment is \c TRUE we do an ECMA check, otherwise, reflection.
+ */
+gboolean
+mono_byref_type_is_assignable_from (MonoType *type, MonoType *ctype, gboolean signature_assignment)
+{
+ g_assert (type->byref);
+ g_assert (ctype->byref);
+ MonoType *t = mono_type_get_underlying_type_ignore_byref (type);
+ MonoType *ot = mono_type_get_underlying_type_ignore_byref (ctype);
+
+ MonoClass *klass = mono_class_from_mono_type_internal (t);
+ MonoClass *klassc = mono_class_from_mono_type_internal (ot);
+
+ if (mono_type_is_primitive (t)) {
+ return mono_type_is_primitive (ot) && m_class_get_instance_size (klass) == m_class_get_instance_size (klassc);
+ } else if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) {
+ return t->type == ot->type && t->data.generic_param->num == ot->data.generic_param->num;
+ } else if (t->type == MONO_TYPE_PTR || t->type == MONO_TYPE_FNPTR) {
+ return t->type == ot->type;
+ } else {
+ if (ot->type == MONO_TYPE_VAR || ot->type == MONO_TYPE_MVAR)
+ return FALSE;
+
+ if (m_class_is_valuetype (klass))
+ return klass == klassc;
+ if (m_class_is_valuetype (klassc))
+ return FALSE;
+ /*
+ * assignment compatability for location types, ECMA I.8.7.2 - two managed pointer types T& and U& are
+ * assignment compatible if the verification types of T and U are identical.
+ */
+ if (signature_assignment)
+ return klass == klassc;
+ /* the reflection IsAssignableFrom does a subtype comparison here for reference types only */
+ return mono_class_is_assignable_from_internal (klass, klassc);
+ }
+}
+
/**
* mono_class_is_assignable_from_internal:
* \param klass the class to be assigned to
@@ -3650,6 +3708,31 @@ mono_class_is_assignable_from (MonoClass *klass, MonoClass *oklass)
return result;
}
+/*
+ * ECMA I.8.7.3 general assignment compatability is defined in terms of an "intermediate type"
+ * whereas ECMA I.8.7.1 assignment compatability for signature types is defined in terms of a "reduced type".
+ *
+ * This matters when we're comparing arrays of IntPtr. IntPtr[] is generally
+ * assignable to int[] or long[], depending on architecture. But for signature
+ * compatability, IntPtr[] is distinct from both of them.
+ *
+ * Similarly for ulong* and IntPtr*, etc.
+ */
+static MonoClass*
+composite_type_to_reduced_element_type (MonoClass *array_klass)
+{
+ switch (m_class_get_byval_arg (m_class_get_element_class (array_klass))->type) {
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ return mono_defaults.int_class;
+ default:
+ return m_class_get_cast_class (array_klass);
+ }
+}
+
+static void
+mono_class_is_assignable_from_general (MonoClass *klass, MonoClass *oklass, gboolean signature_assignment, gboolean *result, MonoError *error);
+
/**
* mono_class_is_assignable_from_checked:
* \param klass the class to be assigned to
@@ -3664,6 +3747,20 @@ mono_class_is_assignable_from (MonoClass *klass, MonoClass *oklass)
void
mono_class_is_assignable_from_checked (MonoClass *klass, MonoClass *oklass, gboolean *result, MonoError *error)
{
+ const gboolean for_sig = FALSE;
+ mono_class_is_assignable_from_general (klass, oklass, for_sig, result, error);
+}
+
+void
+mono_class_signature_is_assignable_from (MonoClass *klass, MonoClass *oklass, gboolean *result, MonoError *error)
+{
+ const gboolean for_sig = TRUE;
+ mono_class_is_assignable_from_general (klass, oklass, for_sig, result, error);
+}
+
+void
+mono_class_is_assignable_from_general (MonoClass *klass, MonoClass *oklass, gboolean signature_assignment, gboolean *result, MonoError *error)
+{
g_assert (result);
if (klass == oklass) {
*result = TRUE;
@@ -3830,8 +3927,14 @@ mono_class_is_assignable_from_checked (MonoClass *klass, MonoClass *oklass, gboo
return;
}
- eclass = m_class_get_cast_class (klass);
- eoclass = m_class_get_cast_class (oklass);
+ if (signature_assignment) {
+ eclass = composite_type_to_reduced_element_type (klass);
+ eoclass = composite_type_to_reduced_element_type (oklass);
+ } else {
+ eclass = m_class_get_cast_class (klass);
+ eoclass = m_class_get_cast_class (oklass);
+ }
+
/*
* a is b does not imply a[] is b[] when a is a valuetype, and
@@ -3897,6 +4000,40 @@ mono_class_is_assignable_from_checked (MonoClass *klass, MonoClass *oklass, gboo
else
mono_class_is_assignable_from_checked (m_class_get_cast_class (klass), oklass, result, error);
return;
+ } else if (m_class_get_class_kind (klass) == MONO_CLASS_POINTER) {
+ if (m_class_get_class_kind (oklass) != MONO_CLASS_POINTER) {
+ *result = FALSE;
+ return;
+ }
+
+ if (m_class_get_byval_arg (klass)->type == MONO_TYPE_FNPTR) {
+ /*
+ * if both klass and oklass are fnptr, and they're equal, we would have returned at the
+ * beginning.
+ */
+ /* Is this right? or do we need to look at signature compatability? */
+ *result = FALSE;
+ return;
+ }
+
+ if (m_class_get_byval_arg (oklass)->type != MONO_TYPE_PTR) {
+ *result = FALSE;
+ }
+ g_assert (m_class_get_byval_arg (klass)->type == MONO_TYPE_PTR);
+
+ MonoClass *eclass;
+ MonoClass *eoclass;
+ if (signature_assignment) {
+ eclass = composite_type_to_reduced_element_type (klass);
+ eoclass = composite_type_to_reduced_element_type (oklass);
+ } else {
+ eclass = m_class_get_cast_class (klass);
+ eoclass = m_class_get_cast_class (oklass);
+ }
+
+ *result = (eclass == eoclass);
+ return;
+
} else if (klass == mono_defaults.object_class) {
if (m_class_get_class_kind (oklass) == MONO_CLASS_POINTER)
*result = FALSE;
diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c
index dc69ca08ab3..22587fa0d32 100644
--- a/mono/metadata/icall.c
+++ b/mono/metadata/icall.c
@@ -2086,16 +2086,6 @@ handle_enum:
}
#endif
-static MonoType*
-mono_type_get_underlying_type_ignore_byref (MonoType *type)
-{
- if (type->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (type->data.klass))
- return mono_class_enum_basetype_internal (type->data.klass);
- if (type->type == MONO_TYPE_GENERICINST && m_class_is_enumtype (type->data.generic_class->container_class))
- return mono_class_enum_basetype_internal (type->data.generic_class->container_class);
- return type;
-}
-
guint32
ves_icall_RuntimeTypeHandle_type_is_assignable_from (MonoReflectionTypeHandle ref_type, MonoReflectionTypeHandle ref_c, MonoError *error)
{
@@ -2110,26 +2100,7 @@ ves_icall_RuntimeTypeHandle_type_is_assignable_from (MonoReflectionTypeHandle re
return FALSE;
if (type->byref) {
- MonoType *t = mono_type_get_underlying_type_ignore_byref (type);
- MonoType *ot = mono_type_get_underlying_type_ignore_byref (ctype);
-
- klass = mono_class_from_mono_type_internal (t);
- klassc = mono_class_from_mono_type_internal (ot);
-
- if (mono_type_is_primitive (t)) {
- return mono_type_is_primitive (ot) && m_class_get_instance_size (klass) == m_class_get_instance_size (klassc);
- } else if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) {
- return t->type == ot->type && t->data.generic_param->num == ot->data.generic_param->num;
- } else if (t->type == MONO_TYPE_PTR || t->type == MONO_TYPE_FNPTR) {
- return t->type == ot->type;
- } else {
- if (ot->type == MONO_TYPE_VAR || ot->type == MONO_TYPE_MVAR)
- return FALSE;
-
- if (m_class_is_valuetype (klass))
- return klass == klassc;
- return m_class_is_valuetype (klass) == m_class_is_valuetype (klassc);
- }
+ return mono_byref_type_is_assignable_from (type, ctype, FALSE);
}
gboolean result;
diff --git a/mono/metadata/metadata-internals.h b/mono/metadata/metadata-internals.h
index 97f6c2dc303..34ef1d307d4 100644
--- a/mono/metadata/metadata-internals.h
+++ b/mono/metadata/metadata-internals.h
@@ -1069,6 +1069,9 @@ mono_metadata_parse_marshal_spec_full (MonoImage *image, MonoImage *parent_image
guint mono_metadata_generic_inst_hash (gconstpointer data);
gboolean mono_metadata_generic_inst_equal (gconstpointer ka, gconstpointer kb);
+gboolean
+mono_metadata_signature_equal_no_ret (MonoMethodSignature *sig1, MonoMethodSignature *sig2);
+
MONO_API void
mono_metadata_field_info_with_mempool (
MonoImage *meta,
diff --git a/mono/metadata/metadata.c b/mono/metadata/metadata.c
index f38fd4f5a3f..de7c1d037e1 100644
--- a/mono/metadata/metadata.c
+++ b/mono/metadata/metadata.c
@@ -5899,6 +5899,14 @@ mono_metadata_type_equal_full (MonoType *t1, MonoType *t2, gboolean signature_on
return do_mono_metadata_type_equal (t1, t2, signature_only);
}
+enum {
+ SIG_EQUIV_FLAG_NO_RET = 1,
+};
+
+gboolean
+signature_equiv (MonoMethodSignature *sig1, MonoMethodSignature *sig2, int flags);
+
+
/**
* mono_metadata_signature_equal:
* \param sig1 a signature
@@ -5911,6 +5919,19 @@ mono_metadata_type_equal_full (MonoType *t1, MonoType *t2, gboolean signature_on
gboolean
mono_metadata_signature_equal (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
{
+ return signature_equiv (sig1, sig2, 0);
+}
+
+gboolean
+mono_metadata_signature_equal_no_ret (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
+{
+ return signature_equiv (sig1, sig2, SIG_EQUIV_FLAG_NO_RET);
+}
+
+
+gboolean
+signature_equiv (MonoMethodSignature *sig1, MonoMethodSignature *sig2, int equiv_flags)
+{
int i;
if (sig1->hasthis != sig2->hasthis || sig1->param_count != sig2->param_count)
@@ -5939,6 +5960,8 @@ mono_metadata_signature_equal (MonoMethodSignature *sig1, MonoMethodSignature *s
return FALSE;
}
+ if ((equiv_flags & SIG_EQUIV_FLAG_NO_RET) != 0)
+ return TRUE;
if (!do_mono_metadata_type_equal (sig1->ret, sig2->ret, TRUE))
return FALSE;
return TRUE;
diff --git a/mono/mini/interp/interp-internals.h b/mono/mini/interp/interp-internals.h
index 3ddd2b9d879..895311b569b 100644
--- a/mono/mini/interp/interp-internals.h
+++ b/mono/mini/interp/interp-internals.h
@@ -321,6 +321,7 @@ enum_type:
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_PTR:
+ case MONO_TYPE_FNPTR:
return MINT_TYPE_I;
case MONO_TYPE_R4:
return MINT_TYPE_R4;
diff --git a/msvc/libmonoruntime-common.targets b/msvc/libmonoruntime-common.targets
index f3ea42ac0e0..9debec84a0e 100644
--- a/msvc/libmonoruntime-common.targets
+++ b/msvc/libmonoruntime-common.targets
@@ -20,8 +20,10 @@
<ClInclude Include="$(MonoSourceLocation)\mono\metadata\class-inlines.h" />
<ClCompile Include="$(MonoSourceLocation)\mono\metadata\class-init.c" />
<ClInclude Include="$(MonoSourceLocation)\mono\metadata\class-init.h" />
+ <ClInclude Include="$(MonoSourceLocation)\mono\metadata\class-init-internals.h" />
<ClCompile Include="$(MonoSourceLocation)\mono\metadata\class-accessors.c" />
<ClInclude Include="$(MonosourceLocation)\mono\metadata\class-private-definition.h" />
+ <ClCompile Include="$(MonoSourceLocation)\mono\metadata\class-setup-vtable.c" />
<ClCompile Include="$(MonoSourceLocation)\mono\metadata\cominterop.c" />
<ClInclude Include="$(MonoSourceLocation)\mono\metadata\cominterop.h" />
<ClInclude Include="$(MonoSourceLocation)\mono\metadata\console-io.h" />
diff --git a/msvc/libmonoruntime-common.targets.filters b/msvc/libmonoruntime-common.targets.filters
index ea308b152b6..b25559f1fb1 100644
--- a/msvc/libmonoruntime-common.targets.filters
+++ b/msvc/libmonoruntime-common.targets.filters
@@ -52,9 +52,15 @@
<ClInclude Include="$(MonoSourceLocation)\mono\metadata\class-init.h">
<Filter>Header Files$(MonoRuntimeFilterSubFolder)\common</Filter>
</ClInclude>
+ <ClInclude Include="$(MonoSourceLocation)\mono\metadata\class-init-internals.h">
+ <Filter>Header Files$(MonoRuntimeFilterSubFolder)\common</Filter>
+ </ClInclude>
<ClInclude Include="$(MonoSourceLocation)\mono\metadata\class-private-definition.h">
<Filter>Header Files$(MonoRuntimeFilterSubFolder)\common</Filter>
</ClInclude>
+ <ClCompile Include="$(MonoSourceLocation)\mono\metadata\class-setup-vtable.c">
+ <Filter>Source Files$(MonoRuntimeFilterSubFolder)\common</Filter>
+ </ClCompile>
<ClCompile Include="$(MonoSourceLocation)\mono\metadata\cominterop.c">
<Filter>Source Files$(MonoRuntimeFilterSubFolder)\common</Filter>
</ClCompile>