+ /* FIXME: The signature is allocated from a mempool */
+ //g_free (sig);
+}
+
+static gboolean
+inflated_method_equal (gconstpointer a, gconstpointer b)
+{
+ const MonoMethodInflated *ma = a;
+ const MonoMethodInflated *mb = b;
+ if (ma->declaring != mb->declaring)
+ return FALSE;
+ return mono_metadata_generic_context_equal (&ma->context, &mb->context);
+}
+
+static guint
+inflated_method_hash (gconstpointer a)
+{
+ const MonoMethodInflated *ma = 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)
+{
+ int i;
+ char *name;
+
+ g_print ("Ginst: <");
+ for (i = 0; i < ginst->type_argc; ++i) {
+ if (i != 0)
+ g_print (", ");
+ name = mono_type_get_name (ginst->type_argv [i]);
+ g_print ("%s", name);
+ g_free (name);
+ }
+ g_print (">");
+}*/
+
+static gboolean gclass_in_image (gpointer key, gpointer value, gpointer data);
+
+typedef struct {
+ MonoImage *image;
+ GSList *ginst_list;
+ 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 = data;
+ MonoGenericInst *ginst = key;
+ int i;
+
+ for (i = 0; i < ginst->type_argc; ++i) {
+ if (type_in_image (ginst->type_argv [i], user_data))
+ break;
+ }
+
+ if (i == ginst->type_argc)
+ 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 = data;
+ MonoGenericClass *gclass = key;
+
+ 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
+inflated_method_in_image (gpointer key, gpointer value, gpointer data)
+{
+ CleanForImageUserData *user_data = (CleanForImageUserData*)data;
+ MonoImage *image = user_data->image;
+ MonoMethodInflated *method = key;
+
+ if (method->declaring->klass->image == image)
+ return TRUE;
+ if (method->context.class_inst && ginst_in_image (method->context.class_inst, NULL, data))
+ return TRUE;
+ if (method->context.method_inst && ginst_in_image (method->context.method_inst, NULL, data))
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean
+inflated_signature_in_image (gpointer key, gpointer value, gpointer data)
+{
+ 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)
+{
+ CleanForImageUserData user_data;
+ GSList *l;
+
+ /* The data structures could reference each other so we delete them in two phases */
+ user_data.image = image;
+ user_data.ginst_list = NULL;
+ user_data.gclass_list = NULL;
+
+ mono_loader_lock ();
+ /* Collect the items to delete and remove them from the hash table */
+ g_hash_table_foreach_steal (generic_inst_cache, ginst_in_image, &user_data);
+ 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);
+ for (l = user_data.gclass_list; l; l = l->next)
+ free_generic_class (l->data);
+ g_slist_free (user_data.ginst_list);
+ g_slist_free (user_data.gclass_list);
+ mono_loader_unlock ();
+}
+
+static void
+free_inflated_method (MonoMethodInflated *imethod)
+{
+ int i;
+ MonoMethod *method = (MonoMethod*)imethod;
+
+ 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;
+ MonoMethodHeader *header = mn->header;
+
+ if (header) {
+ /* Allocated in inflate_generic_header () */
+ for (i = 0; i < header->num_locals; ++i)
+ mono_metadata_free_type (header->locals [i]);
+ g_free (header->clauses);
+ g_free (header);
+ }
+ }
+
+ g_free (method);
+}
+
+static void
+free_generic_inst (MonoGenericInst *ginst)
+{
+ int i;
+
+ for (i = 0; i < ginst->type_argc; ++i)
+ mono_metadata_free_type (ginst->type_argv [i]);
+ g_free (ginst->type_argv);
+ g_free (ginst);
+}
+
+
+static void
+free_generic_class (MonoGenericClass *gclass)
+{
+ int i;
+
+ /* FIXME: The dynamic case */
+ if (gclass->cached_class && !gclass->cached_class->image->dynamic) {
+ MonoClass *class = gclass->cached_class;
+
+ /* Allocated in mono_class_init () */
+ g_free (class->methods);
+ g_free (class->properties);
+ /* Allocated in mono_class_setup_fields () */
+ if (class->fields) {
+ for (i = 0; i < class->field.count; ++i) {
+ g_free (class->fields [i].generic_info);
+ mono_metadata_free_type (class->fields [i].type);
+ }
+ }
+ /* Allocated in mono_generic_class_get_class () */
+ g_free (class->interfaces);
+ g_free (class);
+ }
+ 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;
+ }