2007-07-22 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / appdomain.c
index e55f78d7d0bb7180239f0ab413586e93a79fde5f..020a47cc97be23da621f02b5ea9e8539d36e3558 100644 (file)
@@ -8,10 +8,16 @@
  *
  * (c) 2001-2003 Ximian, Inc. (http://www.ximian.com)
  */
-
+#undef ASSEMBLY_LOAD_DEBUG
 #include <config.h>
 #include <glib.h>
 #include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include <mono/os/gc_wrapper.h>
 
 #include <mono/metadata/marshal.h>
 #include <mono/metadata/monitor.h>
 #include <mono/metadata/threadpool.h>
+#include <mono/metadata/mono-debug.h>
 #include <mono/utils/mono-uri.h>
+#include <mono/utils/mono-logger.h>
+#include <mono/utils/mono-path.h>
+#include <mono/utils/mono-stdlib.h>
+#ifdef PLATFORM_WIN32
+#include <direct.h>
+#endif
 
-#define MONO_CORLIB_VERSION 46
+#define MONO_CORLIB_VERSION 58
 
 CRITICAL_SECTION mono_delegate_section;
 
@@ -200,7 +213,15 @@ mono_runtime_cleanup (MonoDomain *domain)
        /* This ends up calling any pending pending (for at most 2 seconds) */
        mono_gc_cleanup ();
 
+       mono_thread_cleanup ();
+
        mono_network_cleanup ();
+
+       mono_marshal_cleanup ();
+
+       mono_type_initialization_cleanup ();
+
+       mono_monitor_cleanup ();
 }
 
 static MonoDomainFunc quit_function = NULL;
@@ -349,13 +370,15 @@ mono_domain_set (MonoDomain *domain, gboolean force)
 MonoObject *
 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
 {
-       MonoDomain *add = ad->data;
+       MonoDomain *add;
        MonoObject *o;
        char *str;
 
        MONO_ARCH_SAVE_REGS;
 
        g_assert (ad != NULL);
+       add = ad->data;
+       g_assert (add != NULL);
 
        if (name == NULL)
                mono_raise_exception (mono_get_exception_argument_null ("name"));
@@ -397,11 +420,13 @@ ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
 void
 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
 {
-       MonoDomain *add = ad->data;
+       MonoDomain *add;
 
        MONO_ARCH_SAVE_REGS;
 
        g_assert (ad != NULL);
+       add = ad->data;
+       g_assert (add != NULL);
 
        if (name == NULL)
                mono_raise_exception (mono_get_exception_argument_null ("name"));
@@ -458,11 +483,9 @@ ves_icall_System_AppDomain_getRootDomain ()
 MonoAppDomain *
 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
 {
-       MonoDomain *domain = mono_domain_get ();
        MonoClass *adclass;
        MonoAppDomain *ad;
        MonoDomain *data;
-       GSList *tmp;
        
        MONO_ARCH_SAVE_REGS;
 
@@ -481,17 +504,13 @@ ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomai
        if (!setup->application_base) {
                /* Inherit from the root domain since MS.NET does this */
                MonoDomain *root = mono_get_root_domain ();
-               setup->application_base = mono_string_new_utf16 (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base));
+               if (root->setup->application_base)
+                       MONO_OBJECT_SETREF (setup, application_base, mono_string_new_utf16 (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base)));
        }
 
        mono_context_init (data);
 
-       /* The new appdomain should have all assemblies loaded */
-       mono_domain_assemblies_lock (domain);
-       /*g_print ("copy assemblies from domain %p (%s)\n", domain, domain->friendly_name);*/
-       for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next)
-               add_assemblies_to_domain (data, tmp->data, NULL);
-       mono_domain_assemblies_unlock (domain);
+       add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
 
        return ad;
 }
@@ -530,7 +549,7 @@ ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly
                        continue;
                if (ass->corlib_internal)
                        continue;
