Fix crash on 'make run-test' in mcs/errors
[mono.git] / mono / metadata / metadata.c
index ad88ae2c3dd4dcbc9ad2d13b47b10eebdec656d4..7c67e451efa6c46ed9ed95b866175a866056ce5f 100644 (file)
@@ -435,6 +435,12 @@ mono_tables_names [] = {
 
 #endif
 
+/* Auxiliary structure used for caching inflated signatures */
+typedef struct {
+       MonoMethodSignature *sig;
+       MonoGenericContext context;
+} MonoInflatedMethodSignature;
+
 /**
  * mono_meta_table_name:
  * @table: table index
@@ -1353,6 +1359,12 @@ static int next_generic_inst_id = 0;
  */
 static GHashTable *generic_method_cache = NULL;
 
+/*
+ * Protected by the loader lock.
+ * It has a MonoInflatedMethodSignature* as key and value.
+ */
+static GHashTable *generic_signature_cache = NULL;
+
 static guint mono_generic_class_hash (gconstpointer data);
 
 /*
@@ -1465,10 +1477,13 @@ mono_metadata_cleanup (void)
        g_hash_table_destroy (generic_class_cache);
        if (generic_method_cache)
                g_hash_table_destroy (generic_method_cache);
+       if (generic_signature_cache)
+               g_hash_table_destroy (generic_signature_cache);
        type_cache = NULL;
        generic_inst_cache = NULL;
        generic_class_cache = NULL;
        generic_method_cache = NULL;
+       generic_signature_cache = NULL;
 }
 
 /**
@@ -1903,6 +1918,22 @@ mono_metadata_free_method_signature (MonoMethodSignature *sig)
        */
 }
 
+void
+mono_metadata_free_inflated_signature (MonoMethodSignature *sig)
+{
+       int i;
+
+       /* Allocated in inflate_generic_signature () */
+       if (sig->ret)
+               mono_metadata_free_type (sig->ret);
+       for (i = 0; i < sig->param_count; ++i) {
+               if (sig->params [i])
+                       mono_metadata_free_type (sig->params [i]);
+       }
+       /* FIXME: The signature is allocated from a mempool */
+       //g_free (sig);
+}
+
 static gboolean
 inflated_method_equal (gconstpointer a, gconstpointer b)
 {
@@ -1920,6 +1951,28 @@ inflated_method_hash (gconstpointer a)
        return mono_metadata_generic_context_hash (&ma->context) ^ mono_aligned_addr_hash (ma->declaring);
 }
 
