Merge pull request #5303 from lambdageek/cattr-bcheck
[mono.git] / mono / metadata / metadata.c
index 46550b7db1c9ab7e883df4b168ea80c67bfee092..b41dbf4e8186eff36f490f2a4d4844b032c73295 100644 (file)
 #include <mono/utils/mono-error-internals.h>
 #include <mono/utils/bsearch.h>
 #include <mono/utils/atomic.h>
+#include <mono/utils/mono-counters.h>
+
+static int img_set_cache_hit, img_set_cache_miss, img_set_count;
+
 
 /* Auxiliary structure used for caching inflated signatures */
 typedef struct {
@@ -515,6 +519,13 @@ mono_tables_names [] = {
 
 #endif
 
+/* If TRUE (but also see DISABLE_STICT_STRONG_NAMES #define), Mono will check
+ * that the public key token, culture and version of a candidate assembly matches
+ * the requested strong name.  If FALSE, as long as the name matches, the candidate
+ * will be allowed.
+ */
+static gboolean check_strong_names_strictly = FALSE;
+
 // Amount initially reserved in each imageset's mempool.
 // FIXME: This number is arbitrary, a more practical number should be found
 #define INITIAL_IMAGE_SET_SIZE    1024
@@ -825,21 +836,11 @@ mono_metadata_compute_size (MonoImage *meta, int tableindex, guint32 *result_bit
                        break;
 
                        /*
-                        * CustomAttributeType: TypeDef, TypeRef, MethodDef, 
-                        * MemberRef and String.  
+                        * CustomAttributeType: MethodDef, MemberRef.
                         */
                case MONO_MT_CAT_IDX:
-                       /* String is a heap, if it is wide, we know the size */
-                       /* See above, nope. 
-                       if (meta->idx_string_wide){
-                               field_size = 4;
-                               break;
-                       }*/
-                       
-                       n = MAX (get_nrows (meta, MONO_TABLE_TYPEREF),
-                                get_nrows (meta, MONO_TABLE_TYPEDEF));
-                       n = MAX (n, get_nrows (meta, MONO_TABLE_METHOD));
-                       n = MAX (n, get_nrows (meta, MONO_TABLE_MEMBERREF));
+                       n = MAX (get_nrows (meta, MONO_TABLE_METHOD),
+                                        get_nrows (meta, MONO_TABLE_MEMBERREF));
 
                        /* 3 bits to encode */
                        field_size = rtsize (meta, n, 16-3);
@@ -1179,7 +1180,7 @@ mono_metadata_decode_row_col (const MonoTableInfo *t, int idx, guint col)
  * \param ptr pointer to a blob object
  * \param rptr the new position of the pointer
  *
- * This decodes a compressed size as described by 23.1.4 (a blob or user string object)
+ * This decodes a compressed size as described by 24.2.4 (#US and #Blob a blob or user string object)
  *
  * \returns the size of the blob object
  */
@@ -1521,7 +1522,7 @@ builtin_types[] = {
 #define NBUILTIN_TYPES() (sizeof (builtin_types) / sizeof (builtin_types [0]))
 
 static GHashTable *type_cache = NULL;
-static int next_generic_inst_id = 0;
+static gint32 next_generic_inst_id = 0;
 
 /* Protected by image_sets_mutex */
 static MonoImageSet *mscorlib_image_set;
@@ -1646,6 +1647,10 @@ mono_metadata_init (void)
                g_hash_table_insert (type_cache, (gpointer) &builtin_types [i], (gpointer) &builtin_types [i]);
 
        mono_os_mutex_init_recursive (&image_sets_mutex);
+
+       mono_counters_register ("ImgSet Cache Hit", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_hit);
+       mono_counters_register ("ImgSet Cache Miss", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_miss);
+       mono_counters_register ("ImgSet Count", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_count);
 }
 
 /**
@@ -2397,6 +2402,95 @@ image_sets_unlock (void)
        mono_os_mutex_unlock (&image_sets_mutex);
 }
 
+static int
+compare_pointers (const void *a, const void *b)
+{
+       return (size_t)a - (size_t)b;
+}
+
+//1103, 1327, 1597
+#define HASH_TABLE_SIZE 1103
+static MonoImageSet *img_set_cache [HASH_TABLE_SIZE];
+
+static guint32
+mix_hash (uintptr_t source)
+{
+       unsigned int hash = source;
+
+       // Actual hash
+       hash = (((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
+
+       // Mix in highest bits on 64-bit systems only
+       if (sizeof (source) > 4)
+               hash = hash ^ (source >> 32);
+
+       return hash;
+}
+
+static guint32
+hash_images (MonoImage **images, int nimages)
+{
+       guint32 res = 0;
+       int i;
+       for (i = 0; i < nimages; ++i)
+               res += mix_hash ((size_t)images [i]);
+
+       return res;
+}
+
+static gboolean
+compare_img_set (MonoImageSet *set, MonoImage **images, int nimages)
+{
+       int j, k;
+
+       if (set->nimages != nimages)
+               return FALSE;
+
+       for (j = 0; j < nimages; ++j) {
+               for (k = 0; k < nimages; ++k)
+                       if (set->images [k] == images [j])
+                               break; // Break on match
+
+               // If we iterated all the way through set->images, images[j] was *not* found.
+               if (k == nimages)
+                       break; // Break on "image not found"
+       }
+
+       // If we iterated all the way through images without breaking, all items in images were found in set->images
+       return j == nimages;
+}
+
+
+static MonoImageSet*
+img_set_cache_get (MonoImage **images, int nimages)
+{
+       guint32 hash_code = hash_images (images, nimages);
+       int index = hash_code % HASH_TABLE_SIZE;
+       MonoImageSet *img = img_set_cache [index];
+       if (!img || !compare_img_set (img, images, nimages)) {
+               ++img_set_cache_miss;
+               return NULL;
+       }
+       ++img_set_cache_hit;
+       return img;
+}
+
+static void
+img_set_cache_add (MonoImageSet *set)
+{
+       guint32 hash_code = hash_images (set->images, set->nimages);
+       int index = hash_code % HASH_TABLE_SIZE;
+       img_set_cache [index] = set;    
+}
+
+static void
+img_set_cache_remove (MonoImageSet *is)
+{
+       guint32 hash_code = hash_images (is->images, is->nimages);
+       int index = hash_code % HASH_TABLE_SIZE;
+       if (img_set_cache [index] == is)
+               img_set_cache [index] = NULL;
+}
 /*
  * get_image_set:
  *
@@ -2418,6 +2512,10 @@ get_image_set (MonoImage **images, int nimages)
        if (nimages == 0)
                return mscorlib_image_set;
 
+       set = img_set_cache_get (images, nimages);
+       if (set)
+               return set;
+
        image_sets_lock ();
 
        if (!image_sets)
@@ -2448,8 +2546,11 @@ get_image_set (MonoImage **images, int nimages)
                        }
 
                        // If we iterated all the way through images without breaking, all items in images were found in set->images
-                       if (j == nimages)
-                               break; // Break on "found a set with equal members"
+                       if (j == nimages) {
+                               // Break on "found a set with equal members".
+                               // This happens in case of a hash collision with a previously cached set.
+                               break;
+                       }
                }
 
                l = l->next;
@@ -2463,7 +2564,7 @@ get_image_set (MonoImage **images, int nimages)
                mono_os_mutex_init_recursive (&set->lock);
                for (i = 0; i < nimages; ++i)
                        set->images [i] = images [i];
-               set->gclass_cache = g_hash_table_new_full (mono_generic_class_hash, mono_generic_class_equal, NULL, (GDestroyNotify)free_generic_class);
+               set->gclass_cache = mono_conc_hashtable_new_full (mono_generic_class_hash, mono_generic_class_equal, NULL, (GDestroyNotify)free_generic_class);
                set->ginst_cache = g_hash_table_new_full (mono_metadata_generic_inst_hash, mono_metadata_generic_inst_equal, NULL, (GDestroyNotify)free_generic_inst);
                set->gmethod_cache = g_hash_table_new_full (inflated_method_hash, inflated_method_equal, NULL, (GDestroyNotify)free_inflated_method);
                set->gsignature_cache = g_hash_table_new_full (inflated_signature_hash, inflated_signature_equal, NULL, (GDestroyNotify)free_inflated_signature);
@@ -2472,8 +2573,12 @@ get_image_set (MonoImage **images, int nimages)
                        set->images [i]->image_sets = g_slist_prepend (set->images [i]->image_sets, set);
 
                g_ptr_array_add (image_sets, set);
+               ++img_set_count;
        }
 
+       /* Cache the set. If there was a cache collision, the previously cached value will be replaced. */
+       img_set_cache_add (set);
+
        if (nimages == 1 && images [0] == mono_defaults.corlib) {
                mono_memory_barrier ();
                mscorlib_image_set = set;
@@ -2489,7 +2594,7 @@ delete_image_set (MonoImageSet *set)
 {
        int i;
 
-       g_hash_table_destroy (set->gclass_cache);
+       mono_conc_hashtable_destroy (set->gclass_cache);
        g_hash_table_destroy (set->ginst_cache);
        g_hash_table_destroy (set->gmethod_cache);
        g_hash_table_destroy (set->gsignature_cache);
@@ -2505,6 +2610,8 @@ delete_image_set (MonoImageSet *set)
 
        image_sets_unlock ();
 
+       img_set_cache_remove (set);
+
        if (set->mempool)
                mono_mempool_destroy (set->mempool);
        g_free (set->images);
@@ -2848,7 +2955,7 @@ mono_metadata_clean_for_image (MonoImage *image)
                MonoImageSet *set = (MonoImageSet *)l->data;
 
                mono_image_set_lock (set);
-               g_hash_table_foreach_steal (set->gclass_cache, steal_gclass_in_image, &gclass_data);
+               mono_conc_hashtable_foreach_steal (set->gclass_cache, steal_gclass_in_image, &gclass_data);
                g_hash_table_foreach_steal (set->ginst_cache, steal_ginst_in_image, &ginst_data);
                g_hash_table_foreach_remove (set->gmethod_cache, inflated_method_in_image, image);
                g_hash_table_foreach_remove (set->gsignature_cache, inflated_signature_in_image, image);
@@ -2967,6 +3074,18 @@ mono_metadata_get_image_set_for_method (MonoMethodInflated *method)
        return set;
 }
 
+static gboolean
+type_is_gtd (MonoType *type)
+{
+       switch (type->type) {
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_VALUETYPE:
+               return mono_class_is_gtd (type->data.klass);
+       default:
+               return FALSE;
+       }
+}
+
 /*
  * mono_metadata_get_generic_inst:
  *
@@ -2994,6 +3113,13 @@ mono_metadata_get_generic_inst (int type_argc, MonoType **type_argv)
        ginst->type_argc = type_argc;
        memcpy (ginst->type_argv, type_argv, type_argc * sizeof (MonoType *));
 
+       for (i = 0; i < type_argc; ++i) {
+               MonoType *t = ginst->type_argv [i];
+               if (type_is_gtd (t)) {
+                       ginst->type_argv [i] = mono_class_gtd_get_canonical_inst (t->data.klass);
+               }
+       }
+
        return mono_metadata_get_canonical_generic_inst (ginst);
 }
 
@@ -3034,7 +3160,7 @@ mono_metadata_get_canonical_generic_inst (MonoGenericInst *candidate)
                int size = MONO_SIZEOF_GENERIC_INST + type_argc * sizeof (MonoType *);
                ginst = (MonoGenericInst *)mono_image_set_alloc0 (set, size);
 #ifndef MONO_SMALL_CONFIG
-               ginst->id = ++next_generic_inst_id;
+               ginst->id = InterlockedIncrement (&next_generic_inst_id);
 #endif
                ginst->is_open = is_open;
                ginst->type_argc = type_argc;
@@ -3090,17 +3216,13 @@ mono_metadata_lookup_generic_class (MonoClass *container_class, MonoGenericInst
 
        collect_data_free (&data);
 
-       mono_image_set_lock (set);
-
-       gclass = (MonoGenericClass *)g_hash_table_lookup (set->gclass_cache, &helper);
+       gclass = (MonoGenericClass *)mono_conc_hashtable_lookup (set->gclass_cache, &helper);
 
        /* A tripwire just to keep us honest */
        g_assert (!helper.cached_class);
 
-       if (gclass) {
-               mono_image_set_unlock (set);
+       if (gclass)
                return gclass;
-       }
 
        gclass = mono_image_set_new0 (set, MonoGenericClass, 1);
        if (is_dynamic)
@@ -3114,11 +3236,17 @@ mono_metadata_lookup_generic_class (MonoClass *container_class, MonoGenericInst
        if (inst == mono_class_get_generic_container (container_class)->context.class_inst && !is_tb_open)
                gclass->cached_class = container_class;
 
-       g_hash_table_insert (set->gclass_cache, gclass, gclass);
+       mono_image_set_lock (set);
+
+       MonoGenericClass *gclass2 = mono_conc_hashtable_insert (set->gclass_cache, gclass, gclass);
+       if (!gclass2)
+               gclass2 = gclass;
+
+       // g_hash_table_insert (set->gclass_cache, gclass, gclass);
 
        mono_image_set_unlock (set);
 
-       return gclass;
+       return gclass2;
 }
 
 /*
@@ -5688,9 +5816,7 @@ mono_type_create_from_typespec_checked (MonoImage *image, guint32 type_spec, Mon
 
        error_init (error);
 
-       mono_image_lock (image);
-       type = (MonoType *)g_hash_table_lookup (image->typespec_cache, GUINT_TO_POINTER (type_spec));
-       mono_image_unlock (image);
+       type = (MonoType *)mono_conc_hashtable_lookup (image->typespec_cache, GUINT_TO_POINTER (type_spec));
        if (type)
                return type;
 
@@ -5714,12 +5840,12 @@ mono_type_create_from_typespec_checked (MonoImage *image, guint32 type_spec, Mon
        mono_metadata_free_type (type);
 
        mono_image_lock (image);
-       type = (MonoType *)g_hash_table_lookup (image->typespec_cache, GUINT_TO_POINTER (type_spec));
+
        /* We might leak some data in the image mempool if found */
-       if (!type) {
-               g_hash_table_insert (image->typespec_cache, GUINT_TO_POINTER (type_spec), type2);
+       type = mono_conc_hashtable_insert (image->typespec_cache, GUINT_TO_POINTER (type_spec), type2);
+       if (!type)
                type = type2;
-       }
+
        mono_image_unlock (image);
 
        return type;
@@ -6794,3 +6920,15 @@ mono_find_image_set_owner (void *ptr)
 
        return owner;
 }
+
+void
+mono_loader_set_strict_strong_names (gboolean enabled)
+{
+       check_strong_names_strictly = enabled;
+}
+
+gboolean
+mono_loader_get_strict_strong_names (void)
+{
+       return check_strong_names_strictly;
+}