2007-10-11 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / metadata / appdomain.c
1 /*
2  * appdomain.c: AppDomain functions
3  *
4  * Authors:
5  *      Dietmar Maurer (dietmar@ximian.com)
6  *      Patrik Torstensson
7  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8  *
9  * (c) 2001-2003 Ximian, Inc. (http://www.ximian.com)
10  */
11 #undef ASSEMBLY_LOAD_DEBUG
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #include <errno.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21
22 #include <mono/os/gc_wrapper.h>
23
24 #include <mono/metadata/object.h>
25 #include <mono/metadata/domain-internals.h>
26 #include "mono/metadata/metadata-internals.h"
27 #include <mono/metadata/assembly.h>
28 #include <mono/metadata/exception.h>
29 #include <mono/metadata/threads.h>
30 #include <mono/metadata/socket-io.h>
31 #include <mono/metadata/tabledefs.h>
32 #include <mono/metadata/gc-internal.h>
33 #include <mono/metadata/mono-gc.h>
34 #include <mono/metadata/marshal.h>
35 #include <mono/metadata/monitor.h>
36 #include <mono/metadata/threadpool.h>
37 #include <mono/metadata/mono-debug.h>
38 #include <mono/utils/mono-uri.h>
39 #include <mono/utils/mono-logger.h>
40 #include <mono/utils/mono-path.h>
41 #include <mono/utils/mono-stdlib.h>
42 #include <mono/utils/mono-io-portability.h>
43 #ifdef PLATFORM_WIN32
44 #include <direct.h>
45 #endif
46
47 #define MONO_CORLIB_VERSION 58
48
49 typedef struct
50 {
51         int runtime_count;
52         int assemblybinding_count;
53         MonoDomain *domain;
54 } RuntimeConfig;
55
56 CRITICAL_SECTION mono_delegate_section;
57
58 static gunichar2 process_guid [36];
59 static gboolean process_guid_set = FALSE;
60
61 static gboolean shutting_down = FALSE;
62
63 static MonoAssembly *
64 mono_domain_assembly_preload (MonoAssemblyName *aname,
65                               gchar **assemblies_path,
66                               gpointer user_data);
67
68 static MonoAssembly *
69 mono_domain_assembly_search (MonoAssemblyName *aname,
70                                                          gpointer user_data);
71
72 static MonoAssembly *
73 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
74                                                                           gpointer user_data);
75
76 static void
77 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
78
79 static void
80 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
81
82 static void
83 mono_domain_unload (MonoDomain *domain);
84
85 /**
86  * mono_runtime_init:
87  * @domain: domain returned by mono_init ()
88  *
89  * Initialize the core AppDomain: this function will run also some
90  * IL initialization code, so it needs the execution engine to be fully 
91  * operational.
92  *
93  * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
94  * we know the entry_assembly.
95  *
96  */
97 void
98 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
99                    MonoThreadAttachCB attach_cb)
100 {
101         MonoAppDomainSetup *setup;
102         MonoAppDomain *ad;
103         MonoClass *class;
104         MonoString *arg;
105
106         mono_portability_helpers_init ();
107         
108         MONO_GC_PRE_INIT ();
109         mono_monitor_init ();
110         mono_thread_pool_init ();
111         mono_marshal_init ();
112
113         mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
114         mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
115         mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
116         mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
117         mono_install_assembly_postload_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
118         mono_install_assembly_postload_refonly_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
119         mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
120         mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
121
122         mono_thread_init (start_cb, attach_cb);
123
124         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
125         setup = (MonoAppDomainSetup *) mono_object_new (domain, class);
126
127         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
128         ad = (MonoAppDomain *) mono_object_new (domain, class);
129         ad->data = domain;
130         domain->domain = ad;
131         domain->setup = setup;
132
133         InitializeCriticalSection (&mono_delegate_section);
134
135         mono_thread_attach (domain);
136         mono_context_init (domain);
137         mono_context_set (domain->default_context);
138
139         mono_type_initialization_init ();
140
141         
142         /*
143          * Create an instance early since we can't do it when there is no memory.
144          */
145         arg = mono_string_new (domain, "Out of memory");
146         domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
147         
148         /* 
149          * These two are needed because the signal handlers might be executing on
150          * an alternate stack, and Boehm GC can't handle that.
151          */
152         arg = mono_string_new (domain, "A null value was found where an object instance was required");
153         domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
154         arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
155         domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
156         
157         /* GC init has to happen after thread init */
158         mono_gc_init ();
159
160         mono_network_init ();
161
162         /* mscorlib is loaded before we install the load hook */
163         mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
164         
165         return;
166 }
167
168 static int
169 mono_get_corlib_version (void)
170 {
171         MonoClass *klass;
172         MonoClassField *field;
173         MonoObject *value;
174
175         klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
176         mono_class_init (klass);
177         field = mono_class_get_field_from_name (klass, "mono_corlib_version");
178         if (!field)
179                 return -1;
180         if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
181                 return -1;
182         value = mono_field_get_value_object (mono_domain_get (), field, NULL);
183         return *(gint32*)((gchar*)value + sizeof (MonoObject));
184 }
185
186 const char*
187 mono_check_corlib_version (void)
188 {
189         int version = mono_get_corlib_version ();
190         if (version != MONO_CORLIB_VERSION)
191                 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
192         else
193                 return NULL;
194 }
195
196 void
197 mono_context_init (MonoDomain *domain)
198 {
199         MonoClass *class;
200         MonoAppContext *context;
201
202         class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
203         context = (MonoAppContext *) mono_object_new (domain, class);
204         context->domain_id = domain->domain_id;
205         context->context_id = 0;
206         domain->default_context = context;
207 }
208
209 /**
210  * mono_runtime_cleanup:
211  * @domain: unused.
212  *
213  * Internal routine.
214  *
215  * This must not be called while there are still running threads executing
216  * managed code.
217  */
218 void
219 mono_runtime_cleanup (MonoDomain *domain)
220 {
221         shutting_down = TRUE;
222
223         /* This ends up calling any pending pending (for at most 2 seconds) */
224         mono_gc_cleanup ();
225
226         mono_thread_cleanup ();
227
228         mono_network_cleanup ();
229
230         mono_marshal_cleanup ();
231
232         mono_type_initialization_cleanup ();
233
234         mono_monitor_cleanup ();
235
236 #ifndef PLATFORM_WIN32
237         _wapi_cleanup ();
238 #endif
239 }
240
241 static MonoDomainFunc quit_function = NULL;
242
243 void
244 mono_install_runtime_cleanup (MonoDomainFunc func)
245 {
246         quit_function = func;
247 }
248
249 void
250 mono_runtime_quit ()
251 {
252         if (quit_function != NULL)
253                 quit_function (mono_get_root_domain (), NULL);
254 }
255
256 /** 
257  * mono_runtime_set_shutting_down:
258  *
259  * Invoked by System.Environment.Exit to flag that the runtime
260  * is shutting down.
261  */
262 void
263 mono_runtime_set_shutting_down (void)
264 {
265         shutting_down = TRUE;
266 }
267
268 /**
269  * mono_runtime_is_shutting_down:
270  *
271  * Returns whether the runtime has been flagged for shutdown.
272  *
273  * This is consumed by the P:System.Environment.HasShutdownStarted
274  * property.
275  *
276  */
277 gboolean
278 mono_runtime_is_shutting_down (void)
279 {
280         return shutting_down;
281 }
282
283 /**
284  * mono_domain_has_type_resolve:
285  * @domain: application domains being looked up
286  *
287  * Returns true if the AppDomain.TypeResolve field has been
288  * set.
289  */
290 gboolean
291 mono_domain_has_type_resolve (MonoDomain *domain)
292 {
293         static MonoClassField *field = NULL;
294         MonoObject *o;
295
296         if (field == NULL) {
297                 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
298                 g_assert (field);
299         }
300
301         mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
302         return o != NULL;
303 }
304
305 /**
306  * mono_domain_try_type_resolve:
307  * @domain: application domainwhere the name where the type is going to be resolved
308  * @name: the name of the type to resolve or NULL.
309  * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
310  *
311  * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
312  * the assembly that matches name.
313  *
314  * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
315  *
316  * Returns: A MonoReflectionAssembly or NULL if not found
317  */
318 MonoReflectionAssembly *
319 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
320 {
321         MonoClass *klass;
322         void *params [1];
323         static MonoMethod *method = NULL;
324
325         g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
326
327         if (method == NULL) {
328                 klass = domain->domain->mbr.obj.vtable->klass;
329                 g_assert (klass);
330
331                 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
332                 if (method == NULL) {
333                         g_warning ("Method AppDomain.DoTypeResolve not found.\n");
334                         return NULL;
335                 }
336         }
337
338         if (name)
339                 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
340         else
341                 *params = tb;
342         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
343 }
344
345 /**
346  * mono_domain_owns_vtable_slot:
347  *
348  *  Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
349  */
350 gboolean
351 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
352 {
353         gboolean res;
354
355         mono_domain_lock (domain);
356         res = mono_mempool_contains_addr (domain->mp, vtable_slot);
357         mono_domain_unlock (domain);
358         return res;
359 }
360
361 /**
362  * mono_domain_set:
363  * @domain: domain
364  * @force: force setting.
365  *
366  * Set the current appdomain to @domain. If @force is set, set it even
367  * if it is being unloaded.
368  *
369  * Returns:
370  *   TRUE on success;
371  *   FALSE if the domain is unloaded
372  */
373 gboolean
374 mono_domain_set (MonoDomain *domain, gboolean force)
375 {
376         if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
377                 return FALSE;
378
379         mono_domain_set_internal (domain);
380
381         return TRUE;
382 }
383
384 MonoObject *
385 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
386 {
387         MonoDomain *add;
388         MonoObject *o;
389         char *str;
390
391         MONO_ARCH_SAVE_REGS;
392
393         g_assert (ad != NULL);
394         add = ad->data;
395         g_assert (add != NULL);
396
397         if (name == NULL)
398                 mono_raise_exception (mono_get_exception_argument_null ("name"));
399
400         str = mono_string_to_utf8 (name);
401
402         mono_domain_lock (add);
403
404         if (!strcmp (str, "APPBASE"))
405                 o = (MonoObject *)add->setup->application_base;
406         else if (!strcmp (str, "APP_CONFIG_FILE"))
407                 o = (MonoObject *)add->setup->configuration_file;
408         else if (!strcmp (str, "DYNAMIC_BASE"))
409                 o = (MonoObject *)add->setup->dynamic_base;
410         else if (!strcmp (str, "APP_NAME"))
411                 o = (MonoObject *)add->setup->application_name;
412         else if (!strcmp (str, "CACHE_BASE"))
413                 o = (MonoObject *)add->setup->cache_path;
414         else if (!strcmp (str, "PRIVATE_BINPATH"))
415                 o = (MonoObject *)add->setup->private_bin_path;
416         else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
417                 o = (MonoObject *)add->setup->private_bin_path_probe;
418         else if (!strcmp (str, "SHADOW_COPY_DIRS"))
419                 o = (MonoObject *)add->setup->shadow_copy_directories;
420         else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
421                 o = (MonoObject *)add->setup->shadow_copy_files;
422         else 
423                 o = mono_g_hash_table_lookup (add->env, name);
424
425         mono_domain_unlock (add);
426         g_free (str);
427
428         if (!o)
429                 return NULL;
430
431         return o;
432 }
433
434 void
435 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
436 {
437         MonoDomain *add;
438
439         MONO_ARCH_SAVE_REGS;
440
441         g_assert (ad != NULL);
442         add = ad->data;
443         g_assert (add != NULL);
444
445         if (name == NULL)
446                 mono_raise_exception (mono_get_exception_argument_null ("name"));
447
448         mono_domain_lock (add);
449
450         mono_g_hash_table_insert (add->env, name, data);
451
452         mono_domain_unlock (add);
453 }
454
455 MonoAppDomainSetup *
456 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
457 {
458         MONO_ARCH_SAVE_REGS;
459
460         g_assert (ad != NULL);
461         g_assert (ad->data != NULL);
462
463         return ad->data->setup;
464 }
465
466 MonoString *
467 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
468 {
469         MONO_ARCH_SAVE_REGS;
470
471         g_assert (ad != NULL);
472         g_assert (ad->data != NULL);
473
474         return mono_string_new (ad->data, ad->data->friendly_name);
475 }
476
477 MonoAppDomain *
478 ves_icall_System_AppDomain_getCurDomain ()
479 {
480         MonoDomain *add = mono_domain_get ();
481
482         MONO_ARCH_SAVE_REGS;
483
484         return add->domain;
485 }
486
487 MonoAppDomain *
488 ves_icall_System_AppDomain_getRootDomain ()
489 {
490         MonoDomain *root = mono_get_root_domain ();
491
492         MONO_ARCH_SAVE_REGS;
493
494         return root->domain;
495 }
496
497 static char*
498 get_attribute_value (const gchar **attribute_names, 
499                      const gchar **attribute_values, 
500                      const char *att_name)
501 {
502         int n;
503         for (n = 0; attribute_names [n] != NULL; n++) {
504                 if (strcmp (attribute_names [n], att_name) == 0)
505                         return g_strdup (attribute_values [n]);
506         }
507         return NULL;
508 }
509
510 static void
511 start_element (GMarkupParseContext *context, 
512                const gchar         *element_name,
513                const gchar        **attribute_names,
514                const gchar        **attribute_values,
515                gpointer             user_data,
516                GError             **error)
517 {
518         RuntimeConfig *runtime_config = user_data;
519         
520         if (strcmp (element_name, "runtime") == 0) {
521                 runtime_config->runtime_count++;
522                 return;
523         }
524
525         if (strcmp (element_name, "assemblyBinding") == 0) {
526                 runtime_config->assemblybinding_count++;
527                 return;
528         }
529         
530         if (runtime_config->runtime_count != 1 || runtime_config->assemblybinding_count != 1)
531                 return;
532
533         if (strcmp (element_name, "probing") != 0)
534                 return;
535
536         g_free (runtime_config->domain->private_bin_path);
537         runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath");
538         if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) {
539                 g_free (runtime_config->domain->private_bin_path);
540                 runtime_config->domain->private_bin_path = NULL;
541                 return;
542         }
543 }
544
545 static void
546 end_element (GMarkupParseContext *context,
547              const gchar         *element_name,
548              gpointer             user_data,
549              GError             **error)
550 {
551         RuntimeConfig *runtime_config = user_data;
552         if (strcmp (element_name, "runtime") == 0)
553                 runtime_config->runtime_count--;
554         else if (strcmp (element_name, "assemblyBinding") == 0)
555                 runtime_config->assemblybinding_count--;
556 }
557
558 static const GMarkupParser
559 mono_parser = {
560         start_element,
561         end_element,
562         NULL,
563         NULL,
564         NULL
565 };
566
567 static void
568 mono_set_private_bin_path_from_config (MonoDomain *domain)
569 {
570         gchar *config_file, *text;
571         gsize len;
572         struct stat sbuf;
573         GMarkupParseContext *context;
574         RuntimeConfig runtime_config;
575         
576         if (!domain || !domain->setup || !domain->setup->configuration_file)
577                 return;
578
579         config_file = mono_string_to_utf8 (domain->setup->configuration_file);
580         if (stat (config_file, &sbuf) != 0) {
581                 g_free (config_file);
582                 return;
583         }
584
585         if (!g_file_get_contents (config_file, &text, &len, NULL)) {
586                 g_free (config_file);
587                 return;
588         }
589         g_free (config_file);
590
591         runtime_config.runtime_count = 0;
592         runtime_config.assemblybinding_count = 0;
593         runtime_config.domain = domain;
594         
595         context = g_markup_parse_context_new (&mono_parser, 0, &runtime_config, NULL);
596         if (g_markup_parse_context_parse (context, text, len, NULL))
597                 g_markup_parse_context_end_parse (context, NULL);
598         g_markup_parse_context_free (context);
599         g_free (text);
600 }
601
602 MonoAppDomain *
603 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
604 {
605         MonoClass *adclass;
606         MonoAppDomain *ad;
607         MonoDomain *data;
608         
609         MONO_ARCH_SAVE_REGS;
610
611         adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
612
613         /* FIXME: pin all those objects */
614         data = mono_domain_create();
615
616         ad = (MonoAppDomain *) mono_object_new (data, adclass);
617         ad->data = data;
618         data->domain = ad;
619         data->setup = setup;
620         data->friendly_name = mono_string_to_utf8 (friendly_name);
621         data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException");
622
623         if (!setup->application_base) {
624                 /* Inherit from the root domain since MS.NET does this */
625                 MonoDomain *root = mono_get_root_domain ();
626                 if (root->setup->application_base)
627                         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)));
628         }
629
630         mono_set_private_bin_path_from_config (data);
631         
632         mono_context_init (data);
633
634         add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
635
636         return ad;
637 }
638
639 MonoArray *
640 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
641 {
642         MonoDomain *domain = ad->data; 
643         MonoAssembly* ass;
644         static MonoClass *System_Reflection_Assembly;
645         MonoArray *res;
646         GSList *tmp;
647         int i, count;
648         
649         MONO_ARCH_SAVE_REGS;
650
651         if (!System_Reflection_Assembly)
652                 System_Reflection_Assembly = mono_class_from_name (
653                         mono_defaults.corlib, "System.Reflection", "Assembly");
654
655         count = 0;
656         /* Need to skip internal assembly builders created by remoting */
657         mono_domain_assemblies_lock (domain);
658         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
659                 ass = tmp->data;
660                 if (refonly && !ass->ref_only)
661                         continue;
662                 if (!ass->corlib_internal)
663                         count++;
664         }
665         res = mono_array_new (domain, System_Reflection_Assembly, count);
666         i = 0;
667         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
668                 ass = tmp->data;
669                 if (refonly && !ass->ref_only)
670                         continue;
671                 if (ass->corlib_internal)
672                         continue;
673                 mono_array_setref (res, i, mono_assembly_get_object (domain, ass));
674                 ++i;
675         }
676         mono_domain_assemblies_unlock (domain);
677
678         return res;
679 }
680
681 MonoReflectionAssembly *
682 mono_try_assembly_resolve (MonoDomain *domain, MonoString *fname, gboolean refonly)
683 {
684         MonoClass *klass;
685         MonoMethod *method;
686         MonoBoolean isrefonly;
687         gpointer params [2];
688
689         g_assert (domain != NULL && fname != NULL);
690
691         klass = domain->domain->mbr.obj.vtable->klass;
692         g_assert (klass);
693         
694         method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
695         if (method == NULL) {
696                 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
697                 return NULL;
698         }
699
700         isrefonly = refonly ? 1 : 0;
701         params [0] = fname;
702         params [1] = &isrefonly;
703         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
704 }
705
706 static MonoAssembly *
707 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
708                                                                           gpointer user_data)
709 {
710         gboolean refonly = GPOINTER_TO_UINT (user_data);
711         MonoReflectionAssembly *assembly;
712         MonoDomain *domain = mono_domain_get ();
713         char *aname_str;
714
715         aname_str = mono_stringify_assembly_name (aname);
716
717         /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
718         assembly = mono_try_assembly_resolve (domain, mono_string_new (domain, aname_str), refonly);
719
720         g_free (aname_str);
721
722         if (assembly)
723                 return assembly->assembly;
724         else
725                 return NULL;
726 }
727         
728 /*
729  * LOCKING: assumes assemblies_lock in the domain is already locked.
730  */
731 static void
732 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
733 {
734         gint i;
735         GSList *tmp;
736         gboolean destroy_ht = FALSE;
737
738         if (!ass->aname.name)
739                 return;
740
741         if (!ht) {
742                 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
743                 destroy_ht = TRUE;
744         }
745
746         /* FIXME: handle lazy loaded assemblies */
747         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
748                 g_hash_table_insert (ht, tmp->data, tmp->data);
749         }
750         if (!g_hash_table_lookup (ht, ass)) {
751                 mono_assembly_addref (ass);
752                 g_hash_table_insert (ht, ass, ass);
753                 domain->domain_assemblies = g_slist_prepend (domain->domain_assemblies, ass);
754                 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);
755         }
756
757         if (ass->image->references) {
758                 for (i = 0; ass->image->references [i] != NULL; i++) {
759                         if (ass->image->references [i] != REFERENCE_MISSING)
760                                 if (!g_hash_table_lookup (ht, ass->image->references [i])) {
761                                         add_assemblies_to_domain (domain, ass->image->references [i], ht);
762                                 }
763                 }
764         }
765         if (destroy_ht)
766                 g_hash_table_destroy (ht);
767 }
768
769 static void
770 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
771 {
772         static MonoClassField *assembly_load_field;
773         static MonoMethod *assembly_load_method;
774         MonoDomain *domain = mono_domain_get ();
775         MonoReflectionAssembly *ref_assembly;
776         MonoClass *klass;
777         gpointer load_value;
778         void *params [1];
779
780         if (!domain->domain)
781                 /* This can happen during startup */
782                 return;
783 #ifdef ASSEMBLY_LOAD_DEBUG
784         fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
785 #endif
786         klass = domain->domain->mbr.obj.vtable->klass;
787
788         mono_domain_assemblies_lock (domain);
789         add_assemblies_to_domain (domain, assembly, NULL);
790         mono_domain_assemblies_unlock (domain);
791
792         if (assembly_load_field == NULL) {
793                 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
794                 g_assert (assembly_load_field);
795         }
796
797         mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
798         if (load_value == NULL) {
799                 /* No events waiting to be triggered */
800                 return;
801         }
802
803         ref_assembly = mono_assembly_get_object (domain, assembly);
804         g_assert (ref_assembly);
805
806         if (assembly_load_method == NULL) {
807                 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
808                 g_assert (assembly_load_method);
809         }
810
811         *params = ref_assembly;
812         mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
813 }
814
815 static void
816 set_domain_search_path (MonoDomain *domain)
817 {
818         MonoAppDomainSetup *setup;
819         gchar **tmp;
820         gchar *search_path = NULL;
821         gint i;
822         gint npaths = 0;
823         gchar **pvt_split = NULL;
824         GError *error = NULL;
825         gint appbaselen = -1;
826
827         if ((domain->search_path != NULL) && !domain->setup->path_changed)
828                 return;
829         if (!domain->setup)
830                 return;
831
832         setup = domain->setup;
833         if (!setup->application_base)
834                 return; /* Must set application base to get private path working */
835
836         npaths++;
837         
838         if (setup->private_bin_path)
839                 search_path = mono_string_to_utf8 (setup->private_bin_path);
840         
841         if (domain->private_bin_path) {
842                 if (search_path == NULL)
843                         search_path = domain->private_bin_path;
844                 else {
845                         gchar *tmp2 = search_path;
846                         search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL);
847                         g_free (tmp2);
848                 }
849         }
850         
851         if (search_path) {
852                 /*
853                  * As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of
854                  * directories relative to ApplicationBase separated by semicolons (see
855                  * http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx)
856                  * The loop below copes with the fact that some Unix applications may use ':' (or
857                  * System.IO.Path.PathSeparator) as the path search separator. We replace it with
858                  * ';' for the subsequent split.
859                  *
860                  * The issue was reported in bug #81446
861                  */
862
863 #ifndef PLATFORM_WIN32
864                 gint slen;
865
866                 slen = strlen (search_path);
867                 for (i = 0; i < slen; i++)
868                         if (search_path [i] == ':')
869                                 search_path [i] = ';';
870 #endif
871                 
872                 pvt_split = g_strsplit (search_path, ";", 1000);
873                 g_free (search_path);
874                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
875         }
876
877         if (!npaths) {
878                 if (pvt_split)
879                         g_strfreev (pvt_split);
880                 /*
881                  * Don't do this because the first time is called, the domain
882                  * setup is not finished.
883                  *
884                  * domain->search_path = g_malloc (sizeof (char *));
885                  * domain->search_path [0] = NULL;
886                 */
887                 return;
888         }
889
890         if (domain->search_path)
891                 g_strfreev (domain->search_path);
892
893         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
894         tmp [npaths] = NULL;
895
896         *tmp = mono_string_to_utf8 (setup->application_base);
897
898         /* FIXME: is this needed? */
899         if (strncmp (*tmp, "file://", 7) == 0) {
900                 gchar *file = *tmp;
901                 gchar *uri = *tmp;
902                 gchar *tmpuri;
903
904                 if (uri [7] != '/')
905                         uri = g_strdup_printf ("file:///%s", uri + 7);
906
907                 tmpuri = uri;
908                 uri = mono_escape_uri_string (tmpuri);
909                 *tmp = g_filename_from_uri (uri, NULL, &error);
910                 g_free (uri);
911
912                 if (tmpuri != file)
913                         g_free (tmpuri);
914
915                 if (error != NULL) {
916                         g_warning ("%s\n", error->message);
917                         g_error_free (error);
918                         *tmp = file;
919                 } else {
920                         g_free (file);
921                 }
922         }
923
924         for (i = 1; pvt_split && i < npaths; i++) {
925                 if (g_path_is_absolute (pvt_split [i - 1])) {
926                         tmp [i] = g_strdup (pvt_split [i - 1]);
927                 } else {
928                         tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
929                 }
930
931                 if (strchr (tmp [i], '.')) {
932                         gchar *reduced;
933                         gchar *freeme;
934
935                         reduced = mono_path_canonicalize (tmp [i]);
936                         if (appbaselen == -1)
937                                 appbaselen = strlen (tmp [0]);
938
939                         if (strncmp (tmp [0], reduced, appbaselen)) {
940                                 g_free (reduced);
941                                 g_free (tmp [i]);
942                                 tmp [i] = g_strdup ("");
943                                 continue;
944                         }
945
946                         freeme = tmp [i];
947                         tmp [i] = reduced;
948                         g_free (freeme);
949                 }
950         }
951         
952         if (setup->private_bin_path_probe != NULL) {
953                 g_free (tmp [0]);
954                 tmp [0] = g_strdup ("");
955         }
956                 
957         domain->setup->path_changed = FALSE;
958
959         g_strfreev (pvt_split);
960 }
961
962 static gboolean
963 shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
964 {
965         guint16 *orig, *dest;
966         gboolean copy_result;
967         
968         strcpy (src + srclen - tail_len, extension);
969         if (!g_file_test (src, G_FILE_TEST_IS_REGULAR))
970                 return TRUE;
971         orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
972
973         strcpy (target + targetlen - tail_len, extension);
974         dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
975         
976         DeleteFile (dest);
977         copy_result = CopyFile (orig, dest, FALSE);
978         g_free (orig);
979         g_free (dest);
980         
981         return copy_result;
982 }
983
984 static gint32 
985 get_cstring_hash (const char *str)
986 {
987         int len, i;
988         const char *p;
989         gint32 h = 0;
990         
991         if (!str || !str [0])
992                 return 0;
993                 
994         len = strlen (str);
995         p = str;
996         for (i = 0; i < len; i++) {
997                 h = (h << 5) - h + *p;
998                 p++;
999         }
1000         
1001         return h;
1002 }
1003
1004 static char *
1005 get_shadow_assembly_location (const char *filename)
1006 {
1007         gint32 hash = 0, hash2 = 0;
1008         char name_hash [9];
1009         char path_hash [30];
1010         char *bname = g_path_get_basename (filename);
1011         char *dirname = g_path_get_dirname (filename);
1012         char *location, *dyn_base;
1013         MonoDomain *domain = mono_domain_get ();
1014         
1015         hash = get_cstring_hash (bname);
1016         hash2 = get_cstring_hash (dirname);
1017         g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
1018         g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial);
1019         dyn_base = mono_string_to_utf8 (domain->setup->dynamic_base);
1020         location = g_build_filename (dyn_base, "assembly", "shadow", name_hash, path_hash, bname, NULL);
1021         g_free (dyn_base);
1022         g_free (bname);
1023         g_free (dirname);
1024         return location;
1025 }
1026
1027 static gboolean
1028 ensure_directory_exists (const char *filename)
1029 {
1030 #ifdef PLATFORM_WIN32
1031         gchar *dir_utf8 = g_path_get_dirname (filename);
1032         gunichar2 *p;
1033         gunichar2 *dir_utf16 = NULL;
1034         int retval;
1035         
1036         if (!dir_utf8 || !dir_utf8 [0])
1037                 return FALSE;
1038
1039         dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
1040         g_free (dir_utf8);
1041
1042         if (!dir_utf16)
1043                 return FALSE;
1044
1045         p = dir_utf16;
1046
1047         /* make life easy and only use one directory seperator */
1048         while (*p != '\0')
1049         {
1050                 if (*p == '/')
1051                         *p = '\\';
1052                 p++;
1053         }
1054
1055         p = dir_utf16;
1056
1057         /* get past C:\ )*/
1058         while (*p++ != '\\')    
1059         {
1060         }
1061
1062         while (1) {
1063                 BOOL bRet = FALSE;
1064                 p = wcschr (p, '\\');
1065                 if (p)
1066                         *p = '\0';
1067                 retval = _wmkdir (dir_utf16);
1068                 if (retval != 0 && errno != EEXIST) {
1069                         g_free (dir_utf16);
1070                         return FALSE;
1071                 }
1072                 if (!p)
1073                         break;
1074                 *p++ = '\\';
1075         }
1076         
1077         g_free (dir_utf16);
1078         return TRUE;
1079 #else
1080         char *p;
1081         gchar *dir = g_path_get_dirname (filename);
1082         int retval;
1083         
1084         if (!dir || !dir [0])
1085                 return FALSE;
1086
1087         p = dir;
1088         while (*p == '/')
1089                 p++;
1090
1091         while (1) {
1092                 p = strchr (p, '/');
1093                 if (p)
1094                         *p = '\0';
1095                 retval = mkdir (dir, 0777);
1096                 if (retval != 0 && errno != EEXIST) {
1097                         g_free (dir);
1098                         return FALSE;
1099                 }
1100                 if (!p)
1101                         break;
1102                 *p++ = '/';
1103         }
1104         
1105         g_free (dir);
1106         return TRUE;
1107 #endif
1108 }
1109
1110 static char *
1111 make_shadow_copy (const char *filename)
1112 {
1113         gchar *sibling_source, *sibling_target;
1114         gint sibling_source_len, sibling_target_len;
1115         guint16 *orig, *dest;
1116         char *shadow;
1117         gboolean copy_result;
1118         MonoException *exc;
1119         
1120         shadow = get_shadow_assembly_location (filename);
1121         if (ensure_directory_exists (shadow) == FALSE) {
1122                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists).");
1123                 mono_raise_exception (exc);
1124         }       
1125
1126         orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
1127         dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
1128         DeleteFile (dest);
1129         copy_result = CopyFile (orig, dest, FALSE);
1130         g_free (dest);
1131         g_free (orig);
1132
1133         if (copy_result == FALSE) {
1134                 g_free (shadow);
1135                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (CopyFile).");
1136                 mono_raise_exception (exc);
1137         }
1138
1139         /* attempt to copy .mdb, .config if they exist */
1140         sibling_source = g_strconcat (filename, ".config", NULL);
1141         sibling_source_len = strlen (sibling_source);
1142         sibling_target = g_strconcat (shadow, ".config", NULL);
1143         sibling_target_len = strlen (sibling_target);
1144         
1145         copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
1146         if (copy_result == TRUE)
1147                 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
1148         
1149         g_free (sibling_source);
1150         g_free (sibling_target);
1151         
1152         if (copy_result == FALSE)  {
1153                 g_free (shadow);
1154                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy of sibling data (CopyFile).");
1155                 mono_raise_exception (exc);
1156         }
1157         
1158         return shadow;
1159 }
1160
1161 static gboolean
1162 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
1163                                         const gchar *path3, const gchar *path4,
1164                                         gboolean refonly, gboolean is_private)
1165 {
1166         
1167         gchar *fullpath;
1168
1169         *assembly = NULL;
1170         fullpath = g_build_filename (path1, path2, path3, path4, NULL);
1171         if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR)) {
1172                 if (is_private) {
1173                         char *new_path = make_shadow_copy (fullpath);
1174                         g_free (fullpath);
1175                         fullpath = new_path;
1176                 }
1177
1178                 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
1179         }
1180
1181         g_free (fullpath);
1182         return (*assembly != NULL);
1183 }
1184
1185 static MonoAssembly *
1186 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
1187 {
1188         MonoAssembly *result = NULL;
1189         gchar **path;
1190         gchar *filename;
1191         const gchar *local_culture;
1192         gint len;
1193         gboolean is_private = FALSE;
1194
1195         if (!culture || *culture == '\0') {
1196                 local_culture = "";
1197         } else {
1198                 local_culture = culture;
1199         }
1200
1201         filename =  g_strconcat (name, ".dll", NULL);
1202         len = strlen (filename);
1203
1204         for (path = search_path; *path; path++) {
1205                 if (**path == '\0') {
1206                         is_private = TRUE;
1207                         continue; /* Ignore empty ApplicationBase */
1208                 }
1209
1210                 /* See test cases in bug #58992 and bug #57710 */
1211                 /* 1st try: [culture]/[name].dll (culture may be empty) */
1212                 strcpy (filename + len - 4, ".dll");
1213                 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1214                         break;
1215
1216                 /* 2nd try: [culture]/[name].exe (culture may be empty) */
1217                 strcpy (filename + len - 4, ".exe");
1218                 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1219                         break;
1220
1221                 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
1222                 strcpy (filename + len - 4, ".dll");
1223                 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1224                         break;
1225
1226                 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
1227                 strcpy (filename + len - 4, ".exe");
1228                 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1229                         break;
1230         }
1231
1232         g_free (filename);
1233         return result;
1234 }
1235
1236 /*
1237  * Try loading the assembly from ApplicationBase and PrivateBinPath 
1238  * and then from assemblies_path if any.
1239  */
1240 static MonoAssembly *
1241 mono_domain_assembly_preload (MonoAssemblyName *aname,
1242                               gchar **assemblies_path,
1243                               gpointer user_data)
1244 {
1245         MonoDomain *domain = mono_domain_get ();
1246         MonoAssembly *result = NULL;
1247         gboolean refonly = GPOINTER_TO_UINT (user_data);
1248
1249         set_domain_search_path (domain);
1250
1251         if (domain->search_path && domain->search_path [0] != NULL) {
1252                 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
1253         }
1254
1255         if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
1256                 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
1257         }
1258
1259         return result;
1260 }
1261
1262 /*
1263  * Check whenever a given assembly was already loaded in the current appdomain.
1264  */
1265 static MonoAssembly *
1266 mono_domain_assembly_search (MonoAssemblyName *aname,
1267                                                          gpointer user_data)
1268 {
1269         MonoDomain *domain = mono_domain_get ();
1270         GSList *tmp;
1271         MonoAssembly *ass;
1272         gboolean refonly = GPOINTER_TO_UINT (user_data);
1273
1274         mono_domain_assemblies_lock (domain);
1275         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
1276                 ass = tmp->data;
1277                 /* Dynamic assemblies can't match here in MS.NET */
1278                 if (ass->dynamic || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
1279                         continue;
1280
1281                 mono_domain_assemblies_unlock (domain);
1282                 return ass;
1283         }
1284         mono_domain_assemblies_unlock (domain);
1285
1286         return NULL;
1287 }
1288
1289 MonoReflectionAssembly *
1290 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
1291 {
1292         MonoDomain *domain = mono_domain_get ();
1293         char *name, *filename;
1294         MonoImageOpenStatus status = MONO_IMAGE_OK;
1295         MonoAssembly *ass;
1296
1297         MONO_ARCH_SAVE_REGS;
1298
1299         if (fname == NULL) {
1300                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
1301                 mono_raise_exception (exc);
1302         }
1303                 
1304         name = filename = mono_string_to_utf8 (fname);
1305
1306         ass = mono_assembly_open_full (filename, &status, refOnly);
1307         
1308         if (!ass){
1309                 MonoException *exc;
1310
1311                 if (status == MONO_IMAGE_IMAGE_INVALID)
1312                         exc = mono_get_exception_bad_image_format2 (NULL, fname);
1313                 else
1314                         exc = mono_get_exception_file_not_found2 (NULL, fname);
1315                 g_free (name);
1316                 mono_raise_exception (exc);
1317         }
1318
1319         g_free (name);
1320
1321         return mono_assembly_get_object (domain, ass);
1322 }
1323
1324 MonoReflectionAssembly *
1325 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
1326                                             MonoArray *raw_assembly,
1327                                             MonoArray *raw_symbol_store, MonoObject *evidence,
1328                                             MonoBoolean refonly)
1329 {
1330         MonoAssembly *ass;
1331         MonoReflectionAssembly *refass = NULL;
1332         MonoDomain *domain = ad->data;
1333         MonoImageOpenStatus status;
1334         guint32 raw_assembly_len = mono_array_length (raw_assembly);
1335         MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
1336
1337         if (!image) {
1338                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1339                 return NULL;
1340         }
1341
1342         if (raw_symbol_store != NULL)
1343                 mono_debug_open_image_from_memory (image, mono_array_addr (raw_symbol_store, guint8, 0), mono_array_length (raw_symbol_store));
1344
1345         ass = mono_assembly_load_from_full (image, "", &status, refonly);
1346
1347
1348         if (!ass) {
1349                 mono_image_close (image);
1350                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1351                 return NULL; 
1352         }
1353
1354         refass = mono_assembly_get_object (domain, ass);
1355         MONO_OBJECT_SETREF (refass, evidence, evidence);
1356         return refass;
1357 }
1358
1359 MonoReflectionAssembly *
1360 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
1361 {
1362         MonoDomain *domain = ad->data; 
1363         MonoImageOpenStatus status = MONO_IMAGE_OK;
1364         MonoAssembly *ass;
1365         MonoAssemblyName aname;
1366         MonoReflectionAssembly *refass = NULL;
1367         gchar *name;
1368         gboolean parsed;
1369
1370         MONO_ARCH_SAVE_REGS;
1371
1372         g_assert (assRef != NULL);
1373
1374         name = mono_string_to_utf8 (assRef);
1375         parsed = mono_assembly_name_parse (name, &aname);
1376         g_free (name);
1377
1378         if (!parsed) {
1379                 MonoException *exc;
1380
1381                 /* This is a parse error... */
1382                 exc = mono_get_exception_file_not_found2 (NULL, assRef);
1383                 mono_raise_exception (exc);
1384         }
1385
1386         ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly);
1387         mono_assembly_name_free (&aname);
1388
1389         if (!ass && (refass = mono_try_assembly_resolve (domain, assRef, refOnly)) == NULL){
1390                 /* FIXME: it doesn't make much sense since we really don't have a filename ... */
1391                 MonoException *exc = mono_get_exception_file_not_found2 (NULL, assRef);
1392                 mono_raise_exception (exc);
1393         }
1394
1395         if (refass == NULL)
1396                 refass = mono_assembly_get_object (domain, ass);
1397
1398         MONO_OBJECT_SETREF (refass, evidence, evidence);
1399         return refass;
1400 }
1401
1402 void
1403 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
1404 {
1405         MonoDomain * domain = mono_domain_get_by_id (domain_id);
1406
1407         MONO_ARCH_SAVE_REGS;
1408
1409         if (NULL == domain) {
1410                 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
1411                 mono_raise_exception (exc);
1412         }
1413         
1414         if (domain == mono_get_root_domain ()) {
1415                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
1416                 return;
1417         }
1418
1419         /* 
1420          * Unloading seems to cause problems when running NUnit/NAnt, hence
1421          * this workaround.
1422          */
1423         if (g_getenv ("MONO_NO_UNLOAD"))
1424                 return;
1425
1426         mono_domain_unload (domain);
1427 }
1428
1429 gboolean
1430 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
1431 {
1432         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1433
1434         if (!domain)
1435                 return TRUE;
1436
1437         return mono_domain_is_unloading (domain);
1438 }
1439
1440 gint32
1441 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file, 
1442                                             MonoObject *evidence, MonoArray *args)
1443 {
1444         MonoAssembly *assembly;
1445         MonoImage *image;
1446         MonoMethod *method;
1447         char *filename;
1448         gint32 res;
1449         MonoReflectionAssembly *refass;
1450
1451         MONO_ARCH_SAVE_REGS;
1452
1453         filename = mono_string_to_utf8 (file);
1454         assembly = mono_assembly_open (filename, NULL);
1455         g_free (filename);
1456
1457         if (!assembly)
1458                 mono_raise_exception (mono_get_exception_file_not_found2 (NULL, file));
1459
1460         image = assembly->image;
1461
1462         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1463
1464         if (!method)
1465                 g_error ("No entry point method found in %s", image->name);
1466
1467         if (!args)
1468                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1469
1470         refass = mono_assembly_get_object (ad->data, assembly);
1471         MONO_OBJECT_SETREF (refass, evidence, evidence);
1472
1473         res = mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1474
1475         return res;
1476 }
1477
1478 gint32 
1479 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1480 {
1481         MONO_ARCH_SAVE_REGS;
1482
1483         return ad->data->domain_id;
1484 }
1485
1486 MonoAppDomain * 
1487 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1488 {
1489         MonoDomain *old_domain = mono_domain_get();
1490
1491         MONO_ARCH_SAVE_REGS;
1492
1493         if (!mono_domain_set (ad->data, FALSE))
1494                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1495
1496         return old_domain->domain;
1497 }
1498
1499 MonoAppDomain * 
1500 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1501 {
1502         MonoDomain *current_domain = mono_domain_get ();
1503         MonoDomain *domain = mono_domain_get_by_id (domainid);
1504
1505         MONO_ARCH_SAVE_REGS;
1506
1507         if (!domain || !mono_domain_set (domain, FALSE))        
1508                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1509
1510         return current_domain->domain;
1511 }
1512
1513 void
1514 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1515 {
1516         MONO_ARCH_SAVE_REGS;
1517
1518         mono_thread_push_appdomain_ref (ad->data);
1519 }
1520
1521 void
1522 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1523 {
1524         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1525
1526         MONO_ARCH_SAVE_REGS;
1527
1528         if (!domain)
1529                 /* 
1530                  * Raise an exception to prevent the managed code from executing a pop
1531                  * later.
1532                  */
1533                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1534
1535         mono_thread_push_appdomain_ref (domain);
1536 }
1537
1538 void
1539 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1540 {
1541         MONO_ARCH_SAVE_REGS;
1542
1543         mono_thread_pop_appdomain_ref ();
1544 }
1545
1546 MonoAppContext * 
1547 ves_icall_System_AppDomain_InternalGetContext ()
1548 {
1549         MONO_ARCH_SAVE_REGS;
1550
1551         return mono_context_get ();
1552 }
1553
1554 MonoAppContext * 
1555 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1556 {
1557         MONO_ARCH_SAVE_REGS;
1558
1559         return mono_domain_get ()->default_context;
1560 }
1561
1562 MonoAppContext * 
1563 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1564 {
1565         MonoAppContext *old_context = mono_context_get ();
1566
1567         MONO_ARCH_SAVE_REGS;
1568
1569         mono_context_set (mc);
1570
1571         return old_context;
1572 }
1573
1574 MonoString *
1575 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1576 {
1577         MonoDomain* mono_root_domain = mono_get_root_domain ();
1578         mono_domain_lock (mono_root_domain);
1579         if (process_guid_set) {
1580                 mono_domain_unlock (mono_root_domain);
1581                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1582         }
1583         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1584         process_guid_set = TRUE;
1585         mono_domain_unlock (mono_root_domain);
1586         return newguid;
1587 }
1588
1589 gboolean
1590 mono_domain_is_unloading (MonoDomain *domain)
1591 {
1592         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1593                 return TRUE;
1594         else
1595                 return FALSE;
1596 }
1597
1598 static void
1599 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1600 {
1601         MonoClass *klass = (MonoClass*)key;
1602         MonoDomain *domain = (MonoDomain*)user_data;
1603         MonoClassRuntimeInfo *runtime_info;
1604
1605         runtime_info = klass->runtime_info;
1606         if (runtime_info && runtime_info->max_domain >= domain->domain_id)
1607                 runtime_info->domain_vtables [domain->domain_id] = NULL;
1608 }
1609
1610 typedef struct unload_data {
1611         MonoDomain *domain;
1612         char *failure_reason;
1613 } unload_data;
1614
1615 static guint32 WINAPI
1616 unload_thread_main (void *arg)
1617 {
1618         unload_data *data = (unload_data*)arg;
1619         MonoDomain *domain = data->domain;
1620
1621         /* 
1622          * FIXME: Abort our parent thread last, so we can return a failure 
1623          * indication if aborting times out.
1624          */
1625         if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
1626                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1627                 return 1;
1628         }
1629
1630         /* Finalize all finalizable objects in the doomed appdomain */
1631         if (!mono_domain_finalize (domain, 10000)) {
1632                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1633                 return 1;
1634         }
1635
1636         /* Clear references to our vtables in class->runtime_info.
1637          * We also hold the loader lock because we're going to change
1638          * class->runtime_info.
1639          */
1640         mono_domain_lock (domain);
1641         mono_loader_lock ();
1642         g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, domain);
1643         mono_loader_unlock ();
1644         mono_domain_unlock (domain);
1645
1646         mono_threads_clear_cached_culture (domain);
1647
1648         domain->state = MONO_APPDOMAIN_UNLOADED;
1649
1650         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1651
1652         /* remove from the handle table the items related to this domain */
1653         mono_gchandle_free_domain (domain);
1654
1655         mono_domain_free (domain, FALSE);
1656
1657         mono_gc_collect (mono_gc_max_generation ());
1658
1659         return 0;
1660 }
1661
1662 /*
1663  * mono_domain_unload:
1664  * @domain: The domain to unload
1665  *
1666  *  Unloads an appdomain. Follows the process outlined in:
1667  *  http://blogs.gotdotnet.com/cbrumme
1668  *
1669  *  If doing things the 'right' way is too hard or complex, we do it the 
1670  *  'simple' way, which means do everything needed to avoid crashes and
1671  *  memory leaks, but not much else.
1672  */
1673 static void
1674 mono_domain_unload (MonoDomain *domain)
1675 {
1676         HANDLE thread_handle;
1677         gsize tid;
1678         guint32 res;
1679         MonoAppDomainState prev_state;
1680         MonoMethod *method;
1681         MonoObject *exc;
1682         unload_data thread_data;
1683         MonoDomain *caller_domain = mono_domain_get ();
1684
1685         /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
1686
1687         /* Atomically change our state to UNLOADING */
1688         prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
1689                                                                                          MONO_APPDOMAIN_UNLOADING,
1690                                                                                          MONO_APPDOMAIN_CREATED);
1691         if (prev_state != MONO_APPDOMAIN_CREATED) {
1692                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1693                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1694                 else
1695                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1696                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1697                 else
1698                         g_assert_not_reached ();
1699         }
1700
1701         mono_domain_set (domain, FALSE);
1702         /* Notify OnDomainUnload listeners */
1703         method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); 
1704         g_assert (method);
1705
1706         exc = NULL;
1707         mono_runtime_invoke (method, domain->domain, NULL, &exc);
1708         if (exc) {
1709                 /* Roll back the state change */
1710                 domain->state = MONO_APPDOMAIN_CREATED;
1711                 mono_domain_set (caller_domain, FALSE);
1712                 mono_raise_exception ((MonoException*)exc);
1713         }
1714
1715         thread_data.domain = domain;
1716         thread_data.failure_reason = NULL;
1717
1718         /* 
1719          * First we create a separate thread for unloading, since
1720          * we might have to abort some threads, including the current one.
1721          */
1722         /*
1723          * If we create a non-suspended thread, the runtime will hang.
1724          * See:
1725          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1726          */ 
1727 #if 0
1728         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1729 #else
1730         thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1731         if (thread_handle == NULL) {
1732                 return;
1733         }
1734         ResumeThread (thread_handle);
1735 #endif
1736
1737         /* Wait for the thread */       
1738         while ((res = WaitForSingleObjectEx (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION)) {
1739                 if (mono_thread_has_appdomain_ref (mono_thread_current (), domain) && (mono_thread_interruption_requested ()))
1740                         /* The unload thread tries to abort us */
1741                         /* The icall wrapper will execute the abort */
1742                         CloseHandle (thread_handle);
1743                         return;
1744         }
1745         CloseHandle (thread_handle);
1746
1747         mono_domain_set (caller_domain, FALSE);
1748
1749         if (thread_data.failure_reason) {
1750                 MonoException *ex;
1751
1752                 /* Roll back the state change */
1753                 domain->state = MONO_APPDOMAIN_CREATED;
1754
1755                 g_warning (thread_data.failure_reason);
1756
1757                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1758
1759                 g_free (thread_data.failure_reason);
1760                 thread_data.failure_reason = NULL;
1761
1762                 mono_raise_exception (ex);
1763         }
1764 }