Fri Mar 29 16:09:54 CET 2002 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / assembly.c
index e3f013646ab870f818349bb3879ab015394a6eb9..3d9a59b2f3a4fa45464706c6a532e88d09db6684 100644 (file)
 
 #define CSIZE(x) (sizeof (x) / 4)
 
+/*
+ * keeps track of loaded assemblies
+ */
+static GHashTable *assemblies;
+
+/**
+ * g_concat_dir_and_file:
+ * @dir:  directory name
+ * @file: filename.
+ *
+ * returns a new allocated string that is the concatenation of dir and file,
+ * takes care of the exact details for concatenating them.
+ */
+static char *
+g_concat_dir_and_file (const char *dir, const char *file)
+{
+       g_return_val_if_fail (dir != NULL, NULL);
+       g_return_val_if_fail (file != NULL, NULL);
+
+        /*
+        * If the directory name doesn't have a / on the end, we need
+        * to add one so we get a proper path to the file
+        */
+       if (dir [strlen(dir) - 1] != G_DIR_SEPARATOR)
+               return g_strconcat (dir, G_DIR_SEPARATOR_S, file, NULL);
+       else
+               return g_strconcat (dir, file, NULL);
+}
+
 static char *
-default_assembly_name_resolver (const char *name)
+default_assembly_name_resolver (const char *base_dir, const char *name)
 {
-       if (strcmp (name, "mscorlib") == 0)
-               return g_strdup (MONO_ASSEMBLIES "/corlib.dll");
+       char *file, *path;
+       
+       if ((strcmp (name, "mscorlib") == 0) ||
+                       (strcmp (name, "mscorlib.dll") == 0) ||
+                       (strcmp (name, "corlib.dll") == 0) ||
+                       (strcmp (name, "corlib") == 0))
+               return g_concat_dir_and_file (MONO_ASSEMBLIES, CORLIB_NAME);
+
+       path = g_concat_dir_and_file (base_dir, name);
+       if (g_file_test (name, G_FILE_TEST_EXISTS))
+               return path;
+
+       file = path;
+       path = g_strconcat (file, ".dll", NULL);
+       g_free (file);
+       if (g_file_test (path, G_FILE_TEST_EXISTS))
+               return path;
+       g_free (path);
+       
+       path = g_concat_dir_and_file (MONO_ASSEMBLIES, name);
+       if (g_file_test (path, G_FILE_TEST_EXISTS))
+               return path;
+       g_free (path);
+
+       file = g_strconcat (name, ".dll", NULL);
+       path = g_concat_dir_and_file (MONO_ASSEMBLIES, file);
+       g_free (file);
 
-       return g_strconcat (MONO_ASSEMBLIES "/", name, NULL);
+       return path;
 }
 
 /**
@@ -48,63 +102,107 @@ mono_assembly_open (const char *filename, MonoAssemblyResolverFn resolver,
 {
        MonoAssembly *ass;
        MonoImage *image;
-       metadata_tableinfo_t *t;
-       cli_image_info_t *iinfo;
-       metadata_t *m;
+       MonoTableInfo *t;
        int i;
+       char *fullname, *base_dir;
+       const char *base_name = strrchr (filename, G_DIR_SEPARATOR);
+       static MonoAssembly *corlib;
        
        g_return_val_if_fail (filename != NULL, NULL);
 
-       image = mono_image_open (filename, status);
+       if (assemblies == NULL)
+               assemblies = g_hash_table_new (g_str_hash, g_str_equal);
+
+       if ((ass = g_hash_table_lookup (assemblies, filename)) != NULL){
+               ass->ref_count++;
+               return ass;
+       }
+       
+       if (base_name == NULL)
+               base_name = filename;
+       else
+               base_name++;
+
+       if (resolver == NULL)
+               resolver = default_assembly_name_resolver;
+
+       base_dir = g_path_get_dirname (fullname);
+       
+       fullname = resolver (base_dir, filename);
+       image = mono_image_open (fullname, status);
+
        if (!image){
                if (status)
                        *status = MONO_IMAGE_ERROR_ERRNO;
+               g_free (fullname);
+               g_free (base_dir);
                return NULL;
        }
 
-       if (resolver == NULL)
-               resolver = default_assembly_name_resolver;
+       t = &image->tables [MONO_TABLE_ASSEMBLYREF];
 
-       iinfo = image->image_info;
-       m = &iinfo->cli_metadata;
-       t = &m->tables [META_TABLE_ASSEMBLYREF];
+       image->references = g_new0 (MonoAssembly *, t->rows + 1);
 
-       image->references = g_new (MonoImage *, t->rows + 1);
+       /*
+        * Create assembly struct, and enter it into the assembly cache
+        */
+       ass = g_new (MonoAssembly, 1);
+       ass->name = fullname;
+       ass->image = image;
 
+       g_hash_table_insert (assemblies, image->name, ass);
+       g_hash_table_insert (assemblies, ass->name, ass);
+       
        /*
         * Load any assemblies this image references
         */
        for (i = 0; i < t->rows; i++){
                char *assembly_ref;
                const char *name;
-               guint32 cols [9];
+               guint32 cols [MONO_ASSEMBLYREF_SIZE];
 
                mono_metadata_decode_row (t, i, cols, CSIZE (cols));
-               name = mono_metadata_string_heap (m, cols [6]);
+               name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
+
+               /*
+                * Special case until we have a passable corlib:
+                *
+                * ie, references to mscorlib from corlib.dll are ignored 
+                * and we do not load corlib twice.
+                */
+               if (strcmp (base_name, CORLIB_NAME) == 0){
+                       if (corlib == NULL)
+                               corlib = ass;
+                       
+                       if (strcmp (name, "mscorlib") == 0)
+                               continue;
+               }
+               
+               assembly_ref = (*resolver) (base_dir, name);
 
-               assembly_ref = (*resolver) (name);
                image->references [i] = mono_assembly_open (assembly_ref, resolver, status);
-               g_free (assembly_ref);
-               
+
                if (image->references [i] == NULL){
                        int j;
                        
                        for (j = 0; j < i; j++)
-                               mono_image_close (image->references [j]);
+                               mono_assembly_close (image->references [j]);
                        g_free (image->references);
                        mono_image_close (image);
 
                        g_warning ("Could not find assembly %s %s", name, assembly_ref);
+                       g_free (assembly_ref);
                        if (status)
                                *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
+                       g_free (ass);
+                       g_free (base_dir);
                        return NULL;
                }
+               g_free (assembly_ref);
        }
        image->references [i] = NULL;
 
-       ass = g_new (MonoAssembly, 1);
-       ass->image = image;
-       
+       g_free (base_dir);
        return ass;
 }
 
@@ -116,11 +214,27 @@ mono_assembly_close (MonoAssembly *assembly)
        
        g_return_if_fail (assembly != NULL);
 
+       if (--assembly->ref_count != 0)
+               return;
+       
+       image = assembly->image;
        for (i = 0; image->references [i] != NULL; i++)
-               mono_image_close (assembly->image);
+               mono_image_close (image->references [i]->image);
        g_free (image->references);
             
        mono_image_close (assembly->image);
+
+       g_hash_table_remove (assemblies, assembly->name);
+                            
+       g_free (assembly->name);
        g_free (assembly);
 }
 
+/*
+ * Temporary hack until we get AppDomains
+ */
+GHashTable *
+mono_get_assemblies ()
+{
+       return assemblies;
+}