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