+static gboolean
+inflated_signature_equal (gconstpointer a, gconstpointer b)
+{
+       const MonoInflatedMethodSignature *sig1 = a;
+       const MonoInflatedMethodSignature *sig2 = b;
+
+       /* sig->sig is assumed to be canonized */
+       if (sig1->sig != sig2->sig)
+               return FALSE;
+       /* The generic instances are canonized */
+       return mono_metadata_generic_context_equal (&sig1->context, &sig2->context);
+}
+
+static guint
+inflated_signature_hash (gconstpointer a)
+{
+       const MonoInflatedMethodSignature *sig = a;
+
+       /* sig->sig is assumed to be canonized */
+       return mono_metadata_generic_context_hash (&sig->context) ^ mono_aligned_addr_hash (sig->sig);
+}
+
 /*static void
 dump_ginst (MonoGenericInst *ginst)
 {
@@ -1945,59 +1998,80 @@ typedef struct {
        GSList *gclass_list;
 } CleanForImageUserData;
 
+static gboolean
+type_in_image (MonoType *type, CleanForImageUserData *user_data)
+{
+       MonoClass *klass;
+
+       /* Avoid allocations.  We just want _some_ MonoClass reachable from @type.  */
+retry:
+       switch (type->type) {
+       case MONO_TYPE_GENERICINST:
+               return gclass_in_image (type->data.generic_class, NULL, user_data);
+       case MONO_TYPE_PTR:
+               type = type->data.type;
+               goto retry;
+       case MONO_TYPE_SZARRAY:
+               klass = type->data.klass;
+               break;
+       case MONO_TYPE_ARRAY:
+               klass = type->data.array->eklass;
+               break;
+       case MONO_TYPE_FNPTR: {
+               gpointer iter = NULL;
+               MonoMethodSignature *sig = type->data.method;
+               MonoType *p;
+               if (type_in_image (mono_signature_get_return_type (sig), user_data))
+                       return TRUE;
+               while ((p = mono_signature_get_params (sig, &iter)) != NULL)
+                       if (type_in_image (p, user_data))
+                               return TRUE;
+               return FALSE;
+       }
+       default:
+               /* At this point, we should've avoided all potential allocations in mono_class_from_mono_type () */
+               klass = mono_class_from_mono_type (type);
+               break;
+       }
+
+       return klass->image == user_data->image;
+}
+
 static gboolean
 ginst_in_image (gpointer key, gpointer value, gpointer data)
 {
-       CleanForImageUserData *user_data = (CleanForImageUserData*)data;
-       MonoImage *image = user_data->image;
+       CleanForImageUserData *user_data = data;
        MonoGenericInst *ginst = key;
-       MonoClass *klass;
        int i;
+
        for (i = 0; i < ginst->type_argc; ++i) {
-               MonoType *type = ginst->type_argv [i];
+               if (type_in_image (ginst->type_argv [i], user_data))
+                       break;
+       }
 
-               /* FIXME: Avoid a possible mono_class_inst inside mono_class_from_mono_type */
-               if (type->type == MONO_TYPE_GENERICINST) {
-                       if (gclass_in_image (type->data.generic_class, NULL, data)) {
-                               if (!g_slist_find (user_data->ginst_list, ginst))
-                                       user_data->ginst_list = g_slist_append (user_data->ginst_list, ginst);
-                               return TRUE;
-                       }
-                       continue;
-               }
+       if (i == ginst->type_argc)
+               return FALSE;
 
-               klass = mono_class_from_mono_type (ginst->type_argv [i]);
-               if (klass->image == image) {
-                       /*dump_ginst (ginst);
-                         g_print (" removed\n");*/
-                       if (!g_slist_find (user_data->ginst_list, ginst))
-                               user_data->ginst_list = g_slist_append (user_data->ginst_list, ginst);
-                       return TRUE;
-               }
-       }
-       return FALSE;
+       if (!g_slist_find (user_data->ginst_list, ginst))
+               user_data->ginst_list = g_slist_append (user_data->ginst_list, ginst);
+
+       return TRUE;
 }
 
 static gboolean
 gclass_in_image (gpointer key, gpointer value, gpointer data)
 {
-       CleanForImageUserData *user_data = (CleanForImageUserData*)data;
-       MonoImage *image = user_data->image;
+       CleanForImageUserData *user_data = data;
        MonoGenericClass *gclass = key;
-       gboolean match = FALSE;
 
-       if (ginst_in_image (gclass->context.class_inst, NULL, data))
-               match = TRUE;
-       if (gclass->container_class->image == image)
-               match = TRUE;
-
-       if (match) {
-               if (!g_slist_find (user_data->gclass_list, gclass))
-                       user_data->gclass_list = g_slist_append (user_data->gclass_list, gclass);
-               return TRUE;
-       } else {
+       if (gclass->container_class->image != user_data->image &&
+           !ginst_in_image (gclass->context.class_inst, NULL, data))
                return FALSE;
-       }
+
+       if (!g_slist_find (user_data->gclass_list, gclass))
+               user_data->gclass_list = g_slist_append (user_data->gclass_list, gclass);
+
+       return TRUE;
 }
 
 static gboolean
@@ -2016,23 +2090,17 @@ inflated_method_in_image (gpointer key, gpointer value, gpointer data)
        return FALSE;
 }
 
-/*
- * LOCKING: assumes the loader lock is held.
- */
-MonoMethodInflated*
-mono_method_inflated_lookup (MonoMethodInflated* method, gboolean cache)
+static gboolean
+inflated_signature_in_image (gpointer key, gpointer value, gpointer data)
 {
-       if (cache) {
-               if (!generic_method_cache)
-                       generic_method_cache = g_hash_table_new_full (inflated_method_hash, inflated_method_equal, NULL, (GDestroyNotify)free_inflated_method);
-               g_hash_table_insert (generic_method_cache, method, method);
-               return method;
-       } else {
-               if (generic_method_cache)
-                       return g_hash_table_lookup (generic_method_cache, method);
-               return NULL;
-       }
-}
+       MonoInflatedMethodSignature *sig = key;
+
+       if (sig->context.class_inst && ginst_in_image (sig->context.class_inst, NULL, data))
+               return TRUE;
+       if (sig->context.method_inst && ginst_in_image (sig->context.method_inst, NULL, data))
+               return TRUE;
+       return FALSE;
+}      
 
 void
 mono_metadata_clean_for_image (MonoImage *image)
