* locales.c
[mono.git] / mono / metadata / image.c
index 82476ee94b946886722c49d2cc5e4601d90a751b..0c8ca5f9043e6a8c51652c3a35ae2442b0d2576a 100644 (file)
@@ -22,6 +22,7 @@
 #include "private.h"
 #include "tabledefs.h"
 #include "tokentype.h"
+#include <mono/io-layer/io-layer.h>
 
 #define INVALID_ADDRESS 0xffffffff
 
@@ -31,6 +32,8 @@
 static GHashTable *loaded_images_hash;
 static GHashTable *loaded_images_guid_hash;
 
+static CRITICAL_SECTION images_mutex;
+
 guint32
 mono_cli_rva_image_map (MonoCLIImageInfo *iinfo, guint32 addr)
 {
@@ -66,6 +69,20 @@ mono_cli_rva_map (MonoCLIImageInfo *iinfo, guint32 addr)
        return NULL;
 }
 
+/**
+ * mono_images_init:
+ *
+ *  Initialize the global variables used by this module.
+ */
+void
+mono_images_init (void)
+{
+       InitializeCriticalSection (&images_mutex);
+
+       loaded_images_hash = g_hash_table_new (g_str_hash, g_str_equal);
+       loaded_images_guid_hash = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
 /**
  * mono_image_ensure_section_idx:
  * @image: The image we are operating on
@@ -261,6 +278,9 @@ load_metadata_ptrs (MonoImage *image, MonoCLIImageInfo *iinfo)
        char *ptr;
        
        offset = mono_cli_rva_image_map (iinfo, iinfo->cli_cli_header.ch_metadata.rva);
+       if (offset == INVALID_ADDRESS)
+               return FALSE;
+
        size = iinfo->cli_cli_header.ch_metadata.size;
 
        if (!image->f) {
@@ -321,7 +341,7 @@ load_metadata_ptrs (MonoImage *image, MonoCLIImageInfo *iinfo)
                        return FALSE;
                } else {
                        g_message ("Unknown heap type: %s\n", ptr + 8);
-                       ptr += 8 + strlen (ptr) + 1;
+                       ptr += 8 + strlen (ptr + 8) + 1;
                }
                pad = ptr - image->raw_metadata;
                if (pad % 4)
@@ -428,7 +448,6 @@ load_modules (MonoImage *image, MonoImageOpenStatus *status)
                module_ref = g_build_filename (base_dir, name, NULL);
                image->modules [i] = mono_image_open (module_ref, status);
                if (image->modules [i]) {
-                       image->modules [i]->assembly = image->assembly;
                        //g_print ("loaded module %s from %s (%p)\n", module_ref, image->name, image->assembly);
                }
                /* 
@@ -439,6 +458,7 @@ load_modules (MonoImage *image, MonoImageOpenStatus *status)
                        *status = MONO_IMAGE_OK;
                g_free (module_ref);
        }
+
        g_free (base_dir);
 }
 
@@ -449,7 +469,11 @@ load_class_names (MonoImage *image)
        guint32 cols [MONO_TYPEDEF_SIZE];
        const char *name;
        const char *nspace;
-       guint32 i, visib;
+       guint32 i, visib, nspace_index;
+       GHashTable *name_cache2, *nspace_table;
+
+       /* Temporary hash table to avoid lookups in the nspace_table */
+       name_cache2 = g_hash_table_new (NULL, NULL);
 
        for (i = 1; i <= t->rows; ++i) {
                mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE);
@@ -459,15 +483,65 @@ load_class_names (MonoImage *image)
                        continue;
                name = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]);
                nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]);
