merge 99762:100015
[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 #undef ASSEMBLY_LOAD_DEBUG
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #include <errno.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21
22 #include <mono/metadata/gc-internal.h>
23 #include <mono/metadata/object.h>
24 #include <mono/metadata/domain-internals.h>
25 #include "mono/metadata/metadata-internals.h"
26 #include <mono/metadata/assembly.h>
27 #include <mono/metadata/exception.h>
28 #include <mono/metadata/threads.h>
29 #include <mono/metadata/socket-io.h>
30 #include <mono/metadata/tabledefs.h>
31 #include <mono/metadata/gc-internal.h>
32 #include <mono/metadata/mono-gc.h>
33 #include <mono/metadata/marshal.h>
34 #include <mono/metadata/monitor.h>
35 #include <mono/metadata/threadpool.h>
36 #include <mono/metadata/mono-debug.h>
37 #include <mono/utils/mono-uri.h>
38 #include <mono/utils/mono-logger.h>
39 #include <mono/utils/mono-path.h>
40 #include <mono/utils/mono-stdlib.h>
41 #include <mono/utils/mono-io-portability.h>
42 #ifdef PLATFORM_WIN32
43 #include <direct.h>
44 #endif
45
46 /*
47  * This is the version number of the corlib-runtime interface. When
48  * making changes to this interface (by changing the layout
49  * of classes the runtime knows about, changing icall signature or
50  * semantics etc), increment this variable. Also increment the
51  * pair of this variable in mscorlib in:
52  *       mcs/class/mscorlib/System/Environment.cs
53  *
54  * Changes which are already detected at runtime, like the addition
55  * of icalls, do not require an increment.
56  */
57 #define MONO_CORLIB_VERSION 66
58
59 typedef struct
60 {
61         int runtime_count;
62         int assemblybinding_count;
63         MonoDomain *domain;
64 } RuntimeConfig;
65
66 CRITICAL_SECTION mono_delegate_section;
67
68 static gunichar2 process_guid [36];
69 static gboolean process_guid_set = FALSE;
70
71 static gboolean shutting_down = FALSE;
72
73 static MonoAssembly *
74 mono_domain_assembly_preload (MonoAssemblyName *aname,
75                               gchar **assemblies_path,
76                               gpointer user_data);
77
78 static MonoAssembly *
79 mono_domain_assembly_search (MonoAssemblyName *aname,
80                                                          gpointer user_data);
81
82 static MonoAssembly *
83 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
84                                                                           gpointer user_data);
85
86 static void
87 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
88
89 static void
90 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
91
92 static void
93 mono_domain_unload (MonoDomain *domain);
94
95 /**
96  * mono_runtime_init:
97  * @domain: domain returned by mono_init ()
98  *
99  * Initialize the core AppDomain: this function will run also some
100  * IL initialization code, so it needs the execution engine to be fully 
101  * operational.
102  *
103  * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
104  * we know the entry_assembly.
105  *
106  */
107 void
108 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
109                    MonoThreadAttachCB attach_cb)
110 {
111         MonoAppDomainSetup *setup;
112         MonoAppDomain *ad;
113         MonoClass *class;
114         MonoString *arg;
115
116         mono_portability_helpers_init ();
117         
118         mono_gc_base_init ();
119         mono_monitor_init ();
120         mono_thread_pool_init ();
121         mono_marshal_init ();
122
123         mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
124         mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
125         mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
126         mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
127         mono_install_assembly_postload_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
128         mono_install_assembly_postload_refonly_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
129         mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
130         mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
131
132         mono_thread_init (start_cb, attach_cb);
133
134         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
135         setup = (MonoAppDomainSetup *) mono_object_new (domain, class);
136
137         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
138         ad = (MonoAppDomain *) mono_object_new (domain, class);
139         ad->data = domain;
140         domain->domain = ad;
141         domain->setup = setup;
142
143         InitializeCriticalSection (&mono_delegate_section);
144
145         mono_thread_attach (domain);
146         mono_context_init (domain);
147         mono_context_set (domain->default_context);
148
149         mono_type_initialization_init ();
150
151         
152         /*
153          * Create an instance early since we can't do it when there is no memory.
154          */
155         arg = mono_string_new (domain, "Out of memory");
156         domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
157         
158         /* 
159          * These two are needed because the signal handlers might be executing on
160          * an alternate stack, and Boehm GC can't handle that.
161          */
162         arg = mono_string_new (domain, "A null value was found where an object instance was required");
163         domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
164         arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
165         domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
166         
167         /* GC init has to happen after thread init */
168         mono_gc_init ();
169
170         mono_network_init ();
171
172         /* mscorlib is loaded before we install the load hook */
173         mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
174         
175         return;
176 }
177
178 static int
179 mono_get_corlib_version (void)
180 {
181         MonoClass *klass;
182         MonoClassField *field;
183         MonoObject *value;
184
185         klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
186         mono_class_init (klass);
187         field = mono_class_get_field_from_name (klass, "mono_corlib_version");
188         if (!field)
189                 return -1;
190         if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
191                 return -1;
192         value = mono_field_get_value_object (mono_domain_get (), field, NULL);
193         return *(gint32*)((gchar*)value + sizeof (MonoObject));
194 }
195
196 const char*
197 mono_check_corlib_version (void)
198 {
199         int version = mono_get_corlib_version ();
200         if (version != MONO_CORLIB_VERSION)
201                 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
202         else
203                 return NULL;
204 }
205
206 void
207 mono_context_init (MonoDomain *domain)
208 {
209         MonoClass *class;
210         MonoAppContext *context;
211
212         class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
213         context = (MonoAppContext *) mono_object_new (domain, class);
214         context->domain_id = domain->domain_id;
215         context->context_id = 0;
216         domain->default_context = context;
217 }
218
219 /**
220  * mono_runtime_cleanup:
221  * @domain: unused.
222  *
223  * Internal routine.
224  *
225  * This must not be called while there are still running threads executing
226  * managed code.
227  */
228 void
229 mono_runtime_cleanup (MonoDomain *domain)
230 {
231         shutting_down = TRUE;
232
233         /* This ends up calling any pending pending (for at most 2 seconds) */
234         mono_gc_cleanup ();
235
236         mono_thread_cleanup ();
237
238         mono_network_cleanup ();
239
240         mono_marshal_cleanup ();
241
242         mono_type_initialization_cleanup ();
243
244         mono_monitor_cleanup ();
245
246 #ifndef PLATFORM_WIN32
247         _wapi_cleanup ();
248 #endif
249 }
250
251 static MonoDomainFunc quit_function = NULL;
252
253 void
254 mono_install_runtime_cleanup (MonoDomainFunc func)
255 {
256         quit_function = func;
257 }
258
259 void
260 mono_runtime_quit ()
261 {
262         if (quit_function != NULL)
263                 quit_function (mono_get_root_domain (), NULL);
264 }
265
266 /** 
267  * mono_runtime_set_shutting_down:
268  *
269  * Invoked by System.Environment.Exit to flag that the runtime
270  * is shutting down.
271  */
272 void
273 mono_runtime_set_shutting_down (void)
274 {
275         shutting_down = TRUE;
276 }
277
278 /**
279  * mono_runtime_is_shutting_down:
280  *
281  * Returns whether the runtime has been flagged for shutdown.
282  *
283  * This is consumed by the P:System.Environment.HasShutdownStarted
284  * property.
285  *
286  */
287 gboolean
288 mono_runtime_is_shutting_down (void)
289 {
290         return shutting_down;
291 }
292
293 /**
294  * mono_domain_has_type_resolve:
295  * @domain: application domains being looked up
296  *
297  * Returns true if the AppDomain.TypeResolve field has been
298  * set.
299  */
300 gboolean
301 mono_domain_has_type_resolve (MonoDomain *domain)
302 {
303         static MonoClassField *field = NULL;
304         MonoObject *o;
305
306         if (field == NULL) {
307                 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
308                 g_assert (field);
309         }
310
311         mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
312         return o != NULL;
313 }
314
315 /**
316  * mono_domain_try_type_resolve:
317  * @domain: application domainwhere the name where the type is going to be resolved
318  * @name: the name of the type to resolve or NULL.
319  * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
320  *
321  * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
322  * the assembly that matches name.
323  *
324  * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
325  *
326  * Returns: A MonoReflectionAssembly or NULL if not found
327  */
328 MonoReflectionAssembly *
329 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
330 {
331         MonoClass *klass;
332         void *params [1];
333         static MonoMethod *method = NULL;
334
335         g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
336
337         if (method == NULL) {
338                 klass = domain->domain->mbr.obj.vtable->klass;
339                 g_assert (klass);
340
341                 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
342                 if (method == NULL) {
343                         g_warning ("Method AppDomain.DoTypeResolve not found.\n");
344                         return NULL;
345                 }
346         }
347
348         if (name)
349                 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
350         else
351                 *params = tb;
352         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
353 }
354
355 /**
356  * mono_domain_owns_vtable_slot:
357  *
358  *  Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
359  */
360 gboolean
361 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
362 {
363         gboolean res;
364
365         mono_domain_lock (domain);
366         res = mono_mempool_contains_addr (domain->mp, vtable_slot);
367         mono_domain_unlock (domain);
368         return res;
369 }
370
371 /**
372  * mono_domain_set:
373  * @domain: domain
374  * @force: force setting.
375  *
376  * Set the current appdomain to @domain. If @force is set, set it even
377  * if it is being unloaded.
378  *
379  * Returns:
380  *   TRUE on success;
381  *   FALSE if the domain is unloaded
382  */
383 gboolean
384 mono_domain_set (MonoDomain *domain, gboolean force)
385 {
386         if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
387                 return FALSE;
388
389         mono_domain_set_internal (domain);
390
391         return TRUE;
392 }
393
394 MonoObject *
395 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
396 {
397         MonoDomain *add;
398         MonoObject *o;
399         char *str;
400
401         MONO_ARCH_SAVE_REGS;
402
403         g_assert (ad != NULL);
404         add = ad->data;
405         g_assert (add != NULL);
406
407         if (name == NULL)
408                 mono_raise_exception (mono_get_exception_argument_null ("name"));
409
410         str = mono_string_to_utf8 (name);
411
412         mono_domain_lock (add);
413
414         if (!strcmp (str, "APPBASE"))
415                 o = (MonoObject *)add->setup->application_base;
416         else if (!strcmp (str, "APP_CONFIG_FILE"))
417                 o = (MonoObject *)add->setup->configuration_file;
418         else if (!strcmp (str, "DYNAMIC_BASE"))
419                 o = (MonoObject *)add->setup->dynamic_base;
420         else if (!strcmp (str, "APP_NAME"))
421                 o = (MonoObject *)add->setup->application_name;
422         else if (!strcmp (str, "CACHE_BASE"))
423                 o = (MonoObject *)add->setup->cache_path;
424         else if (!strcmp (str, "PRIVATE_BINPATH"))
425                 o = (MonoObject *)add->setup->private_bin_path;
426         else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
427                 o = (MonoObject *)add->setup->private_bin_path_probe;
428         else if (!strcmp (str, "SHADOW_COPY_DIRS"))
429                 o = (MonoObject *)add->setup->shadow_copy_directories;
430         else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
431                 o = (MonoObject *)add->setup->shadow_copy_files;
432         else 
433                 o = mono_g_hash_table_lookup (add->env, name);
434
435         mono_domain_unlock (add);
436         g_free (str);
437
438         if (!o)
439                 return NULL;
440
441         return o;
442 }
443
444 void
445 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
446 {
447         MonoDomain *add;
448
449         MONO_ARCH_SAVE_REGS;
450
451         g_assert (ad != NULL);
452         add = ad->data;
453         g_assert (add != NULL);
454
455         if (name == NULL)
456                 mono_raise_exception (mono_get_exception_argument_null ("name"));
457
458         mono_domain_lock (add);
459
460         mono_g_hash_table_insert (add->env, name, data);
461
462         mono_domain_unlock (add);
463 }
464
465 MonoAppDomainSetup *
466 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
467 {
468         MONO_ARCH_SAVE_REGS;
469
470         g_assert (ad != NULL);
471         g_assert (ad->data != NULL);
472
473         return ad->data->setup;
474 }
475
476 MonoString *
477 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
478 {
479         MONO_ARCH_SAVE_REGS;
480
481         g_assert (ad != NULL);
482         g_assert (ad->data != NULL);
483
484         return mono_string_new (ad->data, ad->data->friendly_name);
485 }
486
487 MonoAppDomain *
488 ves_icall_System_AppDomain_getCurDomain ()
489 {
490         MonoDomain *add = mono_domain_get ();
491
492         MONO_ARCH_SAVE_REGS;
493
494         return add->domain;
495 }
496
497 MonoAppDomain *
498 ves_icall_System_AppDomain_getRootDomain ()
499 {
500         MonoDomain *root = mono_get_root_domain ();
501
502         MONO_ARCH_SAVE_REGS;
503
504         return root->domain;
505 }
506
507 static char*
508 get_attribute_value (const gchar **attribute_names, 
509                      const gchar **attribute_values, 
510                      const char *att_name)
511 {
512         int n;
513         for (n = 0; attribute_names [n] != NULL; n++) {
514                 if (strcmp (attribute_names [n], att_name) == 0)
515                         return g_strdup (attribute_values [n]);
516         }
517         return NULL;
518 }
519
520 static void
521 start_element (GMarkupParseContext *context, 
522                const gchar         *element_name,
523                const gchar        **attribute_names,
524                const gchar        **attribute_values,
525                gpointer             user_data,
526                GError             **error)
527 {
528         RuntimeConfig *runtime_config = user_data;
529         
530         if (strcmp (element_name, "runtime") == 0) {
531                 runtime_config->runtime_count++;
532                 return;
533         }
534
535         if (strcmp (element_name, "assemblyBinding") == 0) {
536                 runtime_config->assemblybinding_count++;
537                 return;
538         }
539         
540         if (runtime_config->runtime_count != 1 || runtime_config->assemblybinding_count != 1)
541                 return;
542
543         if (strcmp (element_name, "probing") != 0)
544                 return;
545
546         g_free (runtime_config->domain->private_bin_path);
547         runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath");
548         if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) {
549                 g_free (runtime_config->domain->private_bin_path);
550                 runtime_config->domain->private_bin_path = NULL;
551                 return;
552         }
553 }
554
555 static void
556 end_element (GMarkupParseContext *context,
557              const gchar         *element_name,
558              gpointer             user_data,
559              GError             **error)
560 {
561         RuntimeConfig *runtime_config = user_data;
562         if (strcmp (element_name, "runtime") == 0)
563                 runtime_config->runtime_count--;
564         else if (strcmp (element_name, "assemblyBinding") == 0)
565                 runtime_config->assemblybinding_count--;
566 }
567
568 static const GMarkupParser
569 mono_parser = {
570         start_element,
571         end_element,
572         NULL,
573         NULL,
574         NULL
575 };
576
577 static void
578 mono_set_private_bin_path_from_config (MonoDomain *domain)
579 {
580         gchar *config_file, *text;
581         gsize len;
582         struct stat sbuf;
583         GMarkupParseContext *context;
584         RuntimeConfig runtime_config;
585         
586         if (!domain || !domain->setup || !domain->setup->configuration_file)
587                 return;
588
589         config_file = mono_string_to_utf8 (domain->setup->configuration_file);
590         if (stat (config_file, &sbuf) != 0) {
591                 g_free (config_file);
592                 return;
593         }
594
595         if (!g_file_get_contents (config_file, &text, &len, NULL)) {
596                 g_free (config_file);
597                 return;
598         }
599         g_free (config_file);
600
601         runtime_config.runtime_count = 0;
602         runtime_config.assemblybinding_count = 0;
603         runtime_config.domain = domain;
604         
605         context = g_markup_parse_context_new (&mono_parser, 0, &runtime_config, NULL);
606         if (g_markup_parse_context_parse (context, text, len, NULL))
607                 g_markup_parse_context_end_parse (context, NULL);
608         g_markup_parse_context_free (context);
609         g_free (text);
610 }
611
612 MonoAppDomain *
613 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
614 {
615         MonoClass *adclass;
616         MonoAppDomain *ad;
617         MonoDomain *data;
618         
619         MONO_ARCH_SAVE_REGS;
620
621         adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
622
623         /* FIXME: pin all those objects */
624         data = mono_domain_create();
625
626         ad = (MonoAppDomain *) mono_object_new (data, adclass);
627         ad->data = data;
628         data->domain = ad;
629         data->setup = setup;
630         data->friendly_name = mono_string_to_utf8 (friendly_name);
631         // FIXME: The ctor runs in the current domain
632         // FIXME: Initialize null_reference_ex and stack_overflow_ex
633         data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException");
634
635         if (!setup->application_base) {
636                 /* Inherit from the root domain since MS.NET does this */
637                 MonoDomain *root = mono_get_root_domain ();
638                 if (root->setup->application_base)
639                         MONO_OBJECT_SETREF (setup, application_base, mono_string_new_utf16 (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base)));
640         }
641
642         mono_set_private_bin_path_from_config (data);
643         
644         mono_context_init (data);
645
646         add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
647
648         return ad;
649 }
650
651 MonoArray *
652 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
653 {
654         MonoDomain *domain = ad->data; 
655         MonoAssembly* ass;
656         static MonoClass *System_Reflection_Assembly;
657         MonoArray *res;
658         GSList *tmp;
659         int i, count;
660         
661         MONO_ARCH_SAVE_REGS;
662
663         if (!System_Reflection_Assembly)
664                 System_Reflection_Assembly = mono_class_from_name (
665                         mono_defaults.corlib, "System.Reflection", "Assembly");
666
667         count = 0;
668         /* Need to skip internal assembly builders created by remoting */
669         mono_domain_assemblies_lock (domain);
670         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
671                 ass = tmp->data;
672                 if (refonly && !ass->ref_only)
673                         continue;
674                 if (!ass->corlib_internal)
675                         count++;
676         }
677         res = mono_array_new (domain, System_Reflection_Assembly, count);
678         i = 0;
679         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
680                 ass = tmp->data;
681                 if (refonly && !ass->ref_only)
682                         continue;
683                 if (ass->corlib_internal)
684                         continue;
685                 mono_array_setref (res, i, mono_assembly_get_object (domain, ass));
686                 ++i;
687         }
688         mono_domain_assemblies_unlock (domain);
689
690         return res;
691 }
692
693 MonoReflectionAssembly *
694 mono_try_assembly_resolve (MonoDomain *domain, MonoString *fname, gboolean refonly)
695 {
696         MonoClass *klass;
697         MonoMethod *method;
698         MonoBoolean isrefonly;
699         gpointer params [2];
700
701         g_assert (domain != NULL && fname != NULL);
702
703         klass = domain->domain->mbr.obj.vtable->klass;
704         g_assert (klass);
705         
706         method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
707         if (method == NULL) {
708                 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
709                 return NULL;
710         }
711
712         isrefonly = refonly ? 1 : 0;
713         params [0] = fname;
714         params [1] = &isrefonly;
715         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
716 }
717
718 static MonoAssembly *
719 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
720                                                                           gpointer user_data)
721 {
722         gboolean refonly = GPOINTER_TO_UINT (user_data);
723         MonoReflectionAssembly *assembly;
724         MonoDomain *domain = mono_domain_get ();
725         char *aname_str;
726
727         aname_str = mono_stringify_assembly_name (aname);
728
729         /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
730         assembly = mono_try_assembly_resolve (domain, mono_string_new (domain, aname_str), refonly);
731
732         g_free (aname_str);
733
734         if (assembly)
735                 return assembly->assembly;
736         else
737                 return NULL;
738 }
739         
740 /*
741  * LOCKING: assumes assemblies_lock in the domain is already locked.
742  */
743 static void
744 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
745 {
746         gint i;
747         GSList *tmp;
748         gboolean destroy_ht = FALSE;
749
750         if (!ass->aname.name)
751                 return;
752
753         if (!ht) {
754                 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
755                 destroy_ht = TRUE;
756         }
757
758         /* FIXME: handle lazy loaded assemblies */
759         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
760                 g_hash_table_insert (ht, tmp->data, tmp->data);
761         }
762         if (!g_hash_table_lookup (ht, ass)) {
763                 mono_assembly_addref (ass);
764                 g_hash_table_insert (ht, ass, ass);
765                 domain->domain_assemblies = g_slist_prepend (domain->domain_assemblies, ass);
766                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s %p added to domain %s, ref_count=%d\n", ass->aname.name, ass, domain->friendly_name, ass->ref_count);
767         }
768
769         if (ass->image->references) {
770                 for (i = 0; ass->image->references [i] != NULL; i++) {
771                         if (ass->image->references [i] != REFERENCE_MISSING)
772                                 if (!g_hash_table_lookup (ht, ass->image->references [i])) {
773                                         add_assemblies_to_domain (domain, ass->image->references [i], ht);
774                                 }
775                 }
776         }
777         if (destroy_ht)
778                 g_hash_table_destroy (ht);
779 }
780
781 static void
782 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
783 {
784         static MonoClassField *assembly_load_field;
785         static MonoMethod *assembly_load_method;
786         MonoDomain *domain = mono_domain_get ();
787         MonoReflectionAssembly *ref_assembly;
788         MonoClass *klass;
789         gpointer load_value;
790         void *params [1];
791
792         if (!domain->domain)
793                 /* This can happen during startup */
794                 return;
795 #ifdef ASSEMBLY_LOAD_DEBUG
796         fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
797 #endif
798         klass = domain->domain->mbr.obj.vtable->klass;
799
800         mono_domain_assemblies_lock (domain);
801         add_assemblies_to_domain (domain, assembly, NULL);
802         mono_domain_assemblies_unlock (domain);
803
804         if (assembly_load_field == NULL) {
805                 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
806                 g_assert (assembly_load_field);
807         }
808
809         mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
810         if (load_value == NULL) {
811                 /* No events waiting to be triggered */
812                 return;
813         }
814
815         ref_assembly = mono_assembly_get_object (domain, assembly);
816         g_assert (ref_assembly);
817
818         if (assembly_load_method == NULL) {
819                 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
820                 g_assert (assembly_load_method);
821         }
822
823         *params = ref_assembly;
824         mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
825 }
826
827 static void
828 set_domain_search_path (MonoDomain *domain)
829 {
830         MonoAppDomainSetup *setup;
831         gchar **tmp;
832         gchar *search_path = NULL;
833         gint i;
834         gint npaths = 0;
835         gchar **pvt_split = NULL;
836         GError *error = NULL;
837         gint appbaselen = -1;
838
839         if ((domain->search_path != NULL) && !domain->setup->path_changed)
840                 return;
841         if (!domain->setup)
842                 return;
843
844         setup = domain->setup;
845         if (!setup->application_base)
846                 return; /* Must set application base to get private path working */
847
848         npaths++;
849         
850         if (setup->private_bin_path)
851                 search_path = mono_string_to_utf8 (setup->private_bin_path);
852         
853         if (domain->private_bin_path) {
854                 if (search_path == NULL)
855                         search_path = domain->private_bin_path;
856                 else {
857                         gchar *tmp2 = search_path;
858                         search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL);
859                         g_free (tmp2);
860                 }
861         }
862         
863         if (search_path) {
864                 /*
865                  * As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of
866                  * directories relative to ApplicationBase separated by semicolons (see
867                  * http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx)
868                  * The loop below copes with the fact that some Unix applications may use ':' (or
869                  * System.IO.Path.PathSeparator) as the path search separator. We replace it with
870                  * ';' for the subsequent split.
871                  *
872                  * The issue was reported in bug #81446
873                  */
874
875 #ifndef PLATFORM_WIN32
876                 gint slen;
877
878                 slen = strlen (search_path);
879                 for (i = 0; i < slen; i++)
880                         if (search_path [i] == ':')
881                                 search_path [i] = ';';
882 #endif
883                 
884                 pvt_split = g_strsplit (search_path, ";", 1000);
885                 g_free (search_path);
886                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
887         }
888
889         if (!npaths) {
890                 if (pvt_split)
891                         g_strfreev (pvt_split);
892                 /*
893                  * Don't do this because the first time is called, the domain
894                  * setup is not finished.
895                  *
896                  * domain->search_path = g_malloc (sizeof (char *));
897                  * domain->search_path [0] = NULL;
898                 */
899                 return;
900         }
901
902         if (domain->search_path)
903                 g_strfreev (domain->search_path);
904
905         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
906         tmp [npaths] = NULL;
907
908         *tmp = mono_string_to_utf8 (setup->application_base);
909
910         /* FIXME: is this needed? */
911         if (strncmp (*tmp, "file://", 7) == 0) {
912                 gchar *file = *tmp;
913                 gchar *uri = *tmp;
914                 gchar *tmpuri;
915
916                 if (uri [7] != '/')
917                         uri = g_strdup_printf ("file:///%s", uri + 7);
918
919                 tmpuri = uri;
920                 uri = mono_escape_uri_string (tmpuri);
921                 *tmp = g_filename_from_uri (uri, NULL, &error);
922                 g_free (uri);
923
924                 if (tmpuri != file)
925                         g_free (tmpuri);
926
927                 if (error != NULL) {
928                         g_warning ("%s\n", error->message);
929                         g_error_free (error);
930                         *tmp = file;
931                 } else {
932                         g_free (file);
933                 }
934         }
935
936         for (i = 1; pvt_split && i < npaths; i++) {
937                 if (g_path_is_absolute (pvt_split [i - 1])) {
938                         tmp [i] = g_strdup (pvt_split [i - 1]);
939                 } else {
940                         tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
941                 }
942
943                 if (strchr (tmp [i], '.')) {
944                         gchar *reduced;
945                         gchar *freeme;
946
947                         reduced = mono_path_canonicalize (tmp [i]);
948                         if (appbaselen == -1)
949                                 appbaselen = strlen (tmp [0]);
950
951                         if (strncmp (tmp [0], reduced, appbaselen)) {
952                                 g_free (reduced);
953                                 g_free (tmp [i]);
954                                 tmp [i] = g_strdup ("");
955                                 continue;
956                         }
957
958                         freeme = tmp [i];
959                         tmp [i] = reduced;
960                         g_free (freeme);
961                 }
962         }
963         
964         if (setup->private_bin_path_probe != NULL) {
965                 g_free (tmp [0]);
966                 tmp [0] = g_strdup ("");
967         }
968                 
969         domain->setup->path_changed = FALSE;
970
971         g_strfreev (pvt_split);
972 }
973
974 static gboolean
975 shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
976 {
977         guint16 *orig, *dest;
978         gboolean copy_result;
979         
980         strcpy (src + srclen - tail_len, extension);
981         if (!g_file_test (src, G_FILE_TEST_IS_REGULAR))
982                 return TRUE;
983         orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
984
985         strcpy (target + targetlen - tail_len, extension);
986         dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
987         
988         DeleteFile (dest);
989         copy_result = CopyFile (orig, dest, FALSE);
990         g_free (orig);
991         g_free (dest);
992         
993         return copy_result;
994 }
995
996 static gint32 
997 get_cstring_hash (const char *str)
998 {
999         int len, i;
1000         const char *p;
1001         gint32 h = 0;
1002         
1003         if (!str || !str [0])
1004                 return 0;
1005                 
1006         len = strlen (str);
1007         p = str;
1008         for (i = 0; i < len; i++) {
1009                 h = (h << 5) - h + *p;
1010                 p++;
1011         }
1012         
1013         return h;
1014 }
1015
1016 static char *
1017 get_shadow_assembly_location (const char *filename)
1018 {
1019         gint32 hash = 0, hash2 = 0;
1020         char name_hash [9];
1021         char path_hash [30];
1022         char *bname = g_path_get_basename (filename);
1023         char *dirname = g_path_get_dirname (filename);
1024         char *location, *dyn_base;
1025         MonoDomain *domain = mono_domain_get ();
1026         
1027         hash = get_cstring_hash (bname);
1028         hash2 = get_cstring_hash (dirname);
1029         g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
1030         g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial);
1031         dyn_base = mono_string_to_utf8 (domain->setup->dynamic_base);
1032         location = g_build_filename (dyn_base, "assembly", "shadow", name_hash, path_hash, bname, NULL);
1033         g_free (dyn_base);
1034         g_free (bname);
1035         g_free (dirname);
1036         return location;
1037 }
1038
1039 static gboolean
1040 ensure_directory_exists (const char *filename)
1041 {
1042 #ifdef PLATFORM_WIN32
1043         gchar *dir_utf8 = g_path_get_dirname (filename);
1044         gunichar2 *p;
1045         gunichar2 *dir_utf16 = NULL;
1046         int retval;
1047         
1048         if (!dir_utf8 || !dir_utf8 [0])
1049                 return FALSE;
1050
1051         dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
1052         g_free (dir_utf8);
1053
1054         if (!dir_utf16)
1055                 return FALSE;
1056
1057         p = dir_utf16;
1058
1059         /* make life easy and only use one directory seperator */
1060         while (*p != '\0')
1061         {
1062                 if (*p == '/')
1063                         *p = '\\';
1064                 p++;
1065         }
1066
1067         p = dir_utf16;
1068
1069         /* get past C:\ )*/
1070         while (*p++ != '\\')    
1071         {
1072         }
1073
1074         while (1) {
1075                 BOOL bRet = FALSE;
1076                 p = wcschr (p, '\\');
1077                 if (p)
1078                         *p = '\0';
1079                 retval = _wmkdir (dir_utf16);
1080                 if (retval != 0 && errno != EEXIST) {
1081                         g_free (dir_utf16);
1082                         return FALSE;
1083                 }
1084                 if (!p)
1085                         break;
1086                 *p++ = '\\';
1087         }
1088         
1089         g_free (dir_utf16);
1090         return TRUE;
1091 #else
1092         char *p;
1093         gchar *dir = g_path_get_dirname (filename);
1094         int retval;
1095         
1096         if (!dir || !dir [0])
1097                 return FALSE;
1098
1099         p = dir;
1100         while (*p == '/')
1101                 p++;
1102
1103         while (1) {
1104                 p = strchr (p, '/');
1105                 if (p)
1106                         *p = '\0';
1107                 retval = mkdir (dir, 0777);
1108                 if (retval != 0 && errno != EEXIST) {
1109                         g_free (dir);
1110                         return FALSE;
1111                 }
1112                 if (!p)
1113                         break;
1114                 *p++ = '/';
1115         }
1116         
1117         g_free (dir);
1118         return TRUE;
1119 #endif
1120 }
1121
1122 static char *
1123 make_shadow_copy (const char *filename)
1124 {
1125         gchar *sibling_source, *sibling_target;
1126         gint sibling_source_len, sibling_target_len;
1127         guint16 *orig, *dest;
1128         char *shadow;
1129         gboolean copy_result;
1130         MonoException *exc;
1131         
1132         shadow = get_shadow_assembly_location (filename);
1133         if (ensure_directory_exists (shadow) == FALSE) {
1134                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists).");
1135                 mono_raise_exception (exc);
1136         }       
1137
1138         orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
1139         dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
1140         DeleteFile (dest);
1141         copy_result = CopyFile (orig, dest, FALSE);
1142         g_free (dest);
1143         g_free (orig);
1144
1145         if (copy_result == FALSE) {
1146                 g_free (shadow);
1147                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (CopyFile).");
1148                 mono_raise_exception (exc);
1149         }
1150
1151         /* attempt to copy .mdb, .config if they exist */
1152         sibling_source = g_strconcat (filename, ".config", NULL);
1153         sibling_source_len = strlen (sibling_source);
1154         sibling_target = g_strconcat (shadow, ".config", NULL);
1155         sibling_target_len = strlen (sibling_target);
1156         
1157         copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
1158         if (copy_result == TRUE)
1159                 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
1160         
1161         g_free (sibling_source);
1162         g_free (sibling_target);
1163         
1164         if (copy_result == FALSE)  {
1165                 g_free (shadow);
1166                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy of sibling data (CopyFile).");
1167                 mono_raise_exception (exc);
1168         }
1169         
1170         return shadow;
1171 }
1172
1173 static gboolean
1174 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
1175                                         const gchar *path3, const gchar *path4,
1176                                         gboolean refonly, gboolean is_private)
1177 {
1178         
1179         gchar *fullpath;
1180
1181         *assembly = NULL;
1182         fullpath = g_build_filename (path1, path2, path3, path4, NULL);
1183         if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR)) {
1184                 if (is_private) {
1185                         char *new_path = make_shadow_copy (fullpath);
1186                         g_free (fullpath);
1187                         fullpath = new_path;
1188                 }
1189
1190                 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
1191         }
1192
1193         g_free (fullpath);
1194         return (*assembly != NULL);
1195 }
1196
1197 static MonoAssembly *
1198 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
1199 {
1200         MonoAssembly *result = NULL;
1201         gchar **path;
1202         gchar *filename;
1203         const gchar *local_culture;
1204         gint len;
1205         gboolean is_private = FALSE;
1206
1207         if (!culture || *culture == '\0') {
1208                 local_culture = "";
1209         } else {
1210                 local_culture = culture;
1211         }
1212
1213         filename =  g_strconcat (name, ".dll", NULL);
1214         len = strlen (filename);
1215
1216         for (path = search_path; *path; path++) {
1217                 if (**path == '\0') {
1218                         is_private = TRUE;
1219                         continue; /* Ignore empty ApplicationBase */
1220                 }
1221
1222                 /* See test cases in bug #58992 and bug #57710 */
1223                 /* 1st try: [culture]/[name].dll (culture may be empty) */
1224                 strcpy (filename + len - 4, ".dll");
1225                 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1226                         break;
1227
1228                 /* 2nd try: [culture]/[name].exe (culture may be empty) */
1229                 strcpy (filename + len - 4, ".exe");
1230                 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1231                         break;
1232
1233                 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
1234                 strcpy (filename + len - 4, ".dll");
1235                 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1236                         break;
1237
1238                 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
1239                 strcpy (filename + len - 4, ".exe");
1240                 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1241                         break;
1242         }
1243
1244         g_free (filename);
1245         return result;
1246 }
1247
1248 /*
1249  * Try loading the assembly from ApplicationBase and PrivateBinPath 
1250  * and then from assemblies_path if any.
1251  */
1252 static MonoAssembly *
1253 mono_domain_assembly_preload (MonoAssemblyName *aname,
1254                               gchar **assemblies_path,
1255                               gpointer user_data)
1256 {
1257         MonoDomain *domain = mono_domain_get ();
1258         MonoAssembly *result = NULL;
1259         gboolean refonly = GPOINTER_TO_UINT (user_data);
1260
1261         set_domain_search_path (domain);
1262
1263         if (domain->search_path && domain->search_path [0] != NULL) {
1264                 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
1265         }
1266
1267         if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
1268                 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
1269         }
1270
1271         return result;
1272 }
1273
1274 /*
1275  * Check whenever a given assembly was already loaded in the current appdomain.
1276  */
1277 static MonoAssembly *
1278 mono_domain_assembly_search (MonoAssemblyName *aname,
1279                                                          gpointer user_data)
1280 {
1281         MonoDomain *domain = mono_domain_get ();
1282         GSList *tmp;
1283         MonoAssembly *ass;
1284         gboolean refonly = GPOINTER_TO_UINT (user_data);
1285
1286         mono_domain_assemblies_lock (domain);
1287         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
1288                 ass = tmp->data;
1289                 /* Dynamic assemblies can't match here in MS.NET */
1290                 if (ass->dynamic || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
1291                         continue;
1292
1293                 mono_domain_assemblies_unlock (domain);
1294                 return ass;
1295         }
1296         mono_domain_assemblies_unlock (domain);
1297
1298         return NULL;
1299 }
1300
1301 MonoReflectionAssembly *
1302 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
1303 {
1304         MonoDomain *domain = mono_domain_get ();
1305         char *name, *filename;
1306         MonoImageOpenStatus status = MONO_IMAGE_OK;
1307         MonoAssembly *ass;
1308
1309         MONO_ARCH_SAVE_REGS;
1310
1311         if (fname == NULL) {
1312                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
1313                 mono_raise_exception (exc);
1314         }
1315                 
1316         name = filename = mono_string_to_utf8 (fname);
1317
1318         ass = mono_assembly_open_full (filename, &status, refOnly);
1319         
1320         if (!ass){
1321                 MonoException *exc;
1322
1323                 if (status == MONO_IMAGE_IMAGE_INVALID)
1324                         exc = mono_get_exception_bad_image_format2 (NULL, fname);
1325                 else
1326                         exc = mono_get_exception_file_not_found2 (NULL, fname);
1327                 g_free (name);
1328                 mono_raise_exception (exc);
1329         }
1330
1331         g_free (name);
1332
1333         return mono_assembly_get_object (domain, ass);
1334 }
1335
1336 MonoReflectionAssembly *
1337 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
1338                                             MonoArray *raw_assembly,
1339                                             MonoArray *raw_symbol_store, MonoObject *evidence,
1340                                             MonoBoolean refonly)
1341 {
1342         MonoAssembly *ass;
1343         MonoReflectionAssembly *refass = NULL;
1344         MonoDomain *domain = ad->data;
1345         MonoImageOpenStatus status;
1346         guint32 raw_assembly_len = mono_array_length (raw_assembly);
1347         MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
1348
1349         if (!image) {
1350                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1351                 return NULL;
1352         }
1353
1354         if (raw_symbol_store != NULL)
1355                 mono_debug_open_image_from_memory (image, mono_array_addr (raw_symbol_store, guint8, 0), mono_array_length (raw_symbol_store));
1356
1357         ass = mono_assembly_load_from_full (image, "", &status, refonly);
1358
1359
1360         if (!ass) {
1361                 mono_image_close (image);
1362                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1363                 return NULL; 
1364         }
1365
1366         refass = mono_assembly_get_object (domain, ass);
1367         MONO_OBJECT_SETREF (refass, evidence, evidence);
1368         return refass;
1369 }
1370
1371 MonoReflectionAssembly *
1372 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
1373 {
1374         MonoDomain *domain = ad->data; 
1375         MonoImageOpenStatus status = MONO_IMAGE_OK;
1376         MonoAssembly *ass;
1377         MonoAssemblyName aname;
1378         MonoReflectionAssembly *refass = NULL;
1379         gchar *name;
1380         gboolean parsed;
1381
1382         MONO_ARCH_SAVE_REGS;
1383
1384         g_assert (assRef != NULL);
1385
1386         name = mono_string_to_utf8 (assRef);
1387         parsed = mono_assembly_name_parse (name, &aname);
1388         g_free (name);
1389
1390         if (!parsed) {
1391                 MonoException *exc;
1392
1393                 /* This is a parse error... */
1394                 exc = mono_get_exception_file_not_found2 (NULL, assRef);
1395                 mono_raise_exception (exc);
1396         }
1397
1398         ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly);
1399         mono_assembly_name_free (&aname);
1400
1401         if (!ass) {
1402                 /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */
1403                 if (!refOnly)
1404                         refass = mono_try_assembly_resolve (domain, assRef, refOnly);
1405                 else
1406                         refass = NULL;
1407                 if (!refass) {
1408                         /* FIXME: it doesn't make much sense since we really don't have a filename ... */
1409                         MonoException *exc = mono_get_exception_file_not_found2 (NULL, assRef);
1410                         mono_raise_exception (exc);
1411                 }
1412         }
1413
1414         if (refass == NULL)
1415                 refass = mono_assembly_get_object (domain, ass);
1416
1417         MONO_OBJECT_SETREF (refass, evidence, evidence);
1418         return refass;
1419 }
1420
1421 void
1422 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
1423 {
1424         MonoDomain * domain = mono_domain_get_by_id (domain_id);
1425
1426         MONO_ARCH_SAVE_REGS;
1427
1428         if (NULL == domain) {
1429                 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
1430                 mono_raise_exception (exc);
1431         }
1432         
1433         if (domain == mono_get_root_domain ()) {
1434                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
1435                 return;
1436         }
1437
1438         /* 
1439          * Unloading seems to cause problems when running NUnit/NAnt, hence
1440          * this workaround.
1441          */
1442         if (g_getenv ("MONO_NO_UNLOAD"))
1443                 return;
1444
1445         mono_domain_unload (domain);
1446 }
1447
1448 gboolean
1449 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
1450 {
1451         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1452
1453         if (!domain)
1454                 return TRUE;
1455
1456         return mono_domain_is_unloading (domain);
1457 }
1458
1459 gint32
1460 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, 
1461                                                                                         MonoReflectionAssembly *refass, MonoArray *args)
1462 {
1463         MonoImage *image;
1464         MonoMethod *method;
1465
1466         MONO_ARCH_SAVE_REGS;
1467
1468         g_assert (refass);
1469         image = refass->assembly->image;
1470         g_assert (image);
1471
1472         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1473
1474         if (!method)
1475                 g_error ("No entry point method found in %s", image->name);
1476
1477         if (!args)
1478                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1479
1480         return mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1481 }
1482
1483 gint32 
1484 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1485 {
1486         MONO_ARCH_SAVE_REGS;
1487
1488         return ad->data->domain_id;
1489 }
1490
1491 MonoAppDomain * 
1492 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1493 {
1494         MonoDomain *old_domain = mono_domain_get();
1495
1496         MONO_ARCH_SAVE_REGS;
1497
1498         if (!mono_domain_set (ad->data, FALSE))
1499                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1500
1501         return old_domain->domain;
1502 }
1503
1504 MonoAppDomain * 
1505 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1506 {
1507         MonoDomain *current_domain = mono_domain_get ();
1508         MonoDomain *domain = mono_domain_get_by_id (domainid);
1509
1510         MONO_ARCH_SAVE_REGS;
1511
1512         if (!domain || !mono_domain_set (domain, FALSE))        
1513                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1514
1515         return current_domain->domain;
1516 }
1517
1518 void
1519 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1520 {
1521         MONO_ARCH_SAVE_REGS;
1522
1523         mono_thread_push_appdomain_ref (ad->data);
1524 }
1525
1526 void
1527 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1528 {
1529         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1530
1531         MONO_ARCH_SAVE_REGS;
1532
1533         if (!domain)
1534                 /* 
1535                  * Raise an exception to prevent the managed code from executing a pop
1536                  * later.
1537                  */
1538                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1539
1540         mono_thread_push_appdomain_ref (domain);
1541 }
1542
1543 void
1544 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1545 {
1546         MONO_ARCH_SAVE_REGS;
1547
1548         mono_thread_pop_appdomain_ref ();
1549 }
1550
1551 MonoAppContext * 
1552 ves_icall_System_AppDomain_InternalGetContext ()
1553 {
1554         MONO_ARCH_SAVE_REGS;
1555
1556         return mono_context_get ();
1557 }
1558
1559 MonoAppContext * 
1560 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1561 {
1562         MONO_ARCH_SAVE_REGS;
1563
1564         return mono_domain_get ()->default_context;
1565 }
1566
1567 MonoAppContext * 
1568 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1569 {
1570         MonoAppContext *old_context = mono_context_get ();
1571
1572         MONO_ARCH_SAVE_REGS;
1573
1574         mono_context_set (mc);
1575
1576         return old_context;
1577 }
1578
1579 MonoString *
1580 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1581 {
1582         MonoDomain* mono_root_domain = mono_get_root_domain ();
1583         mono_domain_lock (mono_root_domain);
1584         if (process_guid_set) {
1585                 mono_domain_unlock (mono_root_domain);
1586                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1587         }
1588         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1589         process_guid_set = TRUE;
1590         mono_domain_unlock (mono_root_domain);
1591         return newguid;
1592 }
1593
1594 gboolean
1595 mono_domain_is_unloading (MonoDomain *domain)
1596 {
1597         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1598                 return TRUE;
1599         else
1600                 return FALSE;
1601 }
1602
1603 static void
1604 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1605 {
1606         MonoClass *klass = (MonoClass*)key;
1607         MonoDomain *domain = (MonoDomain*)user_data;
1608         MonoClassRuntimeInfo *runtime_info;
1609
1610         runtime_info = klass->runtime_info;
1611         if (runtime_info && runtime_info->max_domain >= domain->domain_id)
1612                 runtime_info->domain_vtables [domain->domain_id] = NULL;
1613 }
1614
1615 typedef struct unload_data {
1616         MonoDomain *domain;
1617         char *failure_reason;
1618 } unload_data;
1619
1620 static guint32 WINAPI
1621 unload_thread_main (void *arg)
1622 {
1623         unload_data *data = (unload_data*)arg;
1624         MonoDomain *domain = data->domain;
1625
1626         /* 
1627          * FIXME: Abort our parent thread last, so we can return a failure 
1628          * indication if aborting times out.
1629          */
1630         if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
1631                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1632                 return 1;
1633         }
1634
1635         /* Finalize all finalizable objects in the doomed appdomain */
1636         if (!mono_domain_finalize (domain, 10000)) {
1637                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1638                 return 1;
1639         }
1640
1641         /* Clear references to our vtables in class->runtime_info.
1642          * We also hold the loader lock because we're going to change
1643          * class->runtime_info.
1644          */
1645         mono_domain_lock (domain);
1646         mono_loader_lock ();
1647         g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, domain);
1648         mono_loader_unlock ();
1649         mono_domain_unlock (domain);
1650
1651         mono_threads_clear_cached_culture (domain);
1652
1653         domain->state = MONO_APPDOMAIN_UNLOADED;
1654
1655         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1656
1657         /* remove from the handle table the items related to this domain */
1658         mono_gchandle_free_domain (domain);
1659
1660         mono_domain_free (domain, FALSE);
1661
1662         mono_gc_collect (mono_gc_max_generation ());
1663
1664         return 0;
1665 }
1666
1667 /*
1668  * mono_domain_unload:
1669  * @domain: The domain to unload
1670  *
1671  *  Unloads an appdomain. Follows the process outlined in:
1672  *  http://blogs.gotdotnet.com/cbrumme
1673  *
1674  *  If doing things the 'right' way is too hard or complex, we do it the 
1675  *  'simple' way, which means do everything needed to avoid crashes and
1676  *  memory leaks, but not much else.
1677  */
1678 static void
1679 mono_domain_unload (MonoDomain *domain)
1680 {
1681         HANDLE thread_handle;
1682         gsize tid;
1683         guint32 res;
1684         MonoAppDomainState prev_state;
1685         MonoMethod *method;
1686         MonoObject *exc;
1687         unload_data thread_data;
1688         MonoDomain *caller_domain = mono_domain_get ();
1689
1690         /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
1691
1692         /* Atomically change our state to UNLOADING */
1693         prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
1694                                                                                          MONO_APPDOMAIN_UNLOADING,
1695                                                                                          MONO_APPDOMAIN_CREATED);
1696         if (prev_state != MONO_APPDOMAIN_CREATED) {
1697                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1698                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1699                 else
1700                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1701                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1702                 else
1703                         g_assert_not_reached ();
1704         }
1705
1706         mono_domain_set (domain, FALSE);
1707         /* Notify OnDomainUnload listeners */
1708         method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); 
1709         g_assert (method);
1710
1711         exc = NULL;
1712         mono_runtime_invoke (method, domain->domain, NULL, &exc);
1713         if (exc) {
1714                 /* Roll back the state change */
1715                 domain->state = MONO_APPDOMAIN_CREATED;
1716                 mono_domain_set (caller_domain, FALSE);
1717                 mono_raise_exception ((MonoException*)exc);
1718         }
1719
1720         thread_data.domain = domain;
1721         thread_data.failure_reason = NULL;
1722
1723         /* 
1724          * First we create a separate thread for unloading, since
1725          * we might have to abort some threads, including the current one.
1726          */
1727         /*
1728          * If we create a non-suspended thread, the runtime will hang.
1729          * See:
1730          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1731          */ 
1732 #if 0
1733         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1734 #else
1735         thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1736         if (thread_handle == NULL) {
1737                 return;
1738         }
1739         ResumeThread (thread_handle);
1740 #endif
1741
1742         /* Wait for the thread */       
1743         while ((res = WaitForSingleObjectEx (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION)) {
1744                 if (mono_thread_has_appdomain_ref (mono_thread_current (), domain) && (mono_thread_interruption_requested ()))
1745                         /* The unload thread tries to abort us */
1746                         /* The icall wrapper will execute the abort */
1747                         CloseHandle (thread_handle);
1748                         return;
1749         }
1750         CloseHandle (thread_handle);
1751
1752         mono_domain_set (caller_domain, FALSE);
1753
1754         if (thread_data.failure_reason) {
1755                 MonoException *ex;
1756
1757                 /* Roll back the state change */
1758                 domain->state = MONO_APPDOMAIN_CREATED;
1759
1760                 g_warning (thread_data.failure_reason);
1761
1762                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1763
1764                 g_free (thread_data.failure_reason);
1765                 thread_data.failure_reason = NULL;
1766
1767                 mono_raise_exception (ex);
1768         }
1769 }