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