-               mono_image_add_to_name_cache (image, nspace, name, i);
+
+               nspace_index = cols [MONO_TYPEDEF_NAMESPACE];
+               nspace_table = g_hash_table_lookup (name_cache2, GUINT_TO_POINTER (nspace_index));
+               if (!nspace_table) {
+                       nspace_table = g_hash_table_new (g_str_hash, g_str_equal);
+                       g_hash_table_insert (image->name_cache, (char*)nspace, nspace_table);
+                       g_hash_table_insert (name_cache2, GUINT_TO_POINTER (nspace_index),
+                                                                nspace_table);
+               }
+               g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (i));
+       }
+
+       /* Load type names from EXPORTEDTYPES table */
+       {
+               MonoTableInfo  *t = &image->tables [MONO_TABLE_EXPORTEDTYPE];
+               guint32 cols [MONO_EXP_TYPE_SIZE];
+               int i;
+
+               for (i = 0; i < t->rows; ++i) {
+                       mono_metadata_decode_row (t, i, cols, MONO_EXP_TYPE_SIZE);
+                       name = mono_metadata_string_heap (image, cols [MONO_EXP_TYPE_NAME]);
+                       nspace = mono_metadata_string_heap (image, cols [MONO_EXP_TYPE_NAMESPACE]);
+
+                       nspace_index = cols [MONO_EXP_TYPE_NAMESPACE];
+                       nspace_table = g_hash_table_lookup (name_cache2, GUINT_TO_POINTER (nspace_index));
+                       if (!nspace_table) {
+                               nspace_table = g_hash_table_new (g_str_hash, g_str_equal);
+                               g_hash_table_insert (image->name_cache, (char*)nspace, nspace_table);
+                               g_hash_table_insert (name_cache2, GUINT_TO_POINTER (nspace_index),
+                                                                        nspace_table);
+                       }
+                       g_hash_table_insert (nspace_table, (char *) name, GUINT_TO_POINTER (mono_metadata_make_token (MONO_TABLE_EXPORTEDTYPE, i + 1)));
+               }
        }
+
+       g_hash_table_destroy (name_cache2);
+}
+
+static void
+register_guid (gpointer key, gpointer value, gpointer user_data)
+{
+       MonoImage *image = (MonoImage*)value;
+
+       if (!g_hash_table_lookup (loaded_images_guid_hash, image))
+               g_hash_table_insert (loaded_images_guid_hash, image->guid, image);
+}
+
+static void
+build_guid_table (void)
+{
+       g_hash_table_foreach (loaded_images_hash, register_guid, NULL);
 }
 
 void
 mono_image_init (MonoImage *image)
 {
-       image->method_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
-       image->class_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
+       image->method_cache = g_hash_table_new (NULL, NULL);
+       image->class_cache = g_hash_table_new (NULL, NULL);
+       image->field_cache = g_hash_table_new (NULL, NULL);
        image->name_cache = g_hash_table_new (g_str_hash, g_str_equal);
        image->array_cache = g_hash_table_new (NULL, NULL);
 
@@ -481,13 +555,17 @@ mono_image_init (MonoImage *image)
                g_hash_table_new ((GHashFunc)mono_signature_hash, 
                                  (GCompareFunc)mono_metadata_signature_equal);
 
-       image->runtime_invoke_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
-       image->managed_wrapper_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
-       image->native_wrapper_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
-       image->remoting_invoke_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
-       image->synchronized_cache = g_hash_table_new (g_direct_hash, g_direct_equal);
+       image->runtime_invoke_cache = g_hash_table_new (NULL, NULL);
+       image->managed_wrapper_cache = g_hash_table_new (NULL, NULL);
+       image->native_wrapper_cache = g_hash_table_new (NULL, NULL);
+       image->remoting_invoke_cache = g_hash_table_new (NULL, NULL);
+       image->synchronized_cache = g_hash_table_new (NULL, NULL);
 
+       image->typespec_cache = g_hash_table_new (NULL, NULL);
 
+       image->generic_inst_cache =
+               g_hash_table_new ((GHashFunc)mono_metadata_generic_inst_hash,
+                                 (GCompareFunc)mono_metadata_generic_inst_equal);
 }
 
 static MonoImage *
@@ -684,27 +762,40 @@ do_mono_image_open (const char *fname, MonoImageOpenStatus *status)
        image = g_new0 (MonoImage, 1);
        image->ref_count = 1;
        image->f = filed;
-       image->name = g_strdup (fname);
        iinfo = g_new0 (MonoCLIImageInfo, 1);
        image->image_info = iinfo;
 
