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