Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mono / metadata / class.c
index cc1cf0799f357c198adf586f99dc1dadc5c121bf..6378847ba3a2e998f671ead8e91fd11819112c69 100644 (file)
@@ -67,11 +67,77 @@ static char* mono_assembly_name_from_token (MonoImage *image, guint32 type_token
 static void mono_field_resolve_type (MonoClassField *field, MonoError *error);
 static guint32 mono_field_resolve_flags (MonoClassField *field);
 static void mono_class_setup_vtable_full (MonoClass *class, GList *in_setup);
+static void mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gklass);
 
 
 void (*mono_debugger_class_init_func) (MonoClass *klass) = NULL;
 void (*mono_debugger_class_loaded_methods_func) (MonoClass *klass) = NULL;
 
+
+/*
+We use gclass recording to allow recursive system f types to be referenced by a parent.
+
+Given the following type hierarchy:
+
+class TextBox : TextBoxBase<TextBox> {}
+class TextBoxBase<T> : TextInput<TextBox> where T : TextBoxBase<T> {}
+class TextInput<T> : Input<T> where T: TextInput<T> {}
+class Input<T> {}
+
+The runtime tries to load TextBoxBase<>.
+To load TextBoxBase<> to do so it must resolve the parent which is TextInput<TextBox>.
+To instantiate TextInput<TextBox> it must resolve TextInput<> and TextBox.
+To load TextBox it must resolve the parent which is TextBoxBase<TextBox>.
+
+At this point the runtime must instantiate TextBoxBase<TextBox>. Both types are partially loaded 
+at this point, iow, both are registered in the type map and both and a NULL parent. This means
+that the resulting generic instance will have a NULL parent, which is wrong and will cause breakage.
+
+To fix that what we do is to record all generic instantes created while resolving the parent of
+any generic type definition and, after resolved, correct the parent field if needed.
+
+*/
+static int record_gclass_instantiation;
+static GSList *gclass_recorded_list;
+typedef gboolean (*gclass_record_func) (MonoClass*, void*);
+
+/* 
+ * LOCKING: loader lock must be held until pairing disable_gclass_recording is called.
+*/
+static void
+enable_gclass_recording (void)
+{
+       ++record_gclass_instantiation;
+}
+
+/* 
+ * LOCKING: loader lock must be held since pairing enable_gclass_recording was called.
+*/
+static void
+disable_gclass_recording (gclass_record_func func, void *user_data)
+{
+       GSList **head = &gclass_recorded_list;
+
+       g_assert (record_gclass_instantiation > 0);
+       --record_gclass_instantiation;
+
+       while (*head) {
+               GSList *node = *head;
+               if (func ((MonoClass*)node->data, user_data)) {
+                       *head = node->next;
+                       g_slist_free_1 (node);
+               } else {
+                       head = &node->next;
+               }
+       }
+
+       /* We automatically discard all recorded gclasses when disabled. */
+       if (!record_gclass_instantiation && gclass_recorded_list) {
+               g_slist_free (gclass_recorded_list);
+               gclass_recorded_list = NULL;
+       }
+}
+
 /*
  * mono_class_from_typeref:
  * @image: a MonoImage
@@ -513,6 +579,21 @@ mono_class_is_open_constructed_type (MonoType *t)
        }
 }
 
+/*
+This is a simple function to catch the most common bad instances of generic types.
+Specially those that might lead to further failures in the runtime.
+*/
+static gboolean
+is_valid_generic_argument (MonoType *type)
+{
+       switch (type->type) {
+       case MONO_TYPE_VOID:
+       case MONO_TYPE_TYPEDBYREF:
+               return FALSE;
+       }
+       return TRUE;
+}
+
 static MonoType*
 inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error)
 {
@@ -532,6 +613,12 @@ inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *cont
                        return NULL;
                }
 