+       if (g_path_is_absolute (fname))
+               image->name = g_strdup (fname);
+       else {
+               gchar *path = g_get_current_dir ();
+               image->name = g_build_filename (path, fname, NULL);
+               g_free (path);
+       }
+
        return do_mono_image_load (image, status);
 }
 
 MonoImage *
-mono_image_loaded (const char *name) {
-       if (strcmp (name, "mscorlib") == 0)
-               name = "corlib";
-       if (loaded_images_hash)
-               return g_hash_table_lookup (loaded_images_hash, name);
-       return NULL;
+mono_image_loaded (const char *name)
+{
+       MonoImage *res;
+        
+       EnterCriticalSection (&images_mutex);
+       res = g_hash_table_lookup (loaded_images_hash, name);
+       LeaveCriticalSection (&images_mutex);
+       return res;
 }
 
 MonoImage *
-mono_image_loaded_by_guid (const char *guid) {
-       if (loaded_images_guid_hash)
-               return g_hash_table_lookup (loaded_images_guid_hash, guid);
-       return NULL;
+mono_image_loaded_by_guid (const char *guid)
+{
+       MonoImage *res;
+
+       EnterCriticalSection (&images_mutex);
+       res = g_hash_table_lookup (loaded_images_guid_hash, guid);
+       LeaveCriticalSection (&images_mutex);
+       return res;
 }
 
 MonoImage *
@@ -753,41 +844,66 @@ mono_image_open_from_data (char *data, guint32 data_len, gboolean need_copy, Mon
 MonoImage *
 mono_image_open (const char *fname, MonoImageOpenStatus *status)
 {
-       MonoImage *image;
+       MonoImage *image, *image2;
        
        g_return_val_if_fail (fname != NULL, NULL);
 
-       if (loaded_images_hash){
-               image = g_hash_table_lookup (loaded_images_hash, fname);
-               if (image){
-                       image->ref_count++;
-                       return image;
-               }
+       /*
+        * The easiest solution would be to do all the loading inside the mutex,
+        * but that would lead to scalability problems. So we let the loading
+        * happen outside the mutex, and if multiple threads happen to load
+        * the same image, we discard all but the first copy.
+        */
+       EnterCriticalSection (&images_mutex);
+       image = g_hash_table_lookup (loaded_images_hash, fname);
+       if (image){
+               image->ref_count++;
+               LeaveCriticalSection (&images_mutex);
+               return image;
        }
+       LeaveCriticalSection (&images_mutex);
 
        image = do_mono_image_open (fname, status);
        if (image == NULL)
                return NULL;
 
-       if (!loaded_images_hash)
-               loaded_images_hash = g_hash_table_new (g_str_hash, g_str_equal);
+       EnterCriticalSection (&images_mutex);
+       image2 = g_hash_table_lookup (loaded_images_hash, fname);
+       if (image2) {
+               /* Somebody else beat us to it */
+               image2->ref_count ++;
+               LeaveCriticalSection (&images_mutex);
+               mono_image_close (image);
+
+               return image2;
+       }
        g_hash_table_insert (loaded_images_hash, image->name, image);
        if (image->assembly_name)
-               g_hash_table_insert (loaded_images_hash, (char *) image->assembly_name, image);
-       
-       if (!loaded_images_guid_hash)
-               loaded_images_guid_hash = g_hash_table_new (g_str_hash, g_str_equal);
+               g_hash_table_insert (loaded_images_hash, (char *) image->assembly_name, image); 
        g_hash_table_insert (loaded_images_guid_hash, image->guid, image);
+       LeaveCriticalSection (&images_mutex);
 
        return image;
 }
 
 static void
-free_hash_table(gpointer key, gpointer val, gpointer user_data)
+free_hash_table (gpointer key, gpointer val, gpointer user_data)
 {
        g_hash_table_destroy ((GHashTable*)val);
 }
 
+/**
+ * mono_image_close:
+ * @image: The image file we wish to add a reference to
+ *
+ *  Increases the reference count of an image.
+ */
+void
+mono_image_addref (MonoImage *image)
+{
+       InterlockedIncrement (&image->ref_count);
+}      
+
 /**
  * mono_image_close:
  * @image: The image file we wish to close
@@ -798,24 +914,38 @@ free_hash_table(gpointer key, gpointer val, gpointer user_data)
 void
 mono_image_close (MonoImage *image)
 {
+       MonoImage *image2;
+
        g_return_if_fail (image != NULL);
 
-       if (--image->ref_count)
+       EnterCriticalSection (&images_mutex);
+       if (--image->ref_count) {
+               LeaveCriticalSection (&images_mutex);
                return;
+       }
+       image2 = g_hash_table_lookup (loaded_images_hash, image->name);
+       if (image == image2) {
+               /* This is not true if we are called from mono_image_open () */
+               g_hash_table_remove (loaded_images_hash, image->name);
+               if (image->assembly_name)
+                       g_hash_table_remove (loaded_images_hash, (char *) image->assembly_name);        
+               g_hash_table_remove (loaded_images_guid_hash, image->guid);
+               /* Multiple images might have the same guid */
+               build_guid_table ();
+       }       
+       LeaveCriticalSection (&images_mutex);
 
-       if (!loaded_images_hash)
-               loaded_images_hash = g_hash_table_new (g_str_hash, g_str_equal);
-       g_hash_table_remove (loaded_images_hash, image->name);
-       
        if (image->f)
                fclose (image->f);
        if (image->raw_data_allocated)
                g_free (image->raw_data);
 
        g_free (image->name);
+       g_free (image->files);
 
        g_hash_table_destroy (image->method_cache);
        g_hash_table_destroy (image->class_cache);
+       g_hash_table_destroy (image->field_cache);
        g_hash_table_destroy (image->array_cache);
        g_hash_table_foreach (image->name_cache, free_hash_table, NULL);
        g_hash_table_destroy (image->name_cache);
@@ -826,6 +956,8 @@ mono_image_close (MonoImage *image)
        g_hash_table_destroy (image->delegate_invoke_cache);
        g_hash_table_destroy (image->remoting_invoke_cache);
        g_hash_table_destroy (image->runtime_invoke_cache);
+       g_hash_table_destroy (image->typespec_cache);
+       g_hash_table_destroy (image->generic_inst_cache);
        
        if (image->raw_metadata != NULL)
                mono_raw_buffer_free (image->raw_metadata);
@@ -845,7 +977,7 @@ mono_image_close (MonoImage *image)
                        g_free (ii->cli_sections);
                g_free (image->image_info);
        }
-       
+
        g_free (image);
 }
 
@@ -1023,6 +1155,46 @@ mono_image_get_resource (MonoImage *image, guint32 offset, guint32 *size)
        return data;
 }
 
+MonoImage*
+mono_image_load_file_for_image (MonoImage *image, int fileidx)
+{
+       char *base_dir, *name;
+       MonoImage *res;
+       MonoTableInfo  *t = &image->tables [MONO_TABLE_FILE];
+       const char *fname;
+       guint32 fname_id;
+
+       if (fileidx < 1 || fileidx > t->rows)
+               return NULL;
+
+       if (image->files && image->files [fileidx - 1])
+               return image->files [fileidx - 1];
+
+       if (!image->files)
+               image->files = g_new0 (MonoImage*, t->rows);
+
+       fname_id = mono_metadata_decode_row_col (t, fileidx - 1, MONO_FILE_NAME);
+       fname = mono_metadata_string_heap (image, fname_id);
+       base_dir = g_path_get_dirname (image->name);
+       name = g_build_filename (base_dir, fname, NULL);
+       res = mono_image_open (name, NULL);
+       if (res) {
+               int i;
+               t = &res->tables [MONO_TABLE_MODULEREF];
+               //g_print ("loaded file %s from %s (%p)\n", name, image->name, image->assembly);
+               res->assembly = image->assembly;
+               for (i = 0; i < t->rows; ++i) {
+                       if (res->modules [i] && !res->modules [i]->assembly)
+                               res->modules [i]->assembly = image->assembly;
+               }
+
+               image->files [fileidx - 1] = res;
+       }
+       g_free (name);
+       g_free (base_dir);
+       return res;
+}
+
 const char*
 mono_image_get_strong_name (MonoImage *image, guint32 *size)
 {