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