+               if (!is_valid_generic_argument (inst->type_argv [num])) {
+                       MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
+                       mono_error_set_bad_image (error, image, "MVAR %d (%s) cannot be expanded with type 0x%x",
+                               num, info ? info->name : "", inst->type_argv [num]->type);
+                       return NULL;                    
+               }
                /*
                 * Note that the VAR/MVAR cases are different from the rest.  The other cases duplicate @type,
                 * while the VAR/MVAR duplicates a type from the context.  So, we need to ensure that the
@@ -554,6 +641,12 @@ inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *cont
                                num, info ? info->name : "", inst->type_argc);
                        return NULL;
                }
+               if (!is_valid_generic_argument (inst->type_argv [num])) {
+                       MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
+                       mono_error_set_bad_image (error, image, "VAR %d (%s) cannot be expanded with type 0x%x",
+                               num, info ? info->name : "", inst->type_argv [num]->type);
+                       return NULL;                    
+               }
                nt = mono_metadata_type_dup (image, inst->type_argv [num]);
                nt->byref = type->byref;
                nt->attrs = type->attrs;
@@ -1620,7 +1713,7 @@ mono_class_layout_fields (MonoClass *class)
         */
         /* corlib is missing [StructLayout] directives in many places */
        if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT) {
-               if (class->byval_arg.type != MONO_TYPE_VALUETYPE)
+               if (!class->valuetype)
                        gc_aware_layout = TRUE;
        }
 
@@ -1777,6 +1870,7 @@ mono_class_layout_fields (MonoClass *class)
                                continue;
 
                        size = mono_type_size (field->type, &align);
+                       align = class->packing_size ? MIN (class->packing_size, align): align;
                        class->min_align = MAX (align, class->min_align);
 
                        /*
@@ -1800,6 +1894,10 @@ mono_class_layout_fields (MonoClass *class)
                        real_size = MAX (real_size, size + field->offset);
                }
                class->instance_size = MAX (real_size, class->instance_size);
+               if (class->instance_size & (class->min_align - 1)) {
+                       class->instance_size += class->min_align - 1;
+                       class->instance_size &= ~(class->min_align - 1);
+               }
                break;
        }
 
@@ -5157,6 +5255,7 @@ mono_class_setup_mono_type (MonoClass *class)
 
 }
 
+#ifndef DISABLE_COM
 /*
  * COM initialization (using mono_init_com_types) is delayed until needed. 
  * However when a [ComImport] attribute is present on a type it will trigger
@@ -5180,6 +5279,7 @@ init_com_from_comimport (MonoClass *class)
        /* FIXME : we should add an extra checks to ensure COM can be initialized properly before continuing */
        mono_init_com_types ();
 }
+#endif /*DISABLE_COM*/
 
 /*
  * LOCKING: this assumes the loader lock is held
@@ -5206,11 +5306,13 @@ mono_class_setup_parent (MonoClass *class, MonoClass *parent)
 
        if (!MONO_CLASS_IS_INTERFACE (class)) {
                /* Imported COM Objects always derive from __ComObject. */
+#ifndef DISABLE_COM
                if (MONO_CLASS_IS_IMPORT (class)) {
                        init_com_from_comimport (class);
                        if (parent == mono_defaults.object_class)
                                parent = mono_defaults.com_object_class;
                }
+#endif
                if (!parent) {
                        /* set the parent to something useful and safe, but mark the type as broken */
                        parent = mono_defaults.object_class;
@@ -5256,8 +5358,10 @@ mono_class_setup_parent (MonoClass *class, MonoClass *parent)
                /*class->enumtype = class->parent->enumtype; */
        } else {
                /* initialize com types if COM interfaces are present */
+#ifndef DISABLE_COM
                if (MONO_CLASS_IS_IMPORT (class))
                        init_com_from_comimport (class);
+#endif
                class->parent = NULL;
        }
 
@@ -5305,6 +5409,21 @@ mono_class_setup_supertypes (MonoClass *class)
        mono_atomic_store_release (&class->supertypes, supertypes);
 }
 
+static gboolean
+fix_gclass_incomplete_instantiation (MonoClass *gclass, void *user_data)
+{
+       MonoClass *gtd = (MonoClass*)user_data;
+       /* Only try to fix generic instances of @gtd */
+       if (gclass->generic_class->container_class != gtd)
+               return FALSE;
+
+       /* Check if the generic instance has no parent. */
+       if (gtd->parent && !gclass->parent)
+               mono_generic_class_setup_parent (gclass, gtd);
+
+       return TRUE;
+}
+
 /**
  * mono_class_create_from_typedef:
  * @image: image where the token is valid
@@ -5370,6 +5489,9 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token)
                context = &class->generic_container->context;
        }
 
+       if (class->generic_container)
+               enable_gclass_recording ();
+
        if (cols [MONO_TYPEDEF_EXTENDS]) {
                MonoClass *tmp;
                guint32 parent_token = mono_metadata_token_from_dor (cols [MONO_TYPEDEF_EXTENDS]);
@@ -5407,6 +5529,9 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token)
        /* uses ->valuetype, which is initialized by mono_class_setup_parent above */
        mono_class_setup_mono_type (class);
 
+       if (class->generic_container)
+               disable_gclass_recording (fix_gclass_incomplete_instantiation, class);
+
        /* 
         * This might access class->byval_arg for recursion generated by generic constraints,
         * so it has to come after setup_mono_type ().
@@ -5543,6 +5668,31 @@ mono_class_get_nullable_param (MonoClass *klass)
        return mono_class_from_mono_type (klass->generic_class->context.class_inst->type_argv [0]);
 }
 
+static void
+mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gtd)
+{
+       if (gtd->parent) {
+               MonoError error;
+               MonoGenericClass *gclass = klass->generic_class;
+
+               klass->parent = mono_class_inflate_generic_class_checked (gtd->parent, mono_generic_class_get_context (gclass), &error);
+               if (!mono_error_ok (&error)) {
+                       /*Set parent to something safe as the runtime doesn't handle well this kind of failure.*/
+                       klass->parent = mono_defaults.object_class;
+                       mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, NULL);
+                       mono_error_cleanup (&error);
+               }
+       }
+       if (klass->parent)
+               mono_class_setup_parent (klass, klass->parent);
+
+       if (klass->enumtype) {
+               klass->cast_class = gtd->cast_class;
+               klass->element_class = gtd->element_class;
+       }
+}
+
+
 /*
  * Create the `MonoClass' for an instantiation of a generic type.
  * We only do this if we actually need it.
@@ -5565,6 +5715,9 @@ mono_generic_class_get_class (MonoGenericClass *gclass)
 
        gklass = gclass->container_class;
 
+       if (record_gclass_instantiation > 0)
+               gclass_recorded_list = g_slist_append (gclass_recorded_list, klass);
+
        if (gklass->nested_in) {
                /* The nested_in type should not be inflated since it's possible to produce a nested type with less generic arguments*/
                klass->nested_in = gklass->nested_in;
@@ -5599,24 +5752,7 @@ mono_generic_class_get_class (MonoGenericClass *gclass)
         * We use the generic type definition to look for nested classes.
         */
 
-       if (gklass->parent) {
-               MonoError error;
-               klass->parent = mono_class_inflate_generic_class_checked (gklass->parent, mono_generic_class_get_context (gclass), &error);
-               if (!mono_error_ok (&error)) {
-                       /*Set parent to something safe as the runtime doesn't handle well this kind of failure.*/
-                       klass->parent = mono_defaults.object_class;
-                       mono_class_set_failure (klass, MONO_EXCEPTION_TYPE_LOAD, NULL);
-                       mono_error_cleanup (&error);
-               }
-       }
-
-       if (klass->parent)
-               mono_class_setup_parent (klass, klass->parent);
-
-       if (klass->enumtype) {
-               klass->cast_class = gklass->cast_class;
-               klass->element_class = gklass->element_class;
-       }
+       mono_generic_class_setup_parent (klass, gklass);
 
        if (gclass->is_dynamic) {
                klass->inited = 1;
@@ -5743,7 +5879,7 @@ make_generic_param_class (MonoGenericParam *param, MonoImage *image, gboolean is
 static MonoClass *
 get_anon_gparam_class (MonoGenericParam *param, gboolean is_mvar)
 {
-       int n = mono_generic_param_num (param);
+       int n = mono_generic_param_num (param) | ((guint32)param->serial << 16);
        MonoImage *image = param->image;
        GHashTable *ht;
 
@@ -5766,7 +5902,7 @@ get_anon_gparam_class (MonoGenericParam *param, gboolean is_mvar)
 static void
 set_anon_gparam_class (MonoGenericParam *param, gboolean is_mvar, MonoClass *klass)
 {
-       int n = mono_generic_param_num (param);
+       int n = mono_generic_param_num (param) | ((guint32)param->serial << 16);
        MonoImage *image = param->image;
        GHashTable *ht;