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