@@ -2051,6 +2119,8 @@ mono_metadata_clean_for_image (MonoImage *image)
        g_hash_table_foreach_steal (generic_class_cache, gclass_in_image, &user_data);
        if (generic_method_cache)
                g_hash_table_foreach_remove (generic_method_cache, inflated_method_in_image, &user_data);
+       if (generic_signature_cache)
+               g_hash_table_foreach_remove (generic_signature_cache, inflated_signature_in_image, &user_data);
        /* Delete the removed items */
        for (l = user_data.ginst_list; l; l = l->next)
                free_generic_inst (l->data);
@@ -2067,17 +2137,8 @@ free_inflated_method (MonoMethodInflated *imethod)
        int i;
        MonoMethod *method = (MonoMethod*)imethod;
 
-       if (method->signature) {
-               MonoMethodSignature *sig = method->signature;
-
-               /* Allocated in inflate_generic_signature () */
-               mono_metadata_free_type (sig->ret);
-               for (i = 0; i < sig->param_count; ++i)
-                       mono_metadata_free_type (sig->params [i]);
-
-               /* FIXME: The signature is allocated from a mempool */
-               //g_free (method->signature);
-       }
+       if (method->signature)
+               mono_metadata_free_inflated_signature (method->signature);
 
        if (!((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))) {
                MonoMethodNormal* mn = (MonoMethodNormal*) method;
@@ -2112,7 +2173,8 @@ free_generic_class (MonoGenericClass *gclass)
 {
        int i;
 
-       if (gclass->cached_class) {
+       /* FIXME: The dynamic case */
+       if (gclass->cached_class && !gclass->cached_class->image->dynamic) {
                MonoClass *class = gclass->cached_class;
 
                /* Allocated in mono_class_init () */
@@ -2132,6 +2194,63 @@ free_generic_class (MonoGenericClass *gclass)
        g_free (gclass);
 }
 
+static void
+free_inflated_signature (MonoInflatedMethodSignature *sig)
+{
+       mono_metadata_free_inflated_signature (sig->sig);
+       g_free (sig);
+}
+
+/*
+ * LOCKING: assumes the loader lock is held.
+ */
+MonoMethodInflated*
+mono_method_inflated_lookup (MonoMethodInflated* method, gboolean cache)
+{
+       if (cache) {
+               if (!generic_method_cache)
+                       generic_method_cache = g_hash_table_new_full (inflated_method_hash, inflated_method_equal, NULL, (GDestroyNotify)free_inflated_method);
+               g_hash_table_insert (generic_method_cache, method, method);
+               return method;
+       } else {
+               if (generic_method_cache)
+                       return g_hash_table_lookup (generic_method_cache, method);
+               return NULL;
+       }
+}
+
+/*
+ * mono_metadata_get_inflated_signature:
+ *
+ *   Given an inflated signature and a generic context, return a canonical copy of the 
+ * signature. The returned signature might be equal to SIG or it might be a cached copy.
+ */
+MonoMethodSignature *
+mono_metadata_get_inflated_signature (MonoMethodSignature *sig, MonoGenericContext *context)
+{
+       MonoInflatedMethodSignature helper;
+       MonoInflatedMethodSignature *res;
+
+       mono_loader_lock ();
+       if (!generic_signature_cache)
+               generic_signature_cache = g_hash_table_new_full (inflated_signature_hash, inflated_signature_equal, NULL, (GDestroyNotify)free_inflated_signature);
+
+       helper.sig = sig;
+       helper.context.class_inst = context->class_inst;
+       helper.context.method_inst = context->method_inst;
+       res = g_hash_table_lookup (generic_signature_cache, &helper);
+       if (!res) {
+               res = g_new0 (MonoInflatedMethodSignature, 1);
+               res->sig = sig;
+               res->context.class_inst = context->class_inst;
+               res->context.method_inst = context->method_inst;
+               g_hash_table_insert (generic_signature_cache, res, res);
+       }
+
+       mono_loader_unlock ();
+       return res->sig;
+}
+
 /*
  * mono_metadata_get_generic_inst:
  *
@@ -4300,7 +4419,6 @@ mono_type_create_from_typespec (MonoImage *image, guint32 type_spec)
        type2 = g_hash_table_lookup (image->typespec_cache, GUINT_TO_POINTER (type_spec));
 
        if (type2) {
-               g_free (type);
                mono_loader_unlock ();
                return type2;
        }