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