svn path=/branches/mono-1-1-9/mcs/; revision=51207
[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 41
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_assemblies_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_assemblies_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_assemblies_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_assemblies_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 assemblies_lock in the 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         static MonoClassField *assembly_load_field;
562         static MonoMethod *assembly_load_method;
563         MonoDomain *domain = mono_domain_get ();
564         MonoReflectionAssembly *ref_assembly;
565         MonoClass *klass;
566         gpointer load_value;
567         void *params [1];
568
569         if (!domain->domain)
570                 /* This can happen during startup */
571                 return;
572
573         klass = domain->domain->mbr.obj.vtable->klass;
574
575         mono_domain_assemblies_lock (domain);
576         add_assemblies_to_domain (domain, assembly, NULL);
577         mono_domain_assemblies_unlock (domain);
578
579         if (assembly_load_field == NULL) {
580                 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
581                 g_assert (assembly_load_field);
582         }
583
584         mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
585         if (load_value == NULL) {
586                 /* No events waiting to be triggered */
587                 return;
588         }
589
590         ref_assembly = mono_assembly_get_object (domain, assembly);
591         g_assert (ref_assembly);
592
593         if (assembly_load_method == NULL) {
594                 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
595                 g_assert (assembly_load_method);
596         }
597
598         *params = ref_assembly;
599         mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
600 }
601
602 static gchar *
603 reduce_path (const gchar *dirname)
604 {
605         gchar **parts;
606         gchar *part;
607         GList *list, *tmp;
608         GString *result;
609         gchar *res;
610         gint i;
611
612         parts = g_strsplit (dirname, G_DIR_SEPARATOR_S, 0);
613         list = NULL;
614         for (i = 0; (part = parts [i]) != NULL; i++) {
615                 if (!strcmp (part, "."))
616                         continue;
617
618                 if (!strcmp (part, "..")) {
619                         if (list && list->next) /* Don't remove root */
620                                 list = g_list_delete_link (list, list);
621                 } else {
622                         list = g_list_prepend (list, part);
623                 }
624         }
625
626         result = g_string_new ("");
627         list = g_list_reverse (list);
628
629         for (tmp = list; tmp; tmp = tmp->next) {
630                 gchar *data = (gchar *) tmp->data;
631
632                 if (data && *data) {
633 #ifdef PLATFORM_WIN32
634                         if (result->len == 0)
635                                 g_string_append_printf (result, "%s\\", data);
636                         else if (result->str [result->len - 1] == '\\')
637                                 g_string_append_printf (result, "%s", data);
638                         else
639 #endif
640                                 g_string_append_printf (result, "%c%s",
641                                                         G_DIR_SEPARATOR, data);
642                 }
643         }
644         
645         res = result->str;
646         g_string_free (result, FALSE);
647         g_list_free (list);
648         g_strfreev (parts);
649         return res;
650 }
651
652 static void
653 set_domain_search_path (MonoDomain *domain)
654 {
655         MonoAppDomainSetup *setup;
656         gchar **tmp;
657         gchar *utf8;
658         gint i;
659         gint npaths = 0;
660         gchar **pvt_split = NULL;
661         GError *error = NULL;
662         gint appbaselen = -1;
663
664         if ((domain->search_path != NULL) && !domain->setup->path_changed)
665                 return;
666         if (!domain->setup)
667                 return;
668
669         setup = domain->setup;
670         if (!setup->application_base)
671                 return; /* Must set application base to get private path working */
672
673         npaths++;
674         if (setup->private_bin_path) {
675                 utf8 = mono_string_to_utf8 (setup->private_bin_path);
676                 pvt_split = g_strsplit (utf8, G_SEARCHPATH_SEPARATOR_S, 1000);
677                 g_free (utf8);
678                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
679         }
680
681         if (!npaths) {
682                 if (pvt_split)
683                         g_strfreev (pvt_split);
684                 /*
685                  * Don't do this because the first time is called, the domain
686                  * setup is not finished.
687                  *
688                  * domain->search_path = g_malloc (sizeof (char *));
689                  * domain->search_path [0] = NULL;
690                 */
691                 return;
692         }
693
694         if (domain->search_path)
695                 g_strfreev (domain->search_path);
696
697         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
698         tmp [npaths] = NULL;
699
700         *tmp = mono_string_to_utf8 (setup->application_base);
701
702         /* FIXME: is this needed? */
703         if (strncmp (*tmp, "file://", 7) == 0) {
704                 gchar *file = *tmp;
705                 gchar *uri = *tmp;
706                 gchar *tmpuri;
707
708                 if (uri [7] != '/')
709                         uri = g_strdup_printf ("file:///%s", uri + 7);
710
711                 tmpuri = uri;
712                 uri = mono_escape_uri_string (tmpuri);
713                 *tmp = g_filename_from_uri (uri, NULL, &error);
714                 g_free (uri);
715
716                 if (tmpuri != file)
717                         g_free (tmpuri);
718
719                 if (error != NULL) {
720                         g_warning ("%s\n", error->message);
721                         g_error_free (error);
722                         *tmp = file;
723                 } else {
724                         g_free (file);
725                 }
726         }
727
728         for (i = 1; pvt_split && i < npaths; i++) {
729                 if (g_path_is_absolute (pvt_split [i - 1])) {
730                         tmp [i] = g_strdup (pvt_split [i - 1]);
731                 } else {
732                         tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
733                 }
734
735                 if (strchr (tmp [i], '.')) {
736                         gchar *reduced;
737                         gchar *freeme;
738
739                         reduced = reduce_path (tmp [i]);
740                         if (appbaselen == -1)
741                                 appbaselen = strlen (tmp [0]);
742
743                         if (strncmp (tmp [0], reduced, appbaselen)) {
744                                 g_free (reduced);
745                                 g_free (tmp [i]);
746                                 tmp [i] = g_strdup ("");
747                                 continue;
748                         }
749
750                         freeme = tmp [i];
751                         tmp [i] = reduced;
752                         g_free (freeme);
753                 }
754         }
755         
756         if (setup->private_bin_path_probe != NULL) {
757                 g_free (tmp [0]);
758                 tmp [0] = g_strdup ("");
759         }
760                 
761         domain->setup->path_changed = FALSE;
762
763         g_strfreev (pvt_split);
764 }
765
766 static gboolean
767 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
768                                         const gchar *path3, const gchar *path4, gboolean refonly)
769 {
770         gchar *fullpath;
771
772         *assembly = NULL;
773         fullpath = g_build_filename (path1, path2, path3, path4, NULL);
774         if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
775                 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
776
777         g_free (fullpath);
778         return (*assembly != NULL);
779 }
780
781 static MonoAssembly *
782 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
783 {
784         MonoAssembly *result = NULL;
785         gchar **path;
786         gchar *filename;
787         const gchar *local_culture;
788         gint len;
789
790         if (!culture || *culture == '\0') {
791                 local_culture = "";
792         } else {
793                 local_culture = culture;
794         }
795
796         filename =  g_strconcat (name, ".dll", NULL);
797         len = strlen (filename);
798
799         for (path = search_path; *path; path++) {
800                 if (**path == '\0')
801                         continue; /* Ignore empty ApplicationBase */
802
803                 /* See test cases in bug #58992 and bug #57710 */
804                 /* 1st try: [culture]/[name].dll (culture may be empty) */
805                 strcpy (filename + len - 4, ".dll");
806                 if (try_load_from (&result, *path, local_culture, "", filename, refonly))
807                         break;
808
809                 /* 2nd try: [culture]/[name].exe (culture may be empty) */
810                 strcpy (filename + len - 4, ".exe");
811                 if (try_load_from (&result, *path, local_culture, "", filename, refonly))
812                         break;
813
814                 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
815                 strcpy (filename + len - 4, ".dll");
816                 if (try_load_from (&result, *path, local_culture, name, filename, refonly))
817                         break;
818
819                 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
820                 strcpy (filename + len - 4, ".exe");
821                 if (try_load_from (&result, *path, local_culture, name, filename, refonly))
822                         break;
823         }
824
825         g_free (filename);
826         return result;
827 }
828
829 /*
830  * Try loading the assembly from ApplicationBase and PrivateBinPath 
831  * and then from assemblies_path if any.
832  */
833 static MonoAssembly *
834 mono_domain_assembly_preload (MonoAssemblyName *aname,
835                               gchar **assemblies_path,
836                               gpointer user_data)
837 {
838         MonoDomain *domain = mono_domain_get ();
839         MonoAssembly *result = NULL;
840         gboolean refonly = GPOINTER_TO_UINT (user_data);
841
842         set_domain_search_path (domain);
843
844         if (domain->search_path && domain->search_path [0] != NULL) {
845                 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
846         }
847
848         if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
849                 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
850         }
851
852         return result;
853 }
854
855 /*
856  * Check whenever a given assembly was already loaded in the current appdomain.
857  */
858 static MonoAssembly *
859 mono_domain_assembly_search (MonoAssemblyName *aname,
860                                                          gpointer user_data)
861 {
862         MonoDomain *domain = mono_domain_get ();
863         GSList *tmp;
864         MonoAssembly *ass;
865         gboolean refonly = GPOINTER_TO_UINT (user_data);
866
867         mono_domain_assemblies_lock (domain);
868         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
869                 ass = tmp->data;
870                 /* Dynamic assemblies can't match here in MS.NET */
871                 if (ass->dynamic || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
872                         continue;
873
874                 mono_domain_assemblies_unlock (domain);
875                 return ass;
876         }
877         mono_domain_assemblies_unlock (domain);
878
879         return NULL;
880 }
881
882 MonoReflectionAssembly *
883 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
884 {
885         MonoDomain *domain = mono_domain_get ();
886         char *name, *filename;
887         MonoImageOpenStatus status = MONO_IMAGE_OK;
888         MonoAssembly *ass;
889
890         MONO_ARCH_SAVE_REGS;
891
892         if (fname == NULL) {
893                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
894                 mono_raise_exception (exc);
895         }
896                 
897         name = filename = mono_string_to_utf8 (fname);
898
899         ass = mono_assembly_open_full (filename, &status, refOnly);
900         
901         g_free (name);
902
903         if (!ass){
904                 MonoException *exc = mono_get_exception_file_not_found (fname);
905                 mono_raise_exception (exc);
906         }
907
908         return mono_assembly_get_object (domain, ass);
909 }
910
911 MonoReflectionAssembly *
912 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
913                                                                                         MonoArray *raw_assembly,
914                                                                                         MonoArray *raw_symbol_store, MonoObject *evidence,
915                                                                                         MonoBoolean refonly)
916 {
917         MonoAssembly *ass;
918         MonoReflectionAssembly *refass = NULL;
919         MonoDomain *domain = ad->data;
920         MonoImageOpenStatus status;
921         guint32 raw_assembly_len = mono_array_length (raw_assembly);
922         MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
923
924         if (raw_symbol_store)
925                 mono_raise_exception (mono_get_exception_not_implemented ("LoadAssemblyRaw: Raw Symbol Store not Implemented"));
926   
927         if (!image) {
928                 mono_raise_exception (mono_get_exception_bad_image_format (""));
929                 return NULL;
930         }
931
932         ass = mono_assembly_load_from_full (image, "", &status, refonly);
933
934         if (!ass) {
935                 mono_image_close (image);
936                 mono_raise_exception (mono_get_exception_bad_image_format (""));
937                 return NULL; 
938         }
939
940         refass = mono_assembly_get_object (domain, ass);
941         refass->evidence = evidence;
942         return refass;
943 }
944
945 MonoReflectionAssembly *
946 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
947 {
948         MonoDomain *domain = ad->data; 
949         MonoImageOpenStatus status = MONO_IMAGE_OK;
950         MonoAssembly *ass;
951         MonoAssemblyName aname;
952         MonoReflectionAssembly *refass = NULL;
953         gchar *name;
954         gboolean parsed;
955
956         MONO_ARCH_SAVE_REGS;
957
958         g_assert (assRef != NULL);
959
960         name = mono_string_to_utf8 (assRef);
961         parsed = mono_assembly_name_parse (name, &aname);
962         g_free (name);
963
964         if (!parsed) {
965                 MonoException *exc;
966
967                 /* This is a parse error... */
968                 exc = mono_get_exception_file_not_found (assRef);
969                 mono_raise_exception (exc);
970         }
971
972         ass = mono_assembly_load_full (&aname, NULL, &status, refOnly);
973         mono_assembly_name_free (&aname);
974
975         if (!ass && (refass = try_assembly_resolve (domain, assRef, refOnly)) == NULL){
976                 /* FIXME: it doesn't make much sense since we really don't have a filename ... */
977                 MonoException *exc = mono_get_exception_file_not_found (assRef);
978                 mono_raise_exception (exc);
979         }
980
981         if (refass == NULL)
982                 refass = mono_assembly_get_object (domain, ass);
983
984         refass->evidence = evidence;
985         return refass;
986 }
987
988 void
989 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
990 {
991         MonoDomain * domain = mono_domain_get_by_id (domain_id);
992
993         MONO_ARCH_SAVE_REGS;
994
995         if (NULL == domain) {
996                 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
997                 mono_raise_exception (exc);
998         }
999         
1000         if (domain == mono_get_root_domain ()) {
1001                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
1002                 return;
1003         }
1004
1005         /* 
1006          * Unloading seems to cause problems when running NUnit/NAnt, hence
1007          * this workaround.
1008          */
1009         if (g_getenv ("MONO_NO_UNLOAD"))
1010                 return;
1011
1012         mono_domain_unload (domain);
1013 }
1014
1015 gboolean
1016 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
1017 {
1018         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1019
1020         if (!domain)
1021                 return TRUE;
1022
1023         return mono_domain_is_unloading (domain);
1024 }
1025
1026 gint32
1027 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file, 
1028                                             MonoObject *evidence, MonoArray *args)
1029 {
1030         MonoAssembly *assembly;
1031         MonoImage *image;
1032         MonoMethod *method;
1033         char *filename;
1034         gint32 res;
1035         MonoReflectionAssembly *refass;
1036
1037         MONO_ARCH_SAVE_REGS;
1038
1039         filename = mono_string_to_utf8 (file);
1040         assembly = mono_assembly_open (filename, NULL);
1041         g_free (filename);
1042
1043         if (!assembly)
1044                 mono_raise_exception (mono_get_exception_file_not_found (file));
1045
1046         image = assembly->image;
1047
1048         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1049
1050         if (!method)
1051                 g_error ("No entry point method found in %s", image->name);
1052
1053         if (!args)
1054                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1055
1056         refass = mono_assembly_get_object (ad->data, assembly);
1057         refass->evidence = evidence;
1058
1059         res = mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1060
1061         return res;
1062 }
1063
1064 gint32 
1065 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1066 {
1067         MONO_ARCH_SAVE_REGS;
1068
1069         return ad->data->domain_id;
1070 }
1071
1072 MonoAppDomain * 
1073 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1074 {
1075         MonoDomain *old_domain = mono_domain_get();
1076
1077         MONO_ARCH_SAVE_REGS;
1078
1079         if (!mono_domain_set (ad->data, FALSE))
1080                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1081
1082         return old_domain->domain;
1083 }
1084
1085 MonoAppDomain * 
1086 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1087 {
1088         MonoDomain *current_domain = mono_domain_get ();
1089         MonoDomain *domain = mono_domain_get_by_id (domainid);
1090
1091         MONO_ARCH_SAVE_REGS;
1092
1093         if (!domain || !mono_domain_set (domain, FALSE))        
1094                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1095
1096         return current_domain->domain;
1097 }
1098
1099 void
1100 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1101 {
1102         MONO_ARCH_SAVE_REGS;
1103
1104         mono_thread_push_appdomain_ref (ad->data);
1105 }
1106
1107 void
1108 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1109 {
1110         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1111
1112         MONO_ARCH_SAVE_REGS;
1113
1114         if (!domain)
1115                 /* 
1116                  * Raise an exception to prevent the managed code from executing a pop
1117                  * later.
1118                  */
1119                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1120
1121         mono_thread_push_appdomain_ref (domain);
1122 }
1123
1124 void
1125 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1126 {
1127         MONO_ARCH_SAVE_REGS;
1128
1129         mono_thread_pop_appdomain_ref ();
1130 }
1131
1132 MonoAppContext * 
1133 ves_icall_System_AppDomain_InternalGetContext ()
1134 {
1135         MONO_ARCH_SAVE_REGS;
1136
1137         return mono_context_get ();
1138 }
1139
1140 MonoAppContext * 
1141 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1142 {
1143         MONO_ARCH_SAVE_REGS;
1144
1145         return mono_domain_get ()->default_context;
1146 }
1147
1148 MonoAppContext * 
1149 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1150 {
1151         MonoAppContext *old_context = mono_context_get ();
1152
1153         MONO_ARCH_SAVE_REGS;
1154
1155         mono_context_set (mc);
1156
1157         return old_context;
1158 }
1159
1160 MonoString *
1161 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1162 {
1163         MonoDomain* mono_root_domain = mono_get_root_domain ();
1164         mono_domain_lock (mono_root_domain);
1165         if (process_guid_set) {
1166                 mono_domain_unlock (mono_root_domain);
1167                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1168         }
1169         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1170         process_guid_set = TRUE;
1171         mono_domain_unlock (mono_root_domain);
1172         return newguid;
1173 }
1174
1175 gboolean
1176 mono_domain_is_unloading (MonoDomain *domain)
1177 {
1178         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1179                 return TRUE;
1180         else
1181                 return FALSE;
1182 }
1183
1184 static void
1185 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1186 {
1187         MonoClass *klass = (MonoClass*)key;
1188         MonoDomain *domain = (MonoDomain*)user_data;
1189         MonoClassRuntimeInfo *runtime_info;
1190
1191         runtime_info = klass->runtime_info;
1192         if (runtime_info && runtime_info->max_domain >= domain->domain_id)
1193                 runtime_info->domain_vtables [domain->domain_id] = NULL;
1194 }
1195
1196 typedef struct unload_data {
1197         MonoDomain *domain;
1198         char *failure_reason;
1199 } unload_data;
1200
1201 static guint32 WINAPI
1202 unload_thread_main (void *arg)
1203 {
1204         unload_data *data = (unload_data*)arg;
1205         MonoDomain *domain = data->domain;
1206
1207         /* 
1208          * FIXME: Abort our parent thread last, so we can return a failure 
1209          * indication if aborting times out.
1210          */
1211         if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
1212                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1213                 return 1;
1214         }
1215
1216         /* Finalize all finalizable objects in the doomed appdomain */
1217         if (!mono_domain_finalize (domain, 10000)) {
1218                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1219                 return 1;
1220         }
1221
1222         /* Clear references to our vtables in class->runtime_info.
1223          * We also hold the loader lock because we're going to change
1224          * class->runtime_info.
1225          */
1226         mono_domain_lock (domain);
1227         mono_loader_lock ();
1228         g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, domain);
1229         mono_loader_unlock ();
1230         mono_domain_unlock (domain);
1231
1232         mono_threads_clear_cached_culture (domain);
1233
1234         domain->state = MONO_APPDOMAIN_UNLOADED;
1235
1236         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1237
1238         mono_domain_free (domain, FALSE);
1239
1240         mono_gc_collect (mono_gc_max_generation ());
1241
1242         return 0;
1243 }
1244
1245 /*
1246  * mono_domain_unload:
1247  *
1248  *  Unloads an appdomain. Follows the process outlined in:
1249  *  http://blogs.gotdotnet.com/cbrumme
1250  *  If doing things the 'right' way is too hard or complex, we do it the 
1251  *  'simple' way, which means do everything needed to avoid crashes and
1252  *  memory leaks, but not much else.
1253  */
1254 static void
1255 mono_domain_unload (MonoDomain *domain)
1256 {
1257         HANDLE thread_handle;
1258         gsize tid;
1259         guint32 res;
1260         MonoAppDomainState prev_state;
1261         MonoMethod *method;
1262         MonoObject *exc;
1263         unload_data thread_data;
1264         MonoDomain *caller_domain = mono_domain_get ();
1265
1266         /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
1267
1268         /* Atomically change our state to UNLOADING */
1269         prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
1270                                                                                          MONO_APPDOMAIN_UNLOADING,
1271                                                                                          MONO_APPDOMAIN_CREATED);
1272         if (prev_state != MONO_APPDOMAIN_CREATED) {
1273                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1274                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1275                 else
1276                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1277                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1278                 else
1279                         g_assert_not_reached ();
1280         }
1281
1282         mono_domain_set (domain, FALSE);
1283         /* Notify OnDomainUnload listeners */
1284         method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); 
1285         g_assert (method);
1286
1287         exc = NULL;
1288         mono_runtime_invoke (method, domain->domain, NULL, &exc);
1289         if (exc) {
1290                 /* Roll back the state change */
1291                 domain->state = MONO_APPDOMAIN_CREATED;
1292                 mono_domain_set (caller_domain, FALSE);
1293                 mono_raise_exception ((MonoException*)exc);
1294         }
1295
1296         thread_data.domain = domain;
1297         thread_data.failure_reason = NULL;
1298
1299         /* 
1300          * First we create a separate thread for unloading, since
1301          * we might have to abort some threads, including the current one.
1302          */
1303         /*
1304          * If we create a non-suspended thread, the runtime will hang.
1305          * See:
1306          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1307          */ 
1308 #if 0
1309         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1310 #else
1311         thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1312         ResumeThread (thread_handle);
1313 #endif
1314
1315         /* Wait for the thread */       
1316         while ((res = WaitForSingleObjectEx (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION)) {
1317                 if (mono_thread_has_appdomain_ref (mono_thread_current (), domain) && (mono_thread_interruption_requested ()))
1318                         /* The unload thread tries to abort us */
1319                         /* The icall wrapper will execute the abort */
1320                         return;
1321         }
1322
1323         mono_domain_set (caller_domain, FALSE);
1324
1325         if (thread_data.failure_reason) {
1326                 MonoException *ex;
1327
1328                 /* Roll back the state change */
1329                 domain->state = MONO_APPDOMAIN_CREATED;
1330
1331                 g_warning (thread_data.failure_reason);
1332
1333                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1334
1335                 g_free (thread_data.failure_reason);
1336                 thread_data.failure_reason = NULL;
1337
1338                 mono_raise_exception (ex);
1339         }
1340 }