[reflection] Use coop handles for MonoMethod icalls (#4272)
[mono.git] / mono / metadata / class.c
index cbb9ed9ce6524ba4b3e20f3cf10830f139aa909f..4cf5fcf45754804080565adb1ab40e4b2f8c377e 100644 (file)
@@ -1651,6 +1651,7 @@ static void
 init_sizes_with_info (MonoClass *klass, MonoCachedClassInfo *cached_info)
 {
        if (cached_info) {
+               mono_loader_lock ();
                klass->instance_size = cached_info->instance_size;
                klass->sizes.class_size = cached_info->class_size;
                klass->packing_size = cached_info->packing_size;
@@ -1659,6 +1660,7 @@ init_sizes_with_info (MonoClass *klass, MonoCachedClassInfo *cached_info)
                klass->has_references = cached_info->has_references;
                klass->has_static_refs = cached_info->has_static_refs;
                klass->no_special_static_fields = cached_info->no_special_static_fields;
+               mono_loader_unlock ();
        }
        else {
                if (!klass->size_inited)
@@ -3077,62 +3079,6 @@ print_implemented_interfaces (MonoClass *klass)
        }
 }
 
-static MonoClass*
-inflate_class_one_arg (MonoClass *gtype, MonoClass *arg0)
-{
-       MonoType *args [1];
-       args [0] = &arg0->byval_arg;
-
-       return mono_class_bind_generic_parameters (gtype, 1, args, FALSE);
-}
-
-static MonoClass*
-array_class_get_if_rank (MonoClass *klass, guint rank)
-{
-       return rank ? mono_array_class_get (klass, rank) : klass;
-}
-
-static void
-fill_valuetype_array_derived_types (MonoClass **valuetype_types, MonoClass *eclass, int rank)
-{
-       valuetype_types [0] = eclass;
-       if (eclass == mono_defaults.int16_class)
-               valuetype_types [1] = mono_defaults.uint16_class;
-       else if (eclass == mono_defaults.uint16_class)
-               valuetype_types [1] = mono_defaults.int16_class;
-       else if (eclass == mono_defaults.int32_class)
-               valuetype_types [1] = mono_defaults.uint32_class;
-       else if (eclass == mono_defaults.uint32_class)
-               valuetype_types [1] = mono_defaults.int32_class;
-       else if (eclass == mono_defaults.int64_class)
-               valuetype_types [1] = mono_defaults.uint64_class;
-       else if (eclass == mono_defaults.uint64_class)
-               valuetype_types [1] = mono_defaults.int64_class;
-       else if (eclass == mono_defaults.byte_class)
-               valuetype_types [1] = mono_defaults.sbyte_class;
-       else if (eclass == mono_defaults.sbyte_class)
-               valuetype_types [1] = mono_defaults.byte_class;
-       else if (eclass->enumtype && mono_class_enum_basetype (eclass))
-               valuetype_types [1] = mono_class_from_mono_type (mono_class_enum_basetype (eclass));
-}
-
-static GENERATE_GET_CLASS_WITH_CACHE (generic_icollection, System.Collections.Generic, ICollection`1)
-static GENERATE_GET_CLASS_WITH_CACHE (generic_ienumerable, System.Collections.Generic, IEnumerable`1)
-static GENERATE_GET_CLASS_WITH_CACHE (generic_ienumerator, System.Collections.Generic, IEnumerator`1)
-static GENERATE_GET_CLASS_WITH_CACHE (generic_ireadonlylist, System.Collections.Generic, IReadOnlyList`1)
-static GENERATE_GET_CLASS_WITH_CACHE (generic_ireadonlycollection, System.Collections.Generic, IReadOnlyCollection`1)
-
-static int
-find_array_interface (MonoClass *klass, const char *name)
-{
-       int i;
-       for (i = 0; i < klass->interface_count; ++i) {
-               if (strcmp (klass->interfaces [i]->name, name) == 0)
-                       return i;
-       }
-       return -1;
-}
-
 /*
  * 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.
@@ -3192,15 +3138,6 @@ find_interface (int num_ifaces, MonoClass **interfaces_full, MonoClass *ic)
        }
 }
 
-static int
-find_interface_offset (int num_ifaces, MonoClass **interfaces_full, int *interface_offsets_full, MonoClass *ic)
-{
-       int i = find_interface (num_ifaces, interfaces_full, ic);
-       if (i >= 0)
-               return interface_offsets_full [i];
-       return -1;
-}
-
 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)
 {
@@ -4537,6 +4474,8 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
        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 */
        if (!mono_class_is_abstract (klass)) {
                for (i = 0; i < cur_slot; ++i) {
@@ -5081,13 +5020,14 @@ mono_class_init (MonoClass *klass)
        init_list = g_slist_remove (init_list, klass);
        mono_native_tls_set_value (init_pending_tls_id, init_list);
 
-       /* Because of the double-checking locking pattern */
-       mono_memory_barrier ();
-       klass->inited = 1;
-
        if (locked)
                mono_loader_unlock ();
 
+       /* Leave this for last */
+       mono_loader_lock ();
+       klass->inited = 1;
+       mono_loader_unlock ();
+
        return !mono_class_has_failure (klass);
 }
 
@@ -5142,16 +5082,14 @@ mono_class_has_finalizer (MonoClass *klass)
                }
        }
 
-       mono_image_lock (klass->image);
-
+       mono_loader_lock ();
        if (!klass->has_finalize_inited) {
                klass->has_finalize = has_finalize ? 1 : 0;
 
                mono_memory_barrier ();
                klass->has_finalize_inited = TRUE;
        }
-
-       mono_image_unlock (klass->image);
+       mono_loader_unlock ();
 
        return klass->has_finalize;
 }
@@ -5804,6 +5742,7 @@ mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gtd)
                        mono_error_cleanup (&error);
                }
        }
+       mono_loader_lock ();
        if (klass->parent)
                mono_class_setup_parent (klass, klass->parent);
 
@@ -5811,6 +5750,7 @@ mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gtd)
                klass->cast_class = gtd->cast_class;
                klass->element_class = gtd->element_class;
        }
+       mono_loader_unlock ();
 }
 
 gboolean
@@ -8346,7 +8286,7 @@ mono_class_implement_interface_slow (MonoClass *target, MonoClass *candidate)
 
                /*A TypeBuilder can have more interfaces on tb->interfaces than on candidate->interfaces*/
                if (image_is_dynamic (candidate->image) && !candidate->wastypebuilder) {
-                       MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info (candidate);
+                       MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_class_get_ref_info_raw (candidate); /* FIXME use handles */
                        int j;
                        if (tb && tb->interfaces) {
                                for (j = mono_array_length (tb->interfaces) - 1; j >= 0; --j) {
@@ -8467,9 +8407,14 @@ mono_class_get_cctor (MonoClass *klass)
                return mono_class_get_method_from_name_flags (klass, ".cctor", -1, METHOD_ATTRIBUTE_SPECIAL_NAME);
        }
 
+       mono_class_init (klass);
+
        if (!klass->has_cctor)
                return NULL;
 
+       if (mono_class_is_ginst (klass) && !klass->methods)
+               return mono_class_get_inflated_method (klass, mono_class_get_cctor (mono_class_get_generic_class (klass)->container_class));
+
        if (mono_class_get_cached_class_info (klass, &cached_info)) {
                MonoError error;
                MonoMethod *result = mono_get_method_checked (klass->image, cached_info.cctor_token, klass, NULL, &error);
@@ -8478,9 +8423,6 @@ mono_class_get_cctor (MonoClass *klass)
                return result;
        }
 
-       if (mono_class_is_ginst (klass) && !klass->methods)
-               return mono_class_get_inflated_method (klass, mono_class_get_cctor (mono_class_get_generic_class (klass)->container_class));
-
        return mono_class_get_method_from_name_flags (klass, ".cctor", -1, METHOD_ATTRIBUTE_SPECIAL_NAME);
 }
 
@@ -9275,8 +9217,12 @@ setup_nested_types (MonoClass *klass)
        if (klass->nested_classes_inited)
                return;
 
-       if (!klass->type_token)
+       if (!klass->type_token) {
+               mono_loader_lock ();
                klass->nested_classes_inited = TRUE;
+               mono_loader_unlock ();
+               return;
+       }
 
        i = mono_metadata_nesting_typedef (klass->image, klass->type_token, 1);
        classes = NULL;
@@ -10495,8 +10441,7 @@ mono_class_setup_interfaces (MonoClass *klass, MonoError *error)
                interfaces = NULL;
        }
 
-       mono_image_lock (klass->image);
-
+       mono_loader_lock ();
        if (!klass->interfaces_inited) {
                klass->interface_count = interface_count;
                klass->interfaces = interfaces;
@@ -10505,8 +10450,7 @@ mono_class_setup_interfaces (MonoClass *klass, MonoError *error)
 
                klass->interfaces_inited = TRUE;
        }
-
-       mono_image_unlock (klass->image);
+       mono_loader_unlock ();
 }
 
 static void
@@ -10587,7 +10531,6 @@ mono_field_resolve_flags (MonoClassField *field)
        MonoClass *gtd = mono_class_is_ginst (klass) ? mono_class_get_generic_type_definition (klass) : NULL;
        int field_idx = field - klass->fields;
 
-
        if (gtd) {
                MonoClassField *gfield = &gtd->fields [field_idx];
                return mono_field_get_flags (gfield);
@@ -10651,3 +10594,156 @@ mono_class_full_name (MonoClass *klass)
 
 /* Declare all shared lazy type lookup functions */
 GENERATE_TRY_GET_CLASS_WITH_CACHE (safehandle, System.Runtime.InteropServices, SafeHandle)
+
+/**
+ * mono_method_get_base_method:
+ * @method: a method
+ * @definition: if true, get the definition
+ * @error: set on failure
+ *
+ * Given a virtual method associated with a subclass, return the corresponding
+ * method from an ancestor.  If @definition is FALSE, returns the method in the
+ * superclass of the given method.  If @definition is TRUE, return the method
+ * in the ancestor class where it was first declared.  The type arguments will
+ * be inflated in the ancestor classes.  If the method is not associated with a
+ * class, or isn't virtual, returns the method itself.  On failure returns NULL
+ * and sets @error.
+ */
+MonoMethod*
+mono_method_get_base_method (MonoMethod *method, gboolean definition, MonoError *error)
+{
+       MonoClass *klass, *parent;
+       MonoGenericContext *generic_inst = NULL;
+       MonoMethod *result = NULL;
+       int slot;
+
+       if (method->klass == NULL)
+               return method;
+
+       if (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
+           MONO_CLASS_IS_INTERFACE (method->klass) ||
+           method->flags & METHOD_ATTRIBUTE_NEW_SLOT)
+               return method;
+
+       slot = mono_method_get_vtable_slot (method);
+       if (slot == -1)
+               return method;
+
+       klass = method->klass;
+       if (mono_class_is_ginst (klass)) {
+               generic_inst = mono_class_get_context (klass);
+               klass = mono_class_get_generic_class (klass)->container_class;
+       }
+
+retry:
+       if (definition) {
+               /* At the end of the loop, klass points to the eldest class that has this virtual function slot. */
+               for (parent = klass->parent; parent != NULL; parent = parent->parent) {
+                       /* on entry, klass is either a plain old non-generic class and generic_inst == NULL
+                          or klass is the generic container class and generic_inst is the instantiation.
+
+                          when we go to the parent, if the parent is an open constructed type, we need to
+                          replace the type parameters by the definitions from the generic_inst, and then take it
+                          apart again into the klass and the generic_inst.
+
+                          For cases like this:
+                          class C<T> : B<T, int> {
+                              public override void Foo () { ... }
+                          }
+                          class B<U,V> : A<HashMap<U,V>> {
+                              public override void Foo () { ... }
+                          }
+                          class A<X> {
+                              public virtual void Foo () { ... }
+                          }
+
+                          if at each iteration the parent isn't open, we can skip inflating it.  if at some
+                          iteration the parent isn't generic (after possible inflation), we set generic_inst to
+                          NULL;
+                       */
+                       MonoGenericContext *parent_inst = NULL;
+                       if (mono_class_is_open_constructed_type (mono_class_get_type (parent))) {
+                               parent = mono_class_inflate_generic_class_checked (parent, generic_inst, error);
+                               return_val_if_nok  (error, NULL);
+                       }
+                       if (mono_class_is_ginst (parent)) {
+                               parent_inst = mono_class_get_context (parent);
+                               parent = mono_class_get_generic_class (parent)->container_class;
+                       }
+
+                       mono_class_setup_vtable (parent);
+                       if (parent->vtable_size <= slot)
+                               break;
+                       klass = parent;
+                       generic_inst = parent_inst;
+               }
+       } else {
+               klass = klass->parent;
+               if (!klass)
+                       return method;
+               if (mono_class_is_open_constructed_type (mono_class_get_type (klass))) {
+                       klass = mono_class_inflate_generic_class_checked (klass, generic_inst, error);
+                       return_val_if_nok (error, NULL);
+
+                       generic_inst = NULL;
+               }
+               if (mono_class_is_ginst (klass)) {
+                       generic_inst = mono_class_get_context (klass);
+                       klass = mono_class_get_generic_class (klass)->container_class;
+               }
+
+       }
+
+       if (generic_inst) {
+               klass = mono_class_inflate_generic_class_checked (klass, generic_inst, error);
+               return_val_if_nok (error, NULL);
+       }
+
+       if (klass == method->klass)
+               return method;
+
+       /*This is possible if definition == FALSE.
+        * Do it here to be really sure we don't read invalid memory.
+        */
+       if (slot >= klass->vtable_size)
+               return method;
+
+       mono_class_setup_vtable (klass);
+
+       result = klass->vtable [slot];
+       if (result == NULL) {
+               /* It is an abstract method */
+               gboolean found = FALSE;
+               gpointer iter = NULL;
+               while ((result = mono_class_get_methods (klass, &iter))) {
+                       if (result->slot == slot) {
+                               found = TRUE;
+                               break;
+                       }
+               }
+               /* found might be FALSE if we looked in an abstract class
+                * that doesn't override an abstract method of its
+                * parent: 
+                *   abstract class Base {
+                *     public abstract void Foo ();
+                *   }
+                *   abstract class Derived : Base { }
+                *   class Child : Derived {
+                *     public override void Foo () { }
+                *  }
+                *
+                *  if m was Child.Foo and we ask for the base method,
+                *  then we get here with klass == Derived and found == FALSE
+                */
+               /* but it shouldn't be the case that if we're looking
+                * for the definition and didn't find a result; the
+                * loop above should've taken us as far as we could
+                * go! */
+               g_assert (!(definition && !found));
+               if (!found)
+                       goto retry;
+       }
+
+       g_assert (result != NULL);
+       return result;
+}