-               mono_array_set (res, gpointer, i, mono_assembly_get_object (domain, ass));
+               mono_array_setref (res, i, mono_assembly_get_object (domain, ass));
                ++i;
        }
        mono_domain_assemblies_unlock (domain);
@@ -611,13 +630,15 @@ add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
                mono_assembly_addref (ass);
                g_hash_table_insert (ht, ass, ass);
                domain->domain_assemblies = g_slist_prepend (domain->domain_assemblies, ass);
+               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s %p added to domain %s, ref_count=%d\n", ass->aname.name, ass, domain->friendly_name, ass->ref_count);
        }
 
        if (ass->image->references) {
                for (i = 0; ass->image->references [i] != NULL; i++) {
-                       if (!g_hash_table_lookup (ht, ass->image->references [i])) {
-                               add_assemblies_to_domain (domain, ass->image->references [i], ht);
-                       }
+                       if (ass->image->references [i] != REFERENCE_MISSING)
+                               if (!g_hash_table_lookup (ht, ass->image->references [i])) {
+                                       add_assemblies_to_domain (domain, ass->image->references [i], ht);
+                               }
                }
        }
        if (destroy_ht)
@@ -638,7 +659,9 @@ mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
        if (!domain->domain)
                /* This can happen during startup */
                return;
-
+#ifdef ASSEMBLY_LOAD_DEBUG
+       fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
+#endif
        klass = domain->domain->mbr.obj.vtable->klass;
 
        mono_domain_assemblies_lock (domain);
@@ -668,56 +691,6 @@ mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
        mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
 }
 
-static gchar *
-reduce_path (const gchar *dirname)
-{
-       gchar **parts;
-       gchar *part;
-       GList *list, *tmp;
-       GString *result;
-       gchar *res;
-       gint i;
-
-       parts = g_strsplit (dirname, G_DIR_SEPARATOR_S, 0);
-       list = NULL;
-       for (i = 0; (part = parts [i]) != NULL; i++) {
-               if (!strcmp (part, "."))
-                       continue;
-
-               if (!strcmp (part, "..")) {
-                       if (list && list->next) /* Don't remove root */
-                               list = g_list_delete_link (list, list);
-               } else {
-                       list = g_list_prepend (list, part);
-               }
-       }
-
-       result = g_string_new ("");
-       list = g_list_reverse (list);
-
-       for (tmp = list; tmp; tmp = tmp->next) {
-               gchar *data = (gchar *) tmp->data;
-
-               if (data && *data) {
-#ifdef PLATFORM_WIN32
-                       if (result->len == 0)
-                               g_string_append_printf (result, "%s\\", data);
-                       else if (result->str [result->len - 1] == '\\')
-                               g_string_append_printf (result, "%s", data);
-                       else
-#endif
-                               g_string_append_printf (result, "%c%s",
-                                                       G_DIR_SEPARATOR, data);
-               }
-       }
-       
-       res = result->str;
-       g_string_free (result, FALSE);
-       g_list_free (list);
-       g_strfreev (parts);
-       return res;
-}
-
 static void
 set_domain_search_path (MonoDomain *domain)
 {
@@ -805,7 +778,7 @@ set_domain_search_path (MonoDomain *domain)
                        gchar *reduced;
                        gchar *freeme;
 
-                       reduced = reduce_path (tmp [i]);
+                       reduced = mono_path_canonicalize (tmp [i]);
                        if (appbaselen == -1)
                                appbaselen = strlen (tmp [0]);
 
@@ -832,16 +805,222 @@ set_domain_search_path (MonoDomain *domain)
        g_strfreev (pvt_split);
 }
 
+static gboolean
+shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
+{
+       guint16 *orig, *dest;
+       gboolean copy_result;
+       
+       strcpy (src + srclen - tail_len, extension);
+       if (!g_file_test (src, G_FILE_TEST_IS_REGULAR))
+               return TRUE;
+       orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
+
+       strcpy (target + targetlen - tail_len, extension);
+       dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
+       
+       copy_result = CopyFile (orig, dest, FALSE);
+       g_free (orig);
+       g_free (dest);
+       
+       return copy_result;
+}
+
+static gint32 
+get_cstring_hash (const char *str)
+{
+       int len, i;
+       const char *p;
+       gint32 h = 0;
+       
+       if (!str || !str [0])
+               return 0;
+               
+       len = strlen (str);
+       p = str;
+       for (i = 0; i < len; i++) {
+               h = (h << 5) - h + *p;
+               p++;
+       }
+       
+       return h;
+}
+
+static char *
+get_shadow_assembly_location (const char *filename)
+{
+       gint32 hash = 0, hash2 = 0;
+       guint32 ticks = GetTickCount ();
+       char name_hash [9];
+       char path_hash [30];
+       char *bname = g_path_get_basename (filename);
+       MonoDomain *domain = mono_domain_get ();
+       
+       hash = get_cstring_hash (bname);
+       hash2 = get_cstring_hash (g_path_get_dirname (filename));
+       g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
+       g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, ticks);
+       return g_build_filename (mono_string_to_utf8 (domain->setup->dynamic_base), 
+                                "assembly", 
+                                "shadow", 
+                                name_hash,
+                                path_hash,
+                                bname, 
+                                NULL);
+}
+
+static gboolean
+ensure_directory_exists (const char *filename)
+{
+#ifdef PLATFORM_WIN32
+       gchar *dir_utf8 = g_path_get_dirname (filename);
+       gunichar2 *p;
+       gunichar2 *dir_utf16 = NULL;
+       int retval;
+       
+       if (!dir_utf8 || !dir_utf8 [0])
+               return FALSE;
+
+       dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
+       g_free (dir_utf8);
+
+       if (!dir_utf16)
+               return FALSE;
+
+       p = dir_utf16;
+
+       /* make life easy and only use one directory seperator */
+       while (*p != '\0')
+       {
+               if (*p == '/')
+                       *p = '\\';
+               p++;
+       }
+
+       p = dir_utf16;
+
+       /* get past C:\ )*/
+       while (*p++ != '\\')    
+       {
+       }
+
+       while (1) {
+               BOOL bRet = FALSE;
+               p = wcschr (p, '\\');
+               if (p)
+                       *p = '\0';
+               retval = _wmkdir (dir_utf16);
+               if (retval != 0 && errno != EEXIST) {
+                       g_free (dir_utf16);
+                       return FALSE;
+               }
+               if (!p)
+                       break;
+               *p++ = '\\';
+       }
+       
+       g_free (dir_utf16);
+       return TRUE;
+#else
+       char *p;
+       gchar *dir = g_path_get_dirname (filename);
+       int retval;
+       
+       if (!dir || !dir [0])
+               return FALSE;
+
+       p = dir;
+       while (*p == '/')
+               p++;
+
+       while (1) {
+               p = strchr (p, '/');
+               if (p)
+                       *p = '\0';
+               retval = mkdir (dir, 0777);
+               if (retval != 0 && errno != EEXIST) {
+                       g_free (dir);
+                       return FALSE;
+               }
+               if (!p)
+                       break;
+               *p++ = '/';
+       }
+       
+       g_free (dir);
+       return TRUE;
+#endif
+}
+
+static char *
+make_shadow_copy (const char *filename)
+{
+       gchar *sibling_source, *sibling_target;
+       gint sibling_source_len, sibling_target_len;
+       guint16 *orig, *dest;
+       char *shadow;
+       gboolean copy_result;
+       MonoException *exc;
+       
+       shadow = get_shadow_assembly_location (filename);
+       if (ensure_directory_exists (shadow) == FALSE) {
+               exc = mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists).");
+               mono_raise_exception (exc);
+       }       
+
+       orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
+       dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
+       copy_result = CopyFile (orig, dest, FALSE);
+       g_free (dest);
+       g_free (orig);
+
+       if (copy_result == FALSE) {
+               g_free (shadow);
+               exc = mono_get_exception_execution_engine ("Failed to create shadow copy (CopyFile).");
+               mono_raise_exception (exc);
+       }
+
+       /* attempt to copy .mdb, .config if they exist */
+       sibling_source = g_strconcat (filename, ".config", NULL);
+       sibling_source_len = strlen (sibling_source);
+       sibling_target = g_strconcat (shadow, ".config", NULL);
+       sibling_target_len = strlen (sibling_target);
+       
+       copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
+       if (copy_result == TRUE)
+               copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
+       
+       g_free (sibling_source);
+       g_free (sibling_target);
+       
+       if (copy_result == FALSE)  {
+               g_free (shadow);
+               exc = mono_get_exception_execution_engine ("Failed to create shadow copy of sibling data (CopyFile).");
+               mono_raise_exception (exc);
+       }
+       
+       return shadow;
+}
+
 static gboolean
 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
-                                       const gchar *path3, const gchar *path4, gboolean refonly)
+                                       const gchar *path3, const gchar *path4,
+                                       gboolean refonly, gboolean is_private)
 {
+       
        gchar *fullpath;
 
        *assembly = NULL;
        fullpath = g_build_filename (path1, path2, path3, path4, NULL);
-       if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
+       if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR)) {
+               if (is_private) {
+                       char *new_path = make_shadow_copy (fullpath);
+                       g_free (fullpath);
+                       fullpath = new_path;
+               }
+
                *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
+       }
 
        g_free (fullpath);
        return (*assembly != NULL);
@@ -855,6 +1034,7 @@ real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolea
        gchar *filename;
        const gchar *local_culture;
        gint len;
+       gboolean is_private = FALSE;
 
        if (!culture || *culture == '\0') {
                local_culture = "";
@@ -866,28 +1046,30 @@ real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolea
        len = strlen (filename);
 
        for (path = search_path; *path; path++) {
-               if (**path == '\0')
+               if (**path == '\0') {
+                       is_private = TRUE;
                        continue; /* Ignore empty ApplicationBase */
+               }
 
                /* See test cases in bug #58992 and bug #57710 */
                /* 1st try: [culture]/[name].dll (culture may be empty) */
                strcpy (filename + len - 4, ".dll");
-               if (try_load_from (&result, *path, local_culture, "", filename, refonly))
+               if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
                        break;
 
                /* 2nd try: [culture]/[name].exe (culture may be empty) */
                strcpy (filename + len - 4, ".exe");
-               if (try_load_from (&result, *path, local_culture, "", filename, refonly))
+               if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
                        break;
 
                /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
                strcpy (filename + len - 4, ".dll");
-               if (try_load_from (&result, *path, local_culture, name, filename, refonly))
+               if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
                        break;
 
                /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
                strcpy (filename + len - 4, ".exe");
-               if (try_load_from (&result, *path, local_culture, name, filename, refonly))
+               if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
                        break;
        }
 
@@ -967,21 +1149,27 @@ ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean re
 
        ass = mono_assembly_open_full (filename, &status, refOnly);
        
-       g_free (name);
-
        if (!ass){
-               MonoException *exc = mono_get_exception_file_not_found (fname);
+               MonoException *exc;
+
+               if (status == MONO_IMAGE_IMAGE_INVALID)
+                       exc = mono_get_exception_bad_image_format2 (NULL, fname);
+               else
+                       exc = mono_get_exception_file_not_found2 (NULL, fname);
+               g_free (name);
                mono_raise_exception (exc);
        }
 
+       g_free (name);
+
        return mono_assembly_get_object (domain, ass);
 }
 
 MonoReflectionAssembly *
 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
-                                                                                       MonoArray *raw_assembly,
-                                                                                       MonoArray *raw_symbol_store, MonoObject *evidence,
-                                                                                       MonoBoolean refonly)
+                                           MonoArray *raw_assembly,
+                                           MonoArray *raw_symbol_store, MonoObject *evidence,
+                                           MonoBoolean refonly)
 {
        MonoAssembly *ass;
        MonoReflectionAssembly *refass = NULL;
@@ -990,16 +1178,17 @@ ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad,
        guint32 raw_assembly_len = mono_array_length (raw_assembly);
        MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
 
-       if (raw_symbol_store)
-               mono_raise_exception (mono_get_exception_not_implemented ("LoadAssemblyRaw: Raw Symbol Store not Implemented"));
-  
        if (!image) {
                mono_raise_exception (mono_get_exception_bad_image_format (""));
                return NULL;
        }
 
+       if (raw_symbol_store != NULL)
+               mono_debug_init_2_memory (image, mono_array_addr (raw_symbol_store, guint8, 0), mono_array_length (raw_symbol_store));
+
        ass = mono_assembly_load_from_full (image, "", &status, refonly);
 
+
        if (!ass) {
                mono_image_close (image);
                mono_raise_exception (mono_get_exception_bad_image_format (""));
@@ -1007,7 +1196,7 @@ ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad,
        }
 
        refass = mono_assembly_get_object (domain, ass);
-       refass->evidence = evidence;
+       MONO_OBJECT_SETREF (refass, evidence, evidence);
        return refass;
 }
 
@@ -1034,7 +1223,7 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef,
                MonoException *exc;
 
                /* This is a parse error... */
-               exc = mono_get_exception_file_not_found (assRef);
+               exc = mono_get_exception_file_not_found2 (NULL, assRef);
                mono_raise_exception (exc);
        }
 
@@ -1043,14 +1232,14 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef,
 
        if (!ass && (refass = try_assembly_resolve (domain, assRef, refOnly)) == NULL){
                /* FIXME: it doesn't make much sense since we really don't have a filename ... */
-               MonoException *exc = mono_get_exception_file_not_found (assRef);
+               MonoException *exc = mono_get_exception_file_not_found2 (NULL, assRef);
                mono_raise_exception (exc);
        }
 
        if (refass == NULL)
                refass = mono_assembly_get_object (domain, ass);
 
-       refass->evidence = evidence;
+       MONO_OBJECT_SETREF (refass, evidence, evidence);
        return refass;
 }
 
@@ -1110,7 +1299,7 @@ ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file,
        g_free (filename);
 
        if (!assembly)
-               mono_raise_exception (mono_get_exception_file_not_found (file));
+               mono_raise_exception (mono_get_exception_file_not_found2 (NULL, file));
 
        image = assembly->image;
 
@@ -1123,7 +1312,7 @@ ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file,
                args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
 
        refass = mono_assembly_get_object (ad->data, assembly);
-       refass->evidence = evidence;
+       MONO_OBJECT_SETREF (refass, evidence, evidence);
 
        res = mono_runtime_exec_main (method, (MonoArray *)args, NULL);
 
@@ -1304,6 +1493,9 @@ unload_thread_main (void *arg)
 
        /* printf ("UNLOADED %s.\n", domain->friendly_name); */
 
+       /* remove from the handle table the items related to this domain */
+       mono_gchandle_free_domain (domain);
+
        mono_domain_free (domain, FALSE);
 
        mono_gc_collect (mono_gc_max_generation ());
@@ -1380,6 +1572,9 @@ mono_domain_unload (MonoDomain *domain)
        thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
 #else
        thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
+       if (thread_handle == NULL) {
+               return;
+       }
        ResumeThread (thread_handle);
 #endif
 
@@ -1388,8 +1583,10 @@ mono_domain_unload (MonoDomain *domain)
                if (mono_thread_has_appdomain_ref (mono_thread_current (), domain) && (mono_thread_interruption_requested ()))
                        /* The unload thread tries to abort us */
                        /* The icall wrapper will execute the abort */
+                       CloseHandle (thread_handle);
                        return;
        }
+       CloseHandle (thread_handle);
 
        mono_domain_set (caller_domain, FALSE);