2004-09-23 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 26
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 being unloaded
260  */
261 gboolean
262 mono_domain_set (MonoDomain *domain, gboolean force)
263 {
264         if (!force && (domain->state == MONO_APPDOMAIN_UNLOADING || 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 ((MonoException *)mono_exception_from_name (
1041                         mono_defaults.corlib, "System.IO", "FileNotFoundException"));
1042         }
1043
1044         image = assembly->image;
1045
1046         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1047
1048         if (!method)
1049                 g_error ("No entry point method found in %s", image->name);
1050
1051         if (!args)
1052                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1053
1054         refass = mono_assembly_get_object (ad->data, assembly);
1055         refass->evidence = evidence;
1056
1057         res = mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1058
1059         return res;
1060 }
1061
1062 gint32 
1063 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1064 {
1065         MONO_ARCH_SAVE_REGS;
1066
1067         return ad->data->domain_id;
1068 }
1069
1070 MonoAppDomain * 
1071 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1072 {
1073         MonoDomain *old_domain = mono_domain_get();
1074
1075         MONO_ARCH_SAVE_REGS;
1076
1077         if (!mono_domain_set (ad->data, FALSE))
1078                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1079
1080         return old_domain->domain;
1081 }
1082
1083 MonoAppDomain * 
1084 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1085 {
1086         MonoDomain *current_domain = mono_domain_get ();
1087         MonoDomain *domain = mono_domain_get_by_id (domainid);
1088
1089         MONO_ARCH_SAVE_REGS;
1090
1091         if (!domain || !mono_domain_set (domain, FALSE))        
1092                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1093
1094         return current_domain->domain;
1095 }
1096
1097 void
1098 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1099 {
1100         MONO_ARCH_SAVE_REGS;
1101
1102         mono_thread_push_appdomain_ref (ad->data);
1103 }
1104
1105 void
1106 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1107 {
1108         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1109
1110         MONO_ARCH_SAVE_REGS;
1111
1112         if (!domain)
1113                 /* 
1114                  * Raise an exception to prevent the managed code from executing a pop
1115                  * later.
1116                  */
1117                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1118
1119         mono_thread_push_appdomain_ref (domain);
1120 }
1121
1122 void
1123 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1124 {
1125         MONO_ARCH_SAVE_REGS;
1126
1127         mono_thread_pop_appdomain_ref ();
1128 }
1129
1130 MonoAppContext * 
1131 ves_icall_System_AppDomain_InternalGetContext ()
1132 {
1133         MONO_ARCH_SAVE_REGS;
1134
1135         return mono_context_get ();
1136 }
1137
1138 MonoAppContext * 
1139 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1140 {
1141         MONO_ARCH_SAVE_REGS;
1142
1143         return mono_domain_get ()->default_context;
1144 }
1145
1146 MonoAppContext * 
1147 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1148 {
1149         MonoAppContext *old_context = mono_context_get ();
1150
1151         MONO_ARCH_SAVE_REGS;
1152
1153         mono_context_set (mc);
1154         
1155         return old_context;
1156 }
1157
1158 MonoString *
1159 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1160 {
1161         MonoDomain* mono_root_domain = mono_get_root_domain ();
1162         mono_domain_lock (mono_root_domain);
1163         if (process_guid_set) {
1164                 mono_domain_unlock (mono_root_domain);
1165                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1166         }
1167         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1168         process_guid_set = TRUE;
1169         mono_domain_unlock (mono_root_domain);
1170         return newguid;
1171 }
1172
1173 gboolean
1174 mono_domain_is_unloading (MonoDomain *domain)
1175 {
1176         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1177                 return TRUE;
1178         else
1179                 return FALSE;
1180 }
1181
1182 static void
1183 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1184 {
1185         MonoClass *klass = (MonoClass*)key;
1186         MonoDomain *domain = (MonoDomain*)user_data;
1187         MonoVTable *vt;
1188
1189         vt = klass->cached_vtable;
1190         if (vt && vt->domain == domain)
1191                 klass->cached_vtable = NULL;
1192 }
1193
1194 typedef struct unload_data {
1195         MonoDomain *domain;
1196         char *failure_reason;
1197 } unload_data;
1198
1199 static guint32
1200 unload_thread_main (void *arg)
1201 {
1202         unload_data *data = (unload_data*)arg;
1203         MonoDomain *domain = data->domain;
1204
1205         /* 
1206          * FIXME: Abort our parent thread last, so we can return a failure 
1207          * indication if aborting times out.
1208          */
1209         if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
1210                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1211                 return 1;
1212         }
1213
1214         /* Finalize all finalizable objects in the doomed appdomain */
1215         if (!mono_domain_finalize (domain, 10000)) {
1216                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1217                 return 1;
1218         }
1219
1220         /* Clear references to our vtables in class->cached_vtable */
1221         mono_domain_lock (domain);
1222         mono_g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable,
1223                                                            domain);
1224         mono_domain_unlock (domain);
1225
1226         domain->state = MONO_APPDOMAIN_UNLOADED;
1227
1228         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1229
1230         mono_domain_free (domain, FALSE);
1231
1232 #ifdef HAVE_BOEHM_GC
1233         GC_gcollect ();
1234 #endif
1235
1236         return 0;
1237 }
1238
1239 /*
1240  * mono_domain_unload:
1241  *
1242  *  Unloads an appdomain. Follows the process outlined in:
1243  *  http://blogs.gotdotnet.com/cbrumme
1244  *  If doing things the 'right' way is too hard or complex, we do it the 
1245  *  'simple' way, which means do everything needed to avoid crashes and
1246  *  memory leaks, but not much else.
1247  */
1248 static void
1249 mono_domain_unload (MonoDomain *domain)
1250 {
1251         HANDLE thread_handle;
1252         guint32 tid;
1253         MonoAppDomainState prev_state;
1254         MonoMethod *method;
1255         MonoObject *exc;
1256         unload_data thread_data;
1257
1258         /* printf ("UNLOAD STARTING FOR %s.\n", domain->friendly_name); */
1259
1260         /* Atomically change our state to UNLOADING */
1261         prev_state = InterlockedCompareExchange (&domain->state,
1262                                                                                          MONO_APPDOMAIN_UNLOADING,
1263                                                                                          MONO_APPDOMAIN_CREATED);
1264         if (prev_state != MONO_APPDOMAIN_CREATED) {
1265                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1266                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1267                 else
1268                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1269                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1270                 else
1271                         g_assert_not_reached ();
1272         }
1273
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_raise_exception ((MonoException*)exc);
1284         }
1285
1286         thread_data.domain = domain;
1287         thread_data.failure_reason = NULL;
1288
1289         /* 
1290          * First we create a separate thread for unloading, since
1291          * we might have to abort some threads, including the current one.
1292          */
1293         /*
1294          * If we create a non-suspended thread, the runtime will hang.
1295          * See:
1296          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1297          */ 
1298 #if 0
1299         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1300 #else
1301 #if defined(PLATFORM_WIN32) && defined (HAVE_BOEHM_GC)
1302         thread_handle = GC_CreateThread (NULL, 0, unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1303 #else
1304         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1305 #endif
1306         ResumeThread (thread_handle);
1307 #endif
1308         
1309         while (WaitForSingleObjectEx (thread_handle, INFINITE, FALSE) == WAIT_IO_COMPLETION)
1310                 ; /* wait for the thread */
1311         
1312
1313         if (thread_data.failure_reason) {
1314                 MonoException *ex;
1315
1316                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1317                 /* Roll back the state change */
1318                 domain->state = MONO_APPDOMAIN_CREATED;
1319
1320                 g_warning (thread_data.failure_reason);
1321
1322                 g_free (thread_data.failure_reason);
1323
1324                 mono_raise_exception (ex);
1325         }
1326 }