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