2004-10-03 Zoltan Varga <vargaz@freemail.hu>
[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
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include <mono/os/gc_wrapper.h>
17
18 #include <mono/metadata/object.h>
19 #include <mono/metadata/domain-internals.h>
20 #include "mono/metadata/metadata-internals.h"
21 #include <mono/metadata/assembly.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/threads.h>
24 #include <mono/metadata/socket-io.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/metadata/gc-internal.h>
27 #include <mono/metadata/marshal.h>
28 #include <mono/utils/mono-uri.h>
29
30 #define MONO_CORLIB_VERSION 27
31
32 CRITICAL_SECTION mono_delegate_section;
33
34 static gunichar2 process_guid [36];
35 static gboolean process_guid_set = FALSE;
36
37 static gboolean shutting_down = FALSE;
38
39 static MonoAssembly *
40 mono_domain_assembly_preload (MonoAssemblyName *aname,
41                               gchar **assemblies_path,
42                               gpointer user_data);
43
44 static void
45 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
46
47 static void
48 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass);
49
50 static MonoMethod *
51 look_for_method_by_name (MonoClass *klass, const gchar *name);
52
53 static void
54 mono_domain_unload (MonoDomain *domain);
55
56 /*
57  * mono_runtime_init:
58  * @domain: domain returned by mono_init ()
59  *
60  * Initialize the core AppDomain: this function will run also some
61  * IL initialization code, so it needs the execution engine to be fully 
62  * operational.
63  *
64  * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
65  * we know the entry_assembly.
66  *
67  */
68 void
69 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
70                    MonoThreadAttachCB attach_cb)
71 {
72         MonoAppDomainSetup *setup;
73         MonoAppDomain *ad;
74         MonoClass *class;
75         MonoString *arg;
76         
77         MONO_GC_PRE_INIT ();
78         mono_marshal_init ();
79
80         mono_install_assembly_preload_hook (mono_domain_assembly_preload, NULL);
81         mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
82         mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
83
84         mono_thread_init (start_cb, attach_cb);
85
86         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
87         setup = (MonoAppDomainSetup *) mono_object_new (domain, class);
88
89         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
90         ad = (MonoAppDomain *) mono_object_new (domain, class);
91         ad->data = domain;
92         domain->domain = ad;
93         domain->setup = setup;
94
95         InitializeCriticalSection (&mono_delegate_section);
96
97         mono_context_init (domain);
98         mono_context_set (domain->default_context);
99
100         mono_type_initialization_init ();
101
102         
103         /*
104          * Create an instance early since we can't do it when there is no memory.
105          */
106         arg = mono_string_new (domain, "Out of memory");
107         domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
108         
109         /* 
110          * These two are needed because the signal handlers might be executing on
111          * an alternate stack, and Boehm GC can't handle that.
112          */
113         arg = mono_string_new (domain, "A null value was found where an object instance was required");
114         domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
115         arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
116         domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
117         
118         /* GC init has to happen after thread init */
119         mono_gc_init ();
120
121         mono_network_init ();
122
123         /* mscorlib is loaded before we install the load hook */
124         mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
125
126         return;
127 }
128
129 static int
130 mono_get_corlib_version (void)
131 {
132         MonoClass *klass;
133         MonoClassField *field;
134         MonoObject *value;
135
136         klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
137         mono_class_init (klass);
138         field = mono_class_get_field_from_name (klass, "mono_corlib_version");
139         if (!field)
140                 return -1;
141         if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
142                 return -1;
143         value = mono_field_get_value_object (mono_domain_get (), field, NULL);
144         return *(gint32*)((gchar*)value + sizeof (MonoObject));
145 }
146
147 const char*
148 mono_check_corlib_version (void)
149 {
150         int version = mono_get_corlib_version ();
151         if (version != MONO_CORLIB_VERSION)
152                 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
153         else
154                 return NULL;
155 }
156
157 void
158 mono_context_init (MonoDomain *domain)
159 {
160         MonoClass *class;
161         MonoAppContext *context;
162
163         class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
164         context = (MonoAppContext *) mono_object_new (domain, class);
165         context->domain_id = domain->domain_id;
166         context->context_id = 0;
167         domain->default_context = context;
168 }
169
170 /* This must not be called while there are still running threads executing
171  * managed code.
172  */
173 void
174 mono_runtime_cleanup (MonoDomain *domain)
175 {
176         shutting_down = TRUE;
177
178         /* This ends up calling any pending pending (for at most 2 seconds) */
179         mono_gc_cleanup ();
180
181         mono_network_cleanup ();
182 }
183
184 static MonoDomainFunc quit_function = NULL;
185
186 void
187 mono_runtime_install_cleanup (MonoDomainFunc func)
188 {
189         quit_function = func;
190 }
191
192 void
193 mono_runtime_quit ()
194 {
195         if (quit_function != NULL)
196                 quit_function (mono_get_root_domain (), NULL);
197 }
198
199 gboolean
200 mono_runtime_is_shutting_down (void)
201 {
202         return shutting_down;
203 }
204
205 gboolean
206 mono_domain_has_type_resolve (MonoDomain *domain)
207 {
208         static MonoClassField *field = NULL;
209         MonoObject *o;
210
211         if (field == NULL) {
212                 MonoClass *klass = mono_defaults.appdomain_class;
213                 int i;
214
215                 for (i = 0; i < klass->field.count; ++i)
216                         if (strcmp (klass->fields [i].name, "TypeResolve") == 0)
217                                 field = &klass->fields [i];
218                 g_assert (field);
219         }
220
221         mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
222         return o != NULL;
223 }
224
225 MonoReflectionAssembly *
226 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
227 {
228         MonoClass *klass;
229         void *params [1];
230         static MonoMethod *method = NULL;
231
232         g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
233
234         if (method == NULL) {
235                 klass = domain->domain->mbr.obj.vtable->klass;
236                 g_assert (klass);
237
238                 method = look_for_method_by_name (klass, "DoTypeResolve");
239                 if (method == NULL) {
240                         g_warning ("Method AppDomain.DoTypeResolve not found.\n");
241                         return NULL;
242                 }
243         }
244
245         if (name)
246                 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
247         else
248                 *params = tb;
249         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
250 }
251
252 /*
253  * mono_domain_set:
254  *
255  *   Set the current appdomain to @domain. If @force is set, set it even
256  * if it is being unloaded.
257  * Returns:
258  *   - TRUE on success
259  *   - FALSE if the domain is unloaded
260  */
261 gboolean
262 mono_domain_set (MonoDomain *domain, gboolean force)
263 {
264         if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
265                 return FALSE;
266
267         mono_domain_set_internal (domain);
268
269         return TRUE;
270 }
271
272 MonoObject *
273 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
274 {
275         MonoDomain *add = ad->data;
276         MonoObject *o;
277         char *str;
278
279         MONO_ARCH_SAVE_REGS;
280
281         g_assert (ad != NULL);
282         g_assert (name != NULL);
283
284         str = mono_string_to_utf8 (name);
285
286         mono_domain_lock (add);
287
288         if (!strcmp (str, "APPBASE"))
289                 o = (MonoObject *)add->setup->application_base;
290         else if (!strcmp (str, "APP_CONFIG_FILE"))
291                 o = (MonoObject *)add->setup->configuration_file;
292         else if (!strcmp (str, "DYNAMIC_BASE"))
293                 o = (MonoObject *)add->setup->dynamic_base;
294         else if (!strcmp (str, "APP_NAME"))
295                 o = (MonoObject *)add->setup->application_name;
296         else if (!strcmp (str, "CACHE_BASE"))
297                 o = (MonoObject *)add->setup->cache_path;
298         else if (!strcmp (str, "PRIVATE_BINPATH"))
299                 o = (MonoObject *)add->setup->private_bin_path;
300         else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
301                 o = (MonoObject *)add->setup->private_bin_path_probe;
302         else if (!strcmp (str, "SHADOW_COPY_DIRS"))
303                 o = (MonoObject *)add->setup->shadow_copy_directories;
304         else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
305                 o = (MonoObject *)add->setup->shadow_copy_files;
306         else 
307                 o = mono_g_hash_table_lookup (add->env, name);
308
309         mono_domain_unlock (add);
310         g_free (str);
311
312         if (!o)
313                 return NULL;
314
315         return o;
316 }
317
318 void
319 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
320 {
321         MonoDomain *add = ad->data;
322
323         MONO_ARCH_SAVE_REGS;
324
325         g_assert (ad != NULL);
326         g_assert (name != NULL);
327
328         mono_domain_lock (add);
329
330         mono_g_hash_table_insert (add->env, name, data);
331
332         mono_domain_unlock (add);
333 }
334
335 MonoAppDomainSetup *
336 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
337 {
338         MONO_ARCH_SAVE_REGS;
339
340         g_assert (ad != NULL);
341         g_assert (ad->data != NULL);
342
343         return ad->data->setup;
344 }
345
346 MonoString *
347 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
348 {
349         MONO_ARCH_SAVE_REGS;
350
351         g_assert (ad != NULL);
352         g_assert (ad->data != NULL);
353
354         return mono_string_new (ad->data, ad->data->friendly_name);
355 }
356
357 MonoAppDomain *
358 ves_icall_System_AppDomain_getCurDomain ()
359 {
360         MonoDomain *add = mono_domain_get ();
361
362         MONO_ARCH_SAVE_REGS;
363
364         return add->domain;
365 }
366
367 MonoAppDomain *
368 ves_icall_System_AppDomain_getDomainByID (gint32 domain_id)
369 {
370         MonoDomain *add = mono_domain_get_by_id (domain_id);
371
372         MONO_ARCH_SAVE_REGS;
373
374         return ((add == NULL) ? NULL : add->domain);
375 }
376
377 static void
378 add_assembly_to_domain (gpointer key, gpointer value, gpointer user_data)
379 {
380         add_assemblies_to_domain ((MonoDomain*)user_data, (MonoAssembly*)value);
381 }
382
383 MonoAppDomain *
384 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
385 {
386         MonoDomain *domain = mono_domain_get ();
387         MonoClass *adclass;
388         MonoAppDomain *ad;
389         MonoDomain *data;
390         
391         MONO_ARCH_SAVE_REGS;
392
393         adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
394
395         /* FIXME: pin all those objects */
396         data = mono_domain_create();
397
398         ad = (MonoAppDomain *) mono_object_new (data, adclass);
399         ad->data = data;
400         data->domain = ad;
401         data->setup = setup;
402         data->friendly_name = mono_string_to_utf8 (friendly_name);
403         data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException");
404
405         mono_context_init (data);
406
407         /* The new appdomain should have all assemblies loaded */
408         mono_domain_lock (domain);
409         g_hash_table_foreach (domain->assemblies, add_assembly_to_domain, data);
410         mono_domain_unlock (domain);
411
412         return ad;
413 }
414
415 typedef struct {
416         MonoArray *res;
417         MonoDomain *domain;
418         int idx;
419 } add_assembly_helper_t;
420
421 static void
422 add_assembly (gpointer key, gpointer value, gpointer user_data)
423 {
424         add_assembly_helper_t *ah = (add_assembly_helper_t *) user_data;
425
426         mono_array_set (ah->res, gpointer, ah->idx++, mono_assembly_get_object (ah->domain, value));
427 }
428
429 MonoArray *
430 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad)
431 {
432         MonoDomain *domain = ad->data; 
433         static MonoClass *System_Reflection_Assembly;
434         MonoArray *res;
435         add_assembly_helper_t ah;
436         
437         MONO_ARCH_SAVE_REGS;
438
439         if (!System_Reflection_Assembly)
440                 System_Reflection_Assembly = mono_class_from_name (
441                         mono_defaults.corlib, "System.Reflection", "Assembly");
442
443         res = mono_array_new (domain, System_Reflection_Assembly, g_hash_table_size (domain->assemblies));
444
445         ah.domain = domain;
446         ah.res = res;
447         ah.idx = 0;
448         mono_domain_lock (domain);
449         g_hash_table_foreach (domain->assemblies, add_assembly, &ah);
450         mono_domain_unlock (domain);
451
452         return res;
453 }
454
455 /*
456  * Used to find methods in AppDomain class.
457  * It only works if there are no multiple signatures for any given method name
458  */
459 static MonoMethod *
460 look_for_method_by_name (MonoClass *klass, const gchar *name)
461 {
462         gint i;
463         MonoMethod *method;
464
465         for (i = 0; i < klass->method.count; i++) {
466                 method = klass->methods [i];
467                 if (!strcmp (method->name, name))
468                         return method;
469         }
470
471         return NULL;
472 }
473
474 static MonoReflectionAssembly *
475 try_assembly_resolve (MonoDomain *domain, MonoString *fname)
476 {
477         MonoClass *klass;
478         MonoMethod *method;
479         void *params [1];
480
481         g_assert (domain != NULL && fname != NULL);
482
483         klass = domain->domain->mbr.obj.vtable->klass;
484         g_assert (klass);
485         
486         method = look_for_method_by_name (klass, "DoAssemblyResolve");
487         if (method == NULL) {
488                 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
489                 return NULL;
490         }
491
492         *params = fname;
493         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
494 }
495
496 static void
497 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass)
498 {
499         gint i;
500
501         if (!ass->aname.name)
502                 return;
503
504         mono_domain_lock (domain);
505
506         if (g_hash_table_lookup (domain->assemblies, ass->aname.name)) {
507                 mono_domain_unlock (domain);
508                 return; /* This is ok while no lazy loading of assemblies */
509         }
510
511         g_hash_table_insert (domain->assemblies, (gpointer) ass->aname.name, ass);
512         mono_domain_unlock (domain);
513
514         if (ass->image->references)
515                 for (i = 0; ass->image->references [i] != NULL; i++)
516                         add_assemblies_to_domain (domain, ass->image->references [i]);
517 }
518
519 static void
520 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
521 {
522         MonoDomain *domain = mono_domain_get ();
523         MonoReflectionAssembly *ref_assembly;
524         MonoClass *klass;
525         MonoMethod *method;
526         void *params [1];
527
528         klass = domain->domain->mbr.obj.vtable->klass;
529
530         method = look_for_method_by_name (klass, "DoAssemblyLoad");
531         if (method == NULL) {
532                 g_warning ("Method AppDomain.DoAssemblyLoad not found.\n");
533                 return;
534         }
535
536         add_assemblies_to_domain (domain, assembly);
537
538         ref_assembly = mono_assembly_get_object (domain, assembly);
539         g_assert (ref_assembly);
540
541         *params = ref_assembly;
542         mono_runtime_invoke (method, domain->domain, params, NULL);
543 }
544
545 static gchar *
546 reduce_path (const gchar *dirname)
547 {
548         gchar **parts;
549         gchar *part;
550         GList *list, *tmp;
551         GString *result;
552         gchar *res;
553         gint i;
554
555         parts = g_strsplit (dirname, G_DIR_SEPARATOR_S, 0);
556         list = NULL;
557         for (i = 0; (part = parts [i]) != NULL; i++) {
558                 if (!strcmp (part, "."))
559                         continue;
560
561                 if (!strcmp (part, "..")) {
562                         if (list && list->next) /* Don't remove root */
563                                 list = g_list_delete_link (list, list);
564                 } else {
565                         list = g_list_prepend (list, part);
566                 }
567         }
568
569         result = g_string_new ("");
570         list = g_list_reverse (list);
571
572         for (tmp = list; tmp; tmp = tmp->next) {
573                 gchar *data = (gchar *) tmp->data;
574
575                 if (data && *data) {
576 #ifdef PLATFORM_WIN32
577                         if (result->len == 0)
578                                 g_string_append_printf (result, "%s\\", data);
579                         else if (result->str [result->len - 1] == '\\')
580                                 g_string_append_printf (result, "%s", data);
581                         else
582 #endif
583                                 g_string_append_printf (result, "%c%s",
584                                                         G_DIR_SEPARATOR, data);
585                 }
586         }
587         
588         res = result->str;
589         g_string_free (result, FALSE);
590         g_list_free (list);
591         g_strfreev (parts);
592         return res;
593 }
594
595 static void
596 set_domain_search_path (MonoDomain *domain)
597 {
598         MonoAppDomainSetup *setup;
599         gchar **tmp;
600         gchar *utf8;
601         gint i;
602         gint npaths = 0;
603         gchar **pvt_split = NULL;
604         GError *error = NULL;
605         gint appbaselen = -1;
606
607         if ((domain->search_path != NULL) && !domain->setup->path_changed)
608                 return;
609
610         setup = domain->setup;
611         if (!setup->application_base)
612                 return; /* Must set application base to get private path working */
613
614         npaths++;
615         if (setup->private_bin_path) {
616                 utf8 = mono_string_to_utf8 (setup->private_bin_path);
617                 pvt_split = g_strsplit (utf8, G_SEARCHPATH_SEPARATOR_S, 1000);
618                 g_free (utf8);
619                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
620         }
621
622         if (!npaths) {
623                 if (pvt_split)
624                         g_strfreev (pvt_split);
625                 /*
626                  * Don't do this because the first time is called, the domain
627                  * setup is not finished.
628                  *
629                  * domain->search_path = g_malloc (sizeof (char *));
630                  * domain->search_path [0] = NULL;
631                 */
632                 return;
633         }
634
635         if (domain->search_path)
636                 g_strfreev (domain->search_path);
637
638         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
639         tmp [npaths] = NULL;
640
641         *tmp = mono_string_to_utf8 (setup->application_base);
642
643         /* FIXME: is this needed? */
644         if (strncmp (*tmp, "file://", 7) == 0) {
645                 gchar *file = *tmp;
646                 gchar *uri = *tmp;
647                 gchar *tmpuri;
648
649                 if (uri [7] != '/')
650                         uri = g_strdup_printf ("file:///%s", uri + 7);
651
652                 tmpuri = uri;
653                 uri = mono_escape_uri_string (tmpuri);
654                 *tmp = g_filename_from_uri (uri, NULL, &error);
655                 g_free (uri);
656
657                 if (tmpuri != file)
658                         g_free (tmpuri);
659
660                 if (error != NULL) {
661                         g_warning ("%s\n", error->message);
662                         g_error_free (error);
663                         *tmp = file;
664                 } else {
665                         g_free (file);
666                 }
667         }
668
669         for (i = 1; pvt_split && i < npaths; i++) {
670                 if (g_path_is_absolute (pvt_split [i - 1])) {
671                         tmp [i] = g_strdup (pvt_split [i - 1]);
672                 } else {
673                         tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
674                 }
675
676                 if (strchr (tmp [i], '.')) {
677                         gchar *reduced;
678                         gchar *freeme;
679
680                         reduced = reduce_path (tmp [i]);
681                         if (appbaselen == -1)
682                                 appbaselen = strlen (tmp [0]);
683
684                         if (strncmp (tmp [0], reduced, appbaselen)) {
685                                 g_free (reduced);
686                                 g_free (tmp [i]);
687                                 tmp [i] = g_strdup ("");
688                                 continue;
689                         }
690
691                         freeme = tmp [i];
692                         tmp [i] = reduced;
693                         g_free (freeme);
694                 }
695         }
696         
697         if (setup->private_bin_path_probe != NULL) {
698                 g_free (tmp [0]);
699                 tmp [0] = g_strdup ("");
700         }
701                 
702         domain->setup->path_changed = FALSE;
703
704         g_strfreev (pvt_split);
705 }
706
707 static gboolean
708 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
709                                         const gchar *path3, const gchar *path4)
710 {
711         gchar *fullpath;
712
713         *assembly = NULL;
714         fullpath = g_build_filename (path1, path2, path3, path4, NULL);
715         if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
716                 *assembly = mono_assembly_open (fullpath, NULL);
717
718         g_free (fullpath);
719         return (*assembly != NULL);
720 }
721
722 static MonoAssembly *
723 real_load (gchar **search_path, const gchar *culture, const gchar *name)
724 {
725         MonoAssembly *result = NULL;
726         gchar **path;
727         gchar *filename;
728         const gchar *local_culture;
729         gint len;
730
731         if (!culture || *culture == '\0') {
732                 local_culture = "";
733         } else {
734                 local_culture = culture;
735         }
736
737         filename =  g_strconcat (name, ".dll", NULL);
738         len = strlen (filename);
739
740         for (path = search_path; *path; path++) {
741                 if (**path == '\0')
742                         continue; /* Ignore empty ApplicationBase */
743
744                 /* See test cases in bug #58992 and bug #57710 */
745                 /* 1st try: [culture]/[name].dll (culture may be empty) */
746                 strcpy (filename + len - 4, ".dll");
747                 if (try_load_from (&result, *path, local_culture, "", filename))
748                         break;
749
750                 /* 2nd try: [culture]/[name].exe (culture may be empty) */
751                 strcpy (filename + len - 4, ".exe");
752                 if (try_load_from (&result, *path, local_culture, "", filename))
753                         break;
754
755                 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
756                 strcpy (filename + len - 4, ".dll");
757                 if (try_load_from (&result, *path, local_culture, name, filename))
758                         break;
759
760                 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
761                 strcpy (filename + len - 4, ".exe");
762                 if (try_load_from (&result, *path, local_culture, name, filename))
763                         break;
764         }
765
766         g_free (filename);
767         return result;
768 }
769
770 /*
771  * Try loading the assembly from ApplicationBase and PrivateBinPath 
772  * and then from assemblies_path if any.
773  */
774 static MonoAssembly *
775 mono_domain_assembly_preload (MonoAssemblyName *aname,
776                               gchar **assemblies_path,
777                               gpointer user_data)
778 {
779         MonoDomain *domain = mono_domain_get ();
780         MonoAssembly *result = NULL;
781
782         set_domain_search_path (domain);
783
784         if (domain->search_path && domain->search_path [0] != NULL) {
785                 result = real_load (domain->search_path, aname->culture, aname->name);
786         }
787
788         if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
789                 result = real_load (assemblies_path, aname->culture, aname->name);
790         }
791
792         return NULL;
793 }
794
795 MonoReflectionAssembly *
796 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname)
797 {
798         MonoDomain *domain = mono_domain_get ();
799         char *name, *filename;
800         MonoImageOpenStatus status = MONO_IMAGE_OK;
801         MonoAssembly *ass;
802
803         MONO_ARCH_SAVE_REGS;
804
805         if (fname == NULL) {
806                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
807                 mono_raise_exception (exc);
808         }
809                 
810         name = filename = mono_string_to_utf8 (fname);
811
812         ass = mono_assembly_open (filename, &status);
813         
814         g_free (name);
815
816         if (!ass){
817                 MonoException *exc = mono_get_exception_file_not_found (fname);
818                 mono_raise_exception (exc);
819         }
820
821         return mono_assembly_get_object (domain, ass);
822 }
823
824 static void
825 free_assembly_name (MonoAssemblyName *aname)
826 {
827         if (aname == NULL)
828                 return;
829
830         g_free ((void *) aname->name);
831         g_free ((void *) aname->culture);
832         g_free ((void *) aname->hash_value);
833 }
834
835 static gboolean
836 get_info_from_assembly_name (MonoString *assRef, MonoAssemblyName *aname)
837 {
838         gchar *name;
839         gchar *value;
840         gchar **parts;
841         gchar **tmp;
842         gint major, minor, build, revision;
843
844         memset (aname, 0, sizeof (MonoAssemblyName));
845
846         name = mono_string_to_utf8 (assRef);
847         parts = tmp = g_strsplit (name, ",", 4);
848         g_free (name);
849         if (!tmp || !*tmp) {
850                 g_strfreev (tmp);
851                 return FALSE;
852         }
853
854         value = g_strstrip (*tmp);
855         /* g_print ("Assembly name: %s\n", value); */
856         aname->name = g_strdup (value);
857         tmp++;
858         if (!*tmp) {
859                 g_strfreev (parts);
860                 return TRUE;
861         }
862
863         while (*tmp) {
864                 value = g_strstrip (*tmp);
865                 if (!g_ascii_strncasecmp (value, "Version=", 8)) {
866                         if (sscanf (value + 8, "%u.%u.%u.%u",
867                                     &major, &minor, &build, &revision) != 4) {
868                                 g_strfreev (parts);
869                                 return FALSE;
870                         }
871                         /* g_print ("Version: %u.%u.%u.%u\n", major, minor, build, revision); */
872                         aname->major = major;
873                         aname->minor = minor;
874                         aname->build = build;
875                         aname->revision = revision;
876                         tmp++;
877                         continue;
878                 }
879
880                 if (!g_ascii_strncasecmp (value, "Culture=", 8)) {
881                         gchar *t = g_strdup (value + 8);
882                         g_strchug (t);
883                         aname->culture = g_strdup (g_strchomp (t));
884                         tmp++;
885                         g_free (t);
886                         if (g_strcasecmp (aname->culture, "neutral") == 0) {
887                                 g_free ((void *) aname->culture);
888                                 aname->culture = g_strdup ("");
889                         }
890                         continue;
891                 }
892
893                 if (!g_ascii_strncasecmp (value, "PublicKeyToken=", 15)) {
894                         tmp++;
895                         value += 15;
896                         if (*value && strncmp (value, "null", 4)) {
897                                 gchar *t = g_strdup (value);
898                                 g_strchug (t);
899                                 g_strlcpy (aname->public_key_token, g_strchomp (value), MONO_PUBLIC_KEY_TOKEN_LENGTH);
900                                 g_free (t);
901                         }
902                         continue;
903                 }
904
905                 g_strfreev (parts);
906                 return FALSE;
907         }
908
909         g_strfreev (parts);
910         return TRUE;
911 }
912
913 MonoReflectionAssembly *
914 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
915                                                                                         MonoArray *raw_assembly,
916                                                                                         MonoArray *raw_symbol_store, MonoObject *evidence)
917 {
918         MonoAssembly *ass;
919         MonoReflectionAssembly *refass = NULL;
920         MonoDomain *domain = ad->data;
921         MonoImageOpenStatus status;
922         guint32 raw_assembly_len = mono_array_length (raw_assembly);
923         MonoImage *image = mono_image_open_from_data (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL);
924
925         if (raw_symbol_store)
926                 mono_raise_exception (mono_get_exception_not_implemented ("LoadAssemblyRaw: Raw Symbol Store not Implemented"));
927   
928         if (!image) {
929                 mono_raise_exception (mono_get_exception_bad_image_format (""));
930                 return NULL;
931         }
932
933         ass = mono_assembly_load_from (image, "", &status);
934
935         if (!ass) {
936                 mono_image_close (image);
937                 mono_raise_exception (mono_get_exception_bad_image_format (""));
938                 return NULL; 
939         }
940
941         refass = mono_assembly_get_object (domain, ass);
942         refass->evidence = evidence;
943         return refass;
944 }
945
946 MonoReflectionAssembly *
947 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef, MonoObject *evidence)
948 {
949         MonoDomain *domain = ad->data; 
950         MonoImageOpenStatus status = MONO_IMAGE_OK;
951         MonoAssembly *ass;
952         MonoAssemblyName aname;
953         MonoReflectionAssembly *refass = NULL;
954
955         MONO_ARCH_SAVE_REGS;
956
957         g_assert (assRef != NULL);
958
959         if (!get_info_from_assembly_name (assRef, &aname)) {
960                 MonoException *exc;
961
962                 free_assembly_name (&aname);
963                 /* This is a parse error... */
964                 exc = mono_get_exception_file_not_found (assRef);
965                 mono_raise_exception (exc);
966         }
967
968         ass = mono_assembly_load (&aname, NULL, &status);
969         free_assembly_name (&aname);
970
971         if (!ass && (refass = try_assembly_resolve (domain, assRef)) == NULL){
972                 /* FIXME: it doesn't make much sense since we really don't have a filename ... */
973                 MonoException *exc = mono_get_exception_file_not_found (assRef);
974                 mono_raise_exception (exc);
975         }
976
977         if (refass == NULL)
978                 refass = mono_assembly_get_object (domain, ass);
979
980         refass->evidence = evidence;
981         return refass;
982 }
983
984 void
985 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
986 {
987         MonoDomain * domain = mono_domain_get_by_id (domain_id);
988
989         MONO_ARCH_SAVE_REGS;
990
991         if (NULL == domain) {
992                 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
993                 mono_raise_exception (exc);
994         }
995         
996         if (domain == mono_get_root_domain ()) {
997                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
998                 return;
999         }
1000
1001         /* 
1002          * Unloading seems to cause problems when running NUnit/NAnt, hence
1003          * this workaround.
1004          */
1005         if (g_getenv ("MONO_NO_UNLOAD"))
1006                 return;
1007
1008         mono_domain_unload (domain);
1009 }
1010
1011 gboolean
1012 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
1013 {
1014         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1015
1016         if (!domain)
1017                 return TRUE;
1018
1019         return mono_domain_is_unloading (domain);
1020 }
1021
1022 gint32
1023 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file, 
1024                                             MonoObject *evidence, MonoArray *args)
1025 {
1026         MonoAssembly *assembly;
1027         MonoImage *image;
1028         MonoMethod *method;
1029         char *filename;
1030         gint32 res;
1031         MonoReflectionAssembly *refass;
1032
1033         MONO_ARCH_SAVE_REGS;
1034
1035         filename = mono_string_to_utf8 (file);
1036         assembly = mono_assembly_open (filename, NULL);
1037         g_free (filename);
1038
1039         if (!assembly)
1040                 mono_raise_exception (mono_get_exception_file_not_found (filename));
1041
1042         image = assembly->image;
1043
1044         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1045
1046         if (!method)
1047                 g_error ("No entry point method found in %s", image->name);
1048
1049         if (!args)
1050                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1051
1052         refass = mono_assembly_get_object (ad->data, assembly);
1053         refass->evidence = evidence;
1054
1055         res = mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1056
1057         return res;
1058 }
1059
1060 gint32 
1061 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1062 {
1063         MONO_ARCH_SAVE_REGS;
1064
1065         return ad->data->domain_id;
1066 }
1067
1068 MonoAppDomain * 
1069 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1070 {
1071         MonoDomain *old_domain = mono_domain_get();
1072
1073         MONO_ARCH_SAVE_REGS;
1074
1075         if (!mono_domain_set (ad->data, FALSE))
1076                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1077
1078         return old_domain->domain;
1079 }
1080
1081 MonoAppDomain * 
1082 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1083 {
1084         MonoDomain *current_domain = mono_domain_get ();
1085         MonoDomain *domain = mono_domain_get_by_id (domainid);
1086
1087         MONO_ARCH_SAVE_REGS;
1088
1089         if (!domain || !mono_domain_set (domain, FALSE))        
1090                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1091
1092         return current_domain->domain;
1093 }
1094
1095 void
1096 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1097 {
1098         MONO_ARCH_SAVE_REGS;
1099
1100         mono_thread_push_appdomain_ref (ad->data);
1101 }
1102
1103 void
1104 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1105 {
1106         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1107
1108         MONO_ARCH_SAVE_REGS;
1109
1110         if (!domain)
1111                 /* 
1112                  * Raise an exception to prevent the managed code from executing a pop
1113                  * later.
1114                  */
1115                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1116
1117         mono_thread_push_appdomain_ref (domain);
1118 }
1119
1120 void
1121 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1122 {
1123         MONO_ARCH_SAVE_REGS;
1124
1125         mono_thread_pop_appdomain_ref ();
1126 }
1127
1128 MonoAppContext * 
1129 ves_icall_System_AppDomain_InternalGetContext ()
1130 {
1131         MONO_ARCH_SAVE_REGS;
1132
1133         return mono_context_get ();
1134 }
1135
1136 MonoAppContext * 
1137 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1138 {
1139         MONO_ARCH_SAVE_REGS;
1140
1141         return mono_domain_get ()->default_context;
1142 }
1143
1144 MonoAppContext * 
1145 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1146 {
1147         MonoAppContext *old_context = mono_context_get ();
1148
1149         MONO_ARCH_SAVE_REGS;
1150
1151         mono_context_set (mc);
1152
1153         return old_context;
1154 }
1155
1156 MonoString *
1157 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1158 {
1159         MonoDomain* mono_root_domain = mono_get_root_domain ();
1160         mono_domain_lock (mono_root_domain);
1161         if (process_guid_set) {
1162                 mono_domain_unlock (mono_root_domain);
1163                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1164         }
1165         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1166         process_guid_set = TRUE;
1167         mono_domain_unlock (mono_root_domain);
1168         return newguid;
1169 }
1170
1171 gboolean
1172 mono_domain_is_unloading (MonoDomain *domain)
1173 {
1174         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1175                 return TRUE;
1176         else
1177                 return FALSE;
1178 }
1179
1180 static void
1181 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1182 {
1183         MonoClass *klass = (MonoClass*)key;
1184         MonoDomain *domain = (MonoDomain*)user_data;
1185         MonoVTable *vt;
1186
1187         vt = klass->cached_vtable;
1188         if (vt && vt->domain == domain)
1189                 klass->cached_vtable = NULL;
1190 }
1191
1192 typedef struct unload_data {
1193         MonoDomain *domain;
1194         char *failure_reason;
1195 } unload_data;
1196
1197 static guint32
1198 unload_thread_main (void *arg)
1199 {
1200         unload_data *data = (unload_data*)arg;
1201         MonoDomain *domain = data->domain;
1202
1203         /* 
1204          * FIXME: Abort our parent thread last, so we can return a failure 
1205          * indication if aborting times out.
1206          */
1207         if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
1208                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1209                 return 1;
1210         }
1211
1212         /* Finalize all finalizable objects in the doomed appdomain */
1213         if (!mono_domain_finalize (domain, 10000)) {
1214                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1215                 return 1;
1216         }
1217
1218         /* Clear references to our vtables in class->cached_vtable */
1219         mono_domain_lock (domain);
1220         mono_g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable,
1221                                                            domain);
1222         mono_domain_unlock (domain);
1223
1224         domain->state = MONO_APPDOMAIN_UNLOADED;
1225
1226         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1227
1228         mono_domain_free (domain, FALSE);
1229
1230 #ifdef HAVE_BOEHM_GC
1231         GC_gcollect ();
1232 #endif
1233
1234         return 0;
1235 }
1236
1237 /*
1238  * mono_domain_unload:
1239  *
1240  *  Unloads an appdomain. Follows the process outlined in:
1241  *  http://blogs.gotdotnet.com/cbrumme
1242  *  If doing things the 'right' way is too hard or complex, we do it the 
1243  *  'simple' way, which means do everything needed to avoid crashes and
1244  *  memory leaks, but not much else.
1245  */
1246 static void
1247 mono_domain_unload (MonoDomain *domain)
1248 {
1249         HANDLE thread_handle;
1250         guint32 tid;
1251         MonoAppDomainState prev_state;
1252         MonoMethod *method;
1253         MonoObject *exc;
1254         unload_data thread_data;
1255         MonoDomain *caller_domain = mono_domain_get ();
1256
1257         /* printf ("UNLOAD STARTING FOR %s.\n", domain->friendly_name); */
1258
1259         /* Atomically change our state to UNLOADING */
1260         prev_state = InterlockedCompareExchange (&domain->state,
1261                                                                                          MONO_APPDOMAIN_UNLOADING,
1262                                                                                          MONO_APPDOMAIN_CREATED);
1263         if (prev_state != MONO_APPDOMAIN_CREATED) {
1264                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1265                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1266                 else
1267                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1268                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1269                 else
1270                         g_assert_not_reached ();
1271         }
1272
1273         mono_domain_set (domain, FALSE);
1274         /* Notify OnDomainUnload listeners */
1275         method = look_for_method_by_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload");     
1276         g_assert (method);
1277
1278         exc = NULL;
1279         mono_runtime_invoke (method, domain->domain, NULL, &exc);
1280         if (exc) {
1281                 /* Roll back the state change */
1282                 domain->state = MONO_APPDOMAIN_CREATED;
1283                 mono_domain_set (caller_domain, FALSE);
1284                 mono_raise_exception ((MonoException*)exc);
1285         }
1286
1287         thread_data.domain = domain;
1288         thread_data.failure_reason = NULL;
1289
1290         /* 
1291          * First we create a separate thread for unloading, since
1292          * we might have to abort some threads, including the current one.
1293          */
1294         /*
1295          * If we create a non-suspended thread, the runtime will hang.
1296          * See:
1297          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1298          */ 
1299 #if 0
1300         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1301 #else
1302 #if defined(PLATFORM_WIN32) && defined (HAVE_BOEHM_GC)
1303         thread_handle = GC_CreateThread (NULL, 0, unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1304 #else
1305         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1306 #endif
1307         ResumeThread (thread_handle);
1308 #endif
1309         
1310         while (WaitForSingleObjectEx (thread_handle, INFINITE, FALSE) == WAIT_IO_COMPLETION)
1311                 ; /* wait for the thread */
1312         
1313
1314         mono_domain_set (caller_domain, FALSE);
1315         if (thread_data.failure_reason) {
1316                 MonoException *ex;
1317
1318                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1319                 /* Roll back the state change */
1320                 domain->state = MONO_APPDOMAIN_CREATED;
1321
1322                 g_warning (thread_data.failure_reason);
1323
1324                 g_free (thread_data.failure_reason);
1325
1326                 mono_raise_exception (ex);
1327         }
1328 }