Merge pull request #3100 from lambdageek/dev/monoerror-exns
[mono-project.git] / mono / metadata / appdomain.c
blobeb3f68204036d2f79ab525c3995a8a3a689867e4
1 /*
2 * appdomain.c: AppDomain functions
4 * Authors:
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Patrik Torstensson
7 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11 * Copyright 2012 Xamarin Inc
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #undef ASSEMBLY_LOAD_DEBUG
15 #include <config.h>
16 #include <glib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <time.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #ifdef HAVE_SYS_TIME_H
23 #include <sys/time.h>
24 #endif
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #ifdef HAVE_UTIME_H
29 #include <utime.h>
30 #else
31 #ifdef HAVE_SYS_UTIME_H
32 #include <sys/utime.h>
33 #endif
34 #endif
36 #include <mono/metadata/gc-internals.h>
37 #include <mono/metadata/object.h>
38 #include <mono/metadata/domain-internals.h>
39 #include "mono/metadata/metadata-internals.h"
40 #include <mono/metadata/assembly.h>
41 #include <mono/metadata/exception.h>
42 #include <mono/metadata/exception-internals.h>
43 #include <mono/metadata/threads.h>
44 #include <mono/metadata/threadpool-ms.h>
45 #include <mono/metadata/socket-io.h>
46 #include <mono/metadata/tabledefs.h>
47 #include <mono/metadata/gc-internals.h>
48 #include <mono/metadata/mono-gc.h>
49 #include <mono/metadata/marshal.h>
50 #include <mono/metadata/monitor.h>
51 #include <mono/metadata/mono-debug.h>
52 #include <mono/metadata/mono-debug-debugger.h>
53 #include <mono/metadata/attach.h>
54 #include <mono/metadata/file-io.h>
55 #include <mono/metadata/lock-tracer.h>
56 #include <mono/metadata/console-io.h>
57 #include <mono/metadata/threads-types.h>
58 #include <mono/metadata/tokentype.h>
59 #include <mono/metadata/profiler-private.h>
60 #include <mono/metadata/reflection-internals.h>
61 #include <mono/utils/mono-uri.h>
62 #include <mono/utils/mono-logger-internals.h>
63 #include <mono/utils/mono-path.h>
64 #include <mono/utils/mono-stdlib.h>
65 #include <mono/utils/mono-io-portability.h>
66 #include <mono/utils/mono-error-internals.h>
67 #include <mono/utils/atomic.h>
68 #include <mono/utils/mono-memory-model.h>
69 #include <mono/utils/mono-threads.h>
70 #ifdef HOST_WIN32
71 #include <direct.h>
72 #endif
75 * This is the version number of the corlib-runtime interface. When
76 * making changes to this interface (by changing the layout
77 * of classes the runtime knows about, changing icall signature or
78 * semantics etc), increment this variable. Also increment the
79 * pair of this variable in mscorlib in:
80 * mcs/class/corlib/System/Environment.cs
82 * Changes which are already detected at runtime, like the addition
83 * of icalls, do not require an increment.
85 #define MONO_CORLIB_VERSION 149
87 typedef struct
89 int runtime_count;
90 int assemblybinding_count;
91 MonoDomain *domain;
92 gchar *filename;
93 } RuntimeConfig;
95 static gunichar2 process_guid [36];
96 static gboolean process_guid_set = FALSE;
98 static gboolean no_exec = FALSE;
100 static MonoAssembly *
101 mono_domain_assembly_preload (MonoAssemblyName *aname,
102 gchar **assemblies_path,
103 gpointer user_data);
105 static MonoAssembly *
106 mono_domain_assembly_search (MonoAssemblyName *aname,
107 gpointer user_data);
109 static void
110 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
112 static void
113 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
115 static MonoAppDomain *
116 mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup, MonoError *error);
118 static char *
119 get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error);
121 static MonoLoadFunc load_function = NULL;
123 /* Lazy class loading functions */
124 static GENERATE_GET_CLASS_WITH_CACHE (assembly, System.Reflection, Assembly)
126 void
127 mono_install_runtime_load (MonoLoadFunc func)
129 load_function = func;
132 MonoDomain*
133 mono_runtime_load (const char *filename, const char *runtime_version)
135 g_assert (load_function);
136 return load_function (filename, runtime_version);
140 * mono_runtime_set_no_exec:
142 * Instructs the runtime to operate in static mode, i.e. avoid/do not
143 * allow managed code execution. This is useful for running the AOT
144 * compiler on platforms which allow full-aot execution only. This
145 * should be called before mono_runtime_init ().
147 void
148 mono_runtime_set_no_exec (gboolean val)
150 no_exec = val;
154 * mono_runtime_get_no_exec:
156 * If true, then the runtime will not allow managed code execution.
158 gboolean
159 mono_runtime_get_no_exec (void)
161 return no_exec;
164 static void
165 create_domain_objects (MonoDomain *domain)
167 MonoError error;
168 MonoDomain *old_domain = mono_domain_get ();
169 MonoString *arg;
170 MonoVTable *string_vt;
171 MonoClassField *string_empty_fld;
173 if (domain != old_domain) {
174 mono_thread_push_appdomain_ref (domain);
175 mono_domain_set_internal_with_options (domain, FALSE);
179 * Initialize String.Empty. This enables the removal of
180 * the static cctor of the String class.
182 string_vt = mono_class_vtable (domain, mono_defaults.string_class);
183 string_empty_fld = mono_class_get_field_from_name (mono_defaults.string_class, "Empty");
184 g_assert (string_empty_fld);
185 MonoString *empty_str = mono_string_intern_checked (mono_string_new (domain, ""), &error);
186 mono_error_assert_ok (&error);
187 mono_field_static_set_value (string_vt, string_empty_fld, empty_str);
190 * Create an instance early since we can't do it when there is no memory.
192 arg = mono_string_new (domain, "Out of memory");
193 domain->out_of_memory_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL, &error);
194 mono_error_assert_ok (&error);
197 * These two are needed because the signal handlers might be executing on
198 * an alternate stack, and Boehm GC can't handle that.
200 arg = mono_string_new (domain, "A null value was found where an object instance was required");
201 domain->null_reference_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL, &error);
202 mono_error_assert_ok (&error);
203 arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
204 domain->stack_overflow_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL, &error);
205 mono_error_assert_ok (&error);
207 /*The ephemeron tombstone i*/
208 domain->ephemeron_tombstone = mono_object_new_checked (domain, mono_defaults.object_class, &error);
209 mono_error_assert_ok (&error);
211 if (domain != old_domain) {
212 mono_thread_pop_appdomain_ref ();
213 mono_domain_set_internal_with_options (old_domain, FALSE);
217 * This class is used during exception handling, so initialize it here, to prevent
218 * stack overflows while handling stack overflows.
220 mono_class_init (mono_array_class_get (mono_defaults.int_class, 1));
224 * mono_runtime_init:
225 * @domain: domain returned by mono_init ()
227 * Initialize the core AppDomain: this function will run also some
228 * IL initialization code, so it needs the execution engine to be fully
229 * operational.
231 * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
232 * we know the entry_assembly.
235 void
236 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb)
238 MonoError error;
239 mono_runtime_init_checked (domain, start_cb, attach_cb, &error);
240 mono_error_cleanup (&error);
243 void
244 mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb, MonoError *error)
246 MonoAppDomainSetup *setup;
247 MonoAppDomain *ad;
248 MonoClass *klass;
250 mono_error_init (error);
252 mono_portability_helpers_init ();
254 mono_gc_base_init ();
255 mono_monitor_init ();
256 mono_marshal_init ();
258 mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
259 mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
260 mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
261 mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
262 mono_install_assembly_postload_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
263 mono_install_assembly_postload_refonly_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
264 mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
266 mono_thread_init (start_cb, attach_cb);
268 klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
269 setup = (MonoAppDomainSetup *) mono_object_new_pinned (domain, klass, error);
270 return_if_nok (error);
272 klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomain");
274 ad = (MonoAppDomain *) mono_object_new_pinned (domain, klass, error);
275 return_if_nok (error);
277 ad->data = domain;
278 domain->domain = ad;
279 domain->setup = setup;
281 mono_thread_attach (domain);
283 mono_type_initialization_init ();
285 if (!mono_runtime_get_no_exec ())
286 create_domain_objects (domain);
288 /* GC init has to happen after thread init */
289 mono_gc_init ();
291 /* contexts use GC handles, so they must be initialized after the GC */
292 mono_context_init_checked (domain, error);
293 return_if_nok (error);
294 mono_context_set (domain->default_context);
296 #ifndef DISABLE_SOCKETS
297 mono_network_init ();
298 #endif
300 mono_console_init ();
301 mono_attach_init ();
303 mono_locks_tracer_init ();
305 /* mscorlib is loaded before we install the load hook */
306 mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
308 return;
311 static int
312 mono_get_corlib_version (void)
314 MonoError error;
315 MonoClass *klass;
316 MonoClassField *field;
317 MonoObject *value;
319 klass = mono_class_load_from_name (mono_defaults.corlib, "System", "Environment");
320 mono_class_init (klass);
321 field = mono_class_get_field_from_name (klass, "mono_corlib_version");
322 if (!field)
323 return -1;
324 if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
325 return -1;
326 value = mono_field_get_value_object_checked (mono_domain_get (), field, NULL, &error);
327 mono_error_assert_ok (&error);
328 return *(gint32*)((gchar*)value + sizeof (MonoObject));
332 * mono_check_corlib_version
334 * Checks that the corlib that is loaded matches the version of this runtime.
336 * Returns: NULL if the runtime will work with the corlib, or a g_malloc
337 * allocated string with the error otherwise.
339 const char*
340 mono_check_corlib_version (void)
342 int version = mono_get_corlib_version ();
343 if (version != MONO_CORLIB_VERSION)
344 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
345 else
346 return NULL;
350 * mono_context_init:
351 * @domain: The domain where the System.Runtime.Remoting.Context.Context is initialized
353 * Initializes the @domain's default System.Runtime.Remoting's Context.
355 void
356 mono_context_init (MonoDomain *domain)
358 MonoError error;
359 mono_context_init_checked (domain, &error);
360 mono_error_cleanup (&error);
363 void
364 mono_context_init_checked (MonoDomain *domain, MonoError *error)
366 MonoClass *klass;
367 MonoAppContext *context;
369 mono_error_init (error);
371 klass = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
372 context = (MonoAppContext *) mono_object_new_pinned (domain, klass, error);
373 return_if_nok (error);
375 context->domain_id = domain->domain_id;
376 context->context_id = 0;
377 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (context);
378 domain->default_context = context;
382 * mono_runtime_cleanup:
383 * @domain: unused.
385 * Internal routine.
387 * This must not be called while there are still running threads executing
388 * managed code.
390 void
391 mono_runtime_cleanup (MonoDomain *domain)
393 mono_attach_cleanup ();
395 /* This ends up calling any pending pending (for at most 2 seconds) */
396 mono_gc_cleanup ();
398 mono_thread_cleanup ();
400 #ifndef DISABLE_SOCKETS
401 mono_network_cleanup ();
402 #endif
403 mono_marshal_cleanup ();
405 mono_type_initialization_cleanup ();
407 mono_monitor_cleanup ();
410 static MonoDomainFunc quit_function = NULL;
412 void
413 mono_install_runtime_cleanup (MonoDomainFunc func)
415 quit_function = func;
418 void
419 mono_runtime_quit ()
421 if (quit_function != NULL)
422 quit_function (mono_get_root_domain (), NULL);
426 * mono_domain_create_appdomain:
427 * @friendly_name: The friendly name of the appdomain to create
428 * @configuration_file: The configuration file to initialize the appdomain with
430 * Returns a MonoDomain initialized with the appdomain
432 MonoDomain *
433 mono_domain_create_appdomain (char *friendly_name, char *configuration_file)
435 MonoError error;
436 MonoAppDomain *ad;
437 MonoAppDomainSetup *setup;
438 MonoClass *klass;
440 klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
441 setup = (MonoAppDomainSetup *) mono_object_new_checked (mono_domain_get (), klass, &error);
442 if (!is_ok (&error))
443 goto fail;
444 setup->configuration_file = configuration_file != NULL ? mono_string_new (mono_domain_get (), configuration_file) : NULL;
446 ad = mono_domain_create_appdomain_internal (friendly_name, setup, &error);
447 if (!is_ok (&error))
448 goto fail;
450 return mono_domain_from_appdomain (ad);
451 fail:
452 mono_error_cleanup (&error);
453 return NULL;
457 * mono_domain_set_config:
458 * @domain: MonoDomain initialized with the appdomain we want to change
459 * @base_dir: new base directory for the appdomain
460 * @config_file_name: path to the new configuration for the app domain
462 * Used to set the system configuration for an appdomain
464 * Without using this, embedded builds will get 'System.Configuration.ConfigurationErrorsException:
465 * Error Initializing the configuration system. ---> System.ArgumentException:
466 * The 'ExeConfigFilename' argument cannot be null.' for some managed calls.
468 void
469 mono_domain_set_config (MonoDomain *domain, const char *base_dir, const char *config_file_name)
471 MONO_OBJECT_SETREF (domain->setup, application_base, mono_string_new (domain, base_dir));
472 MONO_OBJECT_SETREF (domain->setup, configuration_file, mono_string_new (domain, config_file_name));
475 static MonoAppDomainSetup*
476 copy_app_domain_setup (MonoDomain *domain, MonoAppDomainSetup *setup, MonoError *error)
478 MonoDomain *caller_domain;
479 MonoClass *ads_class;
480 MonoAppDomainSetup *copy;
482 mono_error_init (error);
484 caller_domain = mono_domain_get ();
485 ads_class = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
487 copy = (MonoAppDomainSetup*)mono_object_new_checked (domain, ads_class, error);
488 return_val_if_nok (error, NULL);
490 mono_domain_set_internal (domain);
492 #define XCOPY_FIELD(dst,field,src,error) \
493 do { \
494 MonoObject *copied_val = mono_marshal_xdomain_copy_value ((MonoObject*)(src), error); \
495 return_val_if_nok (error, NULL); \
496 MONO_OBJECT_SETREF ((dst),field,copied_val); \
497 } while (0)
499 XCOPY_FIELD (copy, application_base, setup->application_base, error);
500 XCOPY_FIELD (copy, application_name, setup->application_name, error);
501 XCOPY_FIELD (copy, cache_path, setup->cache_path, error);
502 XCOPY_FIELD (copy, configuration_file, setup->configuration_file, error);
503 XCOPY_FIELD (copy, dynamic_base, setup->dynamic_base, error);
504 XCOPY_FIELD (copy, license_file, setup->license_file, error);
505 XCOPY_FIELD (copy, private_bin_path, setup->private_bin_path, error);
506 XCOPY_FIELD (copy, private_bin_path_probe, setup->private_bin_path_probe, error);
507 XCOPY_FIELD (copy, shadow_copy_directories, setup->shadow_copy_directories, error);
508 XCOPY_FIELD (copy, shadow_copy_files, setup->shadow_copy_files, error);
509 copy->publisher_policy = setup->publisher_policy;
510 copy->path_changed = setup->path_changed;
511 copy->loader_optimization = setup->loader_optimization;
512 copy->disallow_binding_redirects = setup->disallow_binding_redirects;
513 copy->disallow_code_downloads = setup->disallow_code_downloads;
514 XCOPY_FIELD (copy, domain_initializer_args, setup->domain_initializer_args, error);
515 copy->disallow_appbase_probe = setup->disallow_appbase_probe;
516 XCOPY_FIELD (copy, application_trust, setup->application_trust, error);
517 XCOPY_FIELD (copy, configuration_bytes, setup->configuration_bytes, error);
518 XCOPY_FIELD (copy, serialized_non_primitives, setup->serialized_non_primitives, error);
520 #undef COPY_FIELD
522 mono_domain_set_internal (caller_domain);
524 return copy;
527 static MonoAppDomain *
528 mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup, MonoError *error)
530 MonoClass *adclass;
531 MonoAppDomain *ad;
532 MonoDomain *data;
533 char *shadow_location;
535 mono_error_init (error);
537 adclass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomain");
539 /* FIXME: pin all those objects */
540 data = mono_domain_create();
542 ad = (MonoAppDomain *) mono_object_new_checked (data, adclass, error);
543 return_val_if_nok (error, NULL);
544 ad->data = data;
545 data->domain = ad;
546 data->friendly_name = g_strdup (friendly_name);
548 mono_profiler_appdomain_name (data, data->friendly_name);
550 if (!setup->application_base) {
551 /* Inherit from the root domain since MS.NET does this */
552 MonoDomain *root = mono_get_root_domain ();
553 if (root->setup->application_base) {
554 MonoString *s = mono_string_new_utf16_checked (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base), error);
555 mono_error_assert_ok (error); /* FIXME don't swallow the error */
556 MONO_OBJECT_SETREF (setup, application_base, s);
560 mono_context_init_checked (data, error);
561 return_val_if_nok (error, NULL);
563 data->setup = copy_app_domain_setup (data, setup, error);
564 if (!mono_error_ok (error)) {
565 g_free (data->friendly_name);
566 return NULL;
569 mono_domain_set_options_from_config (data);
570 add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
572 #ifndef DISABLE_SHADOW_COPY
573 /*FIXME, guard this for when the debugger is not running */
574 shadow_location = get_shadow_assembly_location_base (data, error);
575 if (!mono_error_ok (error)) {
576 g_free (data->friendly_name);
577 return NULL;
580 g_free (shadow_location);
581 #endif
583 create_domain_objects (data);
585 return ad;
589 * mono_domain_has_type_resolve:
590 * @domain: application domains being looked up
592 * Returns: TRUE if the AppDomain.TypeResolve field has been
593 * set.
595 gboolean
596 mono_domain_has_type_resolve (MonoDomain *domain)
598 static MonoClassField *field = NULL;
599 MonoObject *o;
601 if (field == NULL) {
602 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
603 g_assert (field);
606 /*pedump doesn't create an appdomin, so the domain object doesn't exist.*/
607 if (!domain->domain)
608 return FALSE;
610 mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
611 return o != NULL;
615 * mono_domain_try_type_resolve:
616 * @domain: application domainwhere the name where the type is going to be resolved
617 * @name: the name of the type to resolve or NULL.
618 * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
620 * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
621 * the assembly that matches name.
623 * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
625 * Returns: A MonoReflectionAssembly or NULL if not found
627 MonoReflectionAssembly *
628 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
630 MonoError error;
631 MonoReflectionAssembly *ret = mono_domain_try_type_resolve_checked (domain, name, tb, &error);
632 mono_error_cleanup (&error);
634 return ret;
637 MonoReflectionAssembly *
638 mono_domain_try_type_resolve_checked (MonoDomain *domain, char *name, MonoObject *tb, MonoError *error)
640 static MonoMethod *method = NULL;
641 MonoReflectionAssembly *ret;
642 MonoClass *klass;
643 void *params [1];
645 mono_error_init (error);
647 g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
649 if (method == NULL) {
650 klass = domain->domain->mbr.obj.vtable->klass;
651 g_assert (klass);
653 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
654 if (method == NULL) {
655 g_warning ("Method AppDomain.DoTypeResolve not found.\n");
656 return NULL;
660 if (name)
661 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
662 else
663 *params = tb;
665 ret = (MonoReflectionAssembly *) mono_runtime_invoke_checked (method, domain->domain, params, error);
666 return_val_if_nok (error, NULL);
668 return ret;
672 * mono_domain_owns_vtable_slot:
674 * Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
676 gboolean
677 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
679 gboolean res;
681 mono_domain_lock (domain);
682 res = mono_mempool_contains_addr (domain->mp, vtable_slot);
683 mono_domain_unlock (domain);
684 return res;
688 * mono_domain_set:
689 * @domain: domain
690 * @force: force setting.
692 * Set the current appdomain to @domain. If @force is set, set it even
693 * if it is being unloaded.
695 * Returns:
696 * TRUE on success;
697 * FALSE if the domain is unloaded
699 gboolean
700 mono_domain_set (MonoDomain *domain, gboolean force)
702 if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
703 return FALSE;
705 mono_domain_set_internal (domain);
707 return TRUE;
710 MonoObject *
711 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
713 MonoError error;
714 MonoDomain *add;
715 MonoObject *o;
716 char *str;
718 MONO_CHECK_ARG_NULL (name, NULL);
720 g_assert (ad);
721 add = ad->data;
722 g_assert (add);
724 str = mono_string_to_utf8_checked (name, &error);
725 if (mono_error_set_pending_exception (&error))
726 return NULL;
728 mono_domain_lock (add);
730 if (!strcmp (str, "APPBASE"))
731 o = (MonoObject *)add->setup->application_base;
732 else if (!strcmp (str, "APP_CONFIG_FILE"))
733 o = (MonoObject *)add->setup->configuration_file;
734 else if (!strcmp (str, "DYNAMIC_BASE"))
735 o = (MonoObject *)add->setup->dynamic_base;
736 else if (!strcmp (str, "APP_NAME"))
737 o = (MonoObject *)add->setup->application_name;
738 else if (!strcmp (str, "CACHE_BASE"))
739 o = (MonoObject *)add->setup->cache_path;
740 else if (!strcmp (str, "PRIVATE_BINPATH"))
741 o = (MonoObject *)add->setup->private_bin_path;
742 else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
743 o = (MonoObject *)add->setup->private_bin_path_probe;
744 else if (!strcmp (str, "SHADOW_COPY_DIRS"))
745 o = (MonoObject *)add->setup->shadow_copy_directories;
746 else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
747 o = (MonoObject *)add->setup->shadow_copy_files;
748 else
749 o = (MonoObject *)mono_g_hash_table_lookup (add->env, name);
751 mono_domain_unlock (add);
752 g_free (str);
754 if (!o)
755 return NULL;
757 return o;
760 void
761 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
763 MonoDomain *add;
765 MONO_CHECK_ARG_NULL (name,);
767 g_assert (ad);
768 add = ad->data;
769 g_assert (add);
771 mono_domain_lock (add);
773 mono_g_hash_table_insert (add->env, name, data);
775 mono_domain_unlock (add);
778 MonoAppDomainSetup *
779 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
781 g_assert (ad);
782 g_assert (ad->data);
784 return ad->data->setup;
787 MonoString *
788 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
790 g_assert (ad);
791 g_assert (ad->data);
793 return mono_string_new (ad->data, ad->data->friendly_name);
796 MonoAppDomain *
797 ves_icall_System_AppDomain_getCurDomain ()
799 MonoDomain *add = mono_domain_get ();
801 return add->domain;
804 MonoAppDomain *
805 ves_icall_System_AppDomain_getRootDomain ()
807 MonoDomain *root = mono_get_root_domain ();
809 return root->domain;
812 MonoBoolean
813 ves_icall_System_CLRConfig_CheckThrowUnobservedTaskExceptions ()
815 MonoDomain *domain = mono_domain_get ();
817 return domain->throw_unobserved_task_exceptions;
820 static char*
821 get_attribute_value (const gchar **attribute_names,
822 const gchar **attribute_values,
823 const char *att_name)
825 int n;
826 for (n = 0; attribute_names [n] != NULL; n++) {
827 if (strcmp (attribute_names [n], att_name) == 0)
828 return g_strdup (attribute_values [n]);
830 return NULL;
833 static void
834 start_element (GMarkupParseContext *context,
835 const gchar *element_name,
836 const gchar **attribute_names,
837 const gchar **attribute_values,
838 gpointer user_data,
839 GError **error)
841 RuntimeConfig *runtime_config = (RuntimeConfig *)user_data;
843 if (strcmp (element_name, "runtime") == 0) {
844 runtime_config->runtime_count++;
845 return;
848 if (strcmp (element_name, "assemblyBinding") == 0) {
849 runtime_config->assemblybinding_count++;
850 return;
853 if (runtime_config->runtime_count != 1)
854 return;
856 if (strcmp (element_name, "ThrowUnobservedTaskExceptions") == 0) {
857 const char *value = get_attribute_value (attribute_names, attribute_values, "enabled");
859 if (value && g_ascii_strcasecmp (value, "true") == 0)
860 runtime_config->domain->throw_unobserved_task_exceptions = TRUE;
863 if (runtime_config->assemblybinding_count != 1)
864 return;
866 if (strcmp (element_name, "probing") != 0)
867 return;
869 g_free (runtime_config->domain->private_bin_path);
870 runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath");
871 if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) {
872 g_free (runtime_config->domain->private_bin_path);
873 runtime_config->domain->private_bin_path = NULL;
874 return;
878 static void
879 end_element (GMarkupParseContext *context,
880 const gchar *element_name,
881 gpointer user_data,
882 GError **error)
884 RuntimeConfig *runtime_config = (RuntimeConfig *)user_data;
885 if (strcmp (element_name, "runtime") == 0)
886 runtime_config->runtime_count--;
887 else if (strcmp (element_name, "assemblyBinding") == 0)
888 runtime_config->assemblybinding_count--;
891 static void
892 parse_error (GMarkupParseContext *context, GError *error, gpointer user_data)
894 RuntimeConfig *state = (RuntimeConfig *)user_data;
895 const gchar *msg;
896 const gchar *filename;
898 filename = state && state->filename ? (gchar *) state->filename : "<unknown>";
899 msg = error && error->message ? error->message : "";
900 g_warning ("Error parsing %s: %s", filename, msg);
903 static const GMarkupParser
904 mono_parser = {
905 start_element,
906 end_element,
907 NULL,
908 NULL,
909 parse_error
912 void
913 mono_domain_set_options_from_config (MonoDomain *domain)
915 MonoError error;
916 gchar *config_file_name = NULL, *text = NULL, *config_file_path = NULL;
917 gsize len;
918 GMarkupParseContext *context;
919 RuntimeConfig runtime_config;
920 gint offset;
922 if (!domain || !domain->setup || !domain->setup->configuration_file)
923 return;
925 config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error);
926 if (!mono_error_ok (&error)) {
927 mono_error_cleanup (&error);
928 goto free_and_out;
931 config_file_path = mono_portability_find_file (config_file_name, TRUE);
932 if (!config_file_path)
933 config_file_path = config_file_name;
935 if (!g_file_get_contents (config_file_path, &text, &len, NULL))
936 goto free_and_out;
938 runtime_config.runtime_count = 0;
939 runtime_config.assemblybinding_count = 0;
940 runtime_config.domain = domain;
941 runtime_config.filename = config_file_path;
943 offset = 0;
944 if (len > 3 && text [0] == '\xef' && text [1] == (gchar) '\xbb' && text [2] == '\xbf')
945 offset = 3; /* Skip UTF-8 BOM */
947 context = g_markup_parse_context_new (&mono_parser, (GMarkupParseFlags)0, &runtime_config, NULL);
948 if (g_markup_parse_context_parse (context, text + offset, len - offset, NULL))
949 g_markup_parse_context_end_parse (context, NULL);
950 g_markup_parse_context_free (context);
952 free_and_out:
953 g_free (text);
954 if (config_file_name != config_file_path)
955 g_free (config_file_name);
956 g_free (config_file_path);
959 MonoAppDomain *
960 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
962 MonoError error;
963 MonoAppDomain *ad = NULL;
964 #ifdef DISABLE_APPDOMAINS
965 mono_error_set_not_supported (&error, "AppDomain creation is not supported on this runtime.");
966 #else
967 char *fname;
969 fname = mono_string_to_utf8_checked (friendly_name, &error);
970 if (mono_error_set_pending_exception (&error))
971 return NULL;
972 ad = mono_domain_create_appdomain_internal (fname, setup, &error);
974 g_free (fname);
975 #endif
976 mono_error_set_pending_exception (&error);
977 return ad;
980 MonoArray *
981 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
983 MonoError error;
984 MonoDomain *domain = ad->data;
985 MonoAssembly* ass;
986 MonoArray *res;
987 GSList *tmp;
988 int i;
989 GPtrArray *assemblies;
991 mono_error_init (&error);
994 * Make a copy of the list of assemblies because we can't hold the assemblies
995 * lock while creating objects etc.
997 assemblies = g_ptr_array_new ();
998 /* Need to skip internal assembly builders created by remoting */
999 mono_domain_assemblies_lock (domain);
1000 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
1001 ass = (MonoAssembly *)tmp->data;
1002 if (refonly != ass->ref_only)
1003 continue;
1004 if (ass->corlib_internal)
1005 continue;
1006 g_ptr_array_add (assemblies, ass);
1008 mono_domain_assemblies_unlock (domain);
1010 res = mono_array_new_checked (domain, mono_class_get_assembly_class (), assemblies->len, &error);
1011 if (!is_ok (&error))
1012 goto leave;
1013 for (i = 0; i < assemblies->len; ++i) {
1014 ass = (MonoAssembly *)g_ptr_array_index (assemblies, i);
1015 MonoReflectionAssembly *ass_obj = mono_assembly_get_object_checked (domain, ass, &error);
1016 if (!mono_error_ok (&error))
1017 goto leave;
1018 mono_array_setref (res, i, ass_obj);
1021 leave:
1022 g_ptr_array_free (assemblies, TRUE);
1023 if (!mono_error_ok (&error))
1024 mono_error_set_pending_exception (&error);
1025 return res;
1028 MonoReflectionAssembly *
1029 mono_try_assembly_resolve (MonoDomain *domain, MonoString *fname, MonoAssembly *requesting, gboolean refonly, MonoError *error)
1031 MonoReflectionAssembly *ret;
1032 MonoClass *klass;
1033 MonoMethod *method;
1034 MonoBoolean isrefonly;
1035 gpointer params [3];
1037 mono_error_init (error);
1039 if (mono_runtime_get_no_exec ())
1040 return NULL;
1042 g_assert (domain != NULL && fname != NULL);
1044 klass = domain->domain->mbr.obj.vtable->klass;
1045 g_assert (klass);
1047 method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
1048 if (method == NULL) {
1049 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
1050 return NULL;
1053 isrefonly = refonly ? 1 : 0;
1054 params [0] = fname;
1055 if (requesting) {
1056 params[1] = mono_assembly_get_object_checked (domain, requesting, error);
1057 return_val_if_nok (error, NULL);
1058 } else
1059 params [1] = NULL;
1060 params [2] = &isrefonly;
1062 ret = (MonoReflectionAssembly *) mono_runtime_invoke_checked (method, domain->domain, params, error);
1063 return_val_if_nok (error, NULL);
1065 return ret;
1068 MonoAssembly *
1069 mono_domain_assembly_postload_search (MonoAssemblyName *aname, MonoAssembly *requesting,
1070 gboolean refonly)
1072 MonoError error;
1073 MonoReflectionAssembly *assembly;
1074 MonoDomain *domain = mono_domain_get ();
1075 char *aname_str;
1076 MonoString *str;
1078 aname_str = mono_stringify_assembly_name (aname);
1080 /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
1081 str = mono_string_new (domain, aname_str);
1082 g_free (aname_str);
1083 if (!str) {
1084 return NULL;
1087 assembly = mono_try_assembly_resolve (domain, str, requesting, refonly, &error);
1088 mono_error_cleanup (&error);
1090 if (assembly)
1091 return assembly->assembly;
1092 else
1093 return NULL;
1097 * LOCKING: assumes assemblies_lock in the domain is already locked.
1099 static void
1100 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
1102 gint i;
1103 GSList *tmp;
1104 gboolean destroy_ht = FALSE;
1106 if (!ass->aname.name)
1107 return;
1109 if (!ht) {
1110 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
1111 destroy_ht = TRUE;
1114 /* FIXME: handle lazy loaded assemblies */
1115 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
1116 g_hash_table_insert (ht, tmp->data, tmp->data);
1118 if (!g_hash_table_lookup (ht, ass)) {
1119 mono_assembly_addref (ass);
1120 g_hash_table_insert (ht, ass, ass);
1121 domain->domain_assemblies = g_slist_append (domain->domain_assemblies, ass);
1122 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s[%p] added to domain %s, ref_count=%d", ass->aname.name, ass, domain->friendly_name, ass->ref_count);
1125 if (ass->image->references) {
1126 for (i = 0; ass->image->references [i] != NULL; i++) {
1127 if (ass->image->references [i] != REFERENCE_MISSING)
1128 if (!g_hash_table_lookup (ht, ass->image->references [i])) {
1129 add_assemblies_to_domain (domain, ass->image->references [i], ht);
1133 if (destroy_ht)
1134 g_hash_table_destroy (ht);
1137 static void
1138 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
1140 static MonoClassField *assembly_load_field;
1141 static MonoMethod *assembly_load_method;
1142 MonoError error;
1143 MonoDomain *domain = mono_domain_get ();
1144 MonoReflectionAssembly *ref_assembly;
1145 MonoClass *klass;
1146 gpointer load_value;
1147 void *params [1];
1149 if (!domain->domain)
1150 /* This can happen during startup */
1151 return;
1152 #ifdef ASSEMBLY_LOAD_DEBUG
1153 fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
1154 #endif
1155 klass = domain->domain->mbr.obj.vtable->klass;
1157 mono_domain_assemblies_lock (domain);
1158 add_assemblies_to_domain (domain, assembly, NULL);
1159 mono_domain_assemblies_unlock (domain);
1161 if (assembly_load_field == NULL) {
1162 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
1163 g_assert (assembly_load_field);
1166 mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
1167 if (load_value == NULL) {
1168 /* No events waiting to be triggered */
1169 return;
1172 ref_assembly = mono_assembly_get_object_checked (domain, assembly, &error);
1173 mono_error_assert_ok (&error);
1175 if (assembly_load_method == NULL) {
1176 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
1177 g_assert (assembly_load_method);
1180 *params = ref_assembly;
1182 mono_runtime_invoke_checked (assembly_load_method, domain->domain, params, &error);
1183 mono_error_cleanup (&error);
1187 * LOCKING: Acquires the domain assemblies lock.
1189 static void
1190 set_domain_search_path (MonoDomain *domain)
1192 MonoError error;
1193 MonoAppDomainSetup *setup;
1194 gchar **tmp;
1195 gchar *search_path = NULL;
1196 gint i;
1197 gint npaths = 0;
1198 gchar **pvt_split = NULL;
1199 GError *gerror = NULL;
1200 gint appbaselen = -1;
1203 * We use the low-level domain assemblies lock, since this is called from
1204 * assembly loads hooks, which means this thread might hold the loader lock.
1206 mono_domain_assemblies_lock (domain);
1208 if (!domain->setup) {
1209 mono_domain_assemblies_unlock (domain);
1210 return;
1213 if ((domain->search_path != NULL) && !domain->setup->path_changed) {
1214 mono_domain_assemblies_unlock (domain);
1215 return;
1217 setup = domain->setup;
1218 if (!setup->application_base) {
1219 mono_domain_assemblies_unlock (domain);
1220 return; /* Must set application base to get private path working */
1223 npaths++;
1225 if (setup->private_bin_path) {
1226 search_path = mono_string_to_utf8_checked (setup->private_bin_path, &error);
1227 if (!mono_error_ok (&error)) { /*FIXME maybe we should bubble up the error.*/
1228 g_warning ("Could not decode AppDomain search path since it contains invalid characters");
1229 mono_error_cleanup (&error);
1230 mono_domain_assemblies_unlock (domain);
1231 return;
1235 if (domain->private_bin_path) {
1236 if (search_path == NULL)
1237 search_path = domain->private_bin_path;
1238 else {
1239 gchar *tmp2 = search_path;
1240 search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL);
1241 g_free (tmp2);
1245 if (search_path) {
1247 * As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of
1248 * directories relative to ApplicationBase separated by semicolons (see
1249 * http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx)
1250 * The loop below copes with the fact that some Unix applications may use ':' (or
1251 * System.IO.Path.PathSeparator) as the path search separator. We replace it with
1252 * ';' for the subsequent split.
1254 * The issue was reported in bug #81446
1257 #ifndef TARGET_WIN32
1258 gint slen;
1260 slen = strlen (search_path);
1261 for (i = 0; i < slen; i++)
1262 if (search_path [i] == ':')
1263 search_path [i] = ';';
1264 #endif
1266 pvt_split = g_strsplit (search_path, ";", 1000);
1267 g_free (search_path);
1268 for (tmp = pvt_split; *tmp; tmp++, npaths++);
1271 if (!npaths) {
1272 if (pvt_split)
1273 g_strfreev (pvt_split);
1275 * Don't do this because the first time is called, the domain
1276 * setup is not finished.
1278 * domain->search_path = g_malloc (sizeof (char *));
1279 * domain->search_path [0] = NULL;
1281 mono_domain_assemblies_unlock (domain);
1282 return;
1285 if (domain->search_path)
1286 g_strfreev (domain->search_path);
1288 tmp = (gchar **)g_malloc ((npaths + 1) * sizeof (gchar *));
1289 tmp [npaths] = NULL;
1291 *tmp = mono_string_to_utf8_checked (setup->application_base, &error);
1292 if (!mono_error_ok (&error)) {
1293 mono_error_cleanup (&error);
1294 g_strfreev (pvt_split);
1295 g_free (tmp);
1297 mono_domain_assemblies_unlock (domain);
1298 return;
1301 domain->search_path = tmp;
1303 /* FIXME: is this needed? */
1304 if (strncmp (*tmp, "file://", 7) == 0) {
1305 gchar *file = *tmp;
1306 gchar *uri = *tmp;
1307 gchar *tmpuri;
1309 if (uri [7] != '/')
1310 uri = g_strdup_printf ("file:///%s", uri + 7);
1312 tmpuri = uri;
1313 uri = mono_escape_uri_string (tmpuri);
1314 *tmp = g_filename_from_uri (uri, NULL, &gerror);
1315 g_free (uri);
1317 if (tmpuri != file)
1318 g_free (tmpuri);
1320 if (gerror != NULL) {
1321 g_warning ("%s\n", gerror->message);
1322 g_error_free (gerror);
1323 *tmp = file;
1324 } else {
1325 g_free (file);
1329 for (i = 1; pvt_split && i < npaths; i++) {
1330 if (g_path_is_absolute (pvt_split [i - 1])) {
1331 tmp [i] = g_strdup (pvt_split [i - 1]);
1332 } else {
1333 tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
1336 if (strchr (tmp [i], '.')) {
1337 gchar *reduced;
1338 gchar *freeme;
1340 reduced = mono_path_canonicalize (tmp [i]);
1341 if (appbaselen == -1)
1342 appbaselen = strlen (tmp [0]);
1344 if (strncmp (tmp [0], reduced, appbaselen)) {
1345 g_free (reduced);
1346 g_free (tmp [i]);
1347 tmp [i] = g_strdup ("");
1348 continue;
1351 freeme = tmp [i];
1352 tmp [i] = reduced;
1353 g_free (freeme);
1357 if (setup->private_bin_path_probe != NULL) {
1358 g_free (tmp [0]);
1359 tmp [0] = g_strdup ("");
1362 domain->setup->path_changed = FALSE;
1364 g_strfreev (pvt_split);
1366 mono_domain_assemblies_unlock (domain);
1369 #ifdef DISABLE_SHADOW_COPY
1370 gboolean
1371 mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
1373 return FALSE;
1376 char *
1377 mono_make_shadow_copy (const char *filename, MonoError *error)
1379 mono_error_init (error);
1380 return (char *) filename;
1382 #else
1383 static gboolean
1384 shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
1386 guint16 *orig, *dest;
1387 gboolean copy_result;
1389 strcpy (src + srclen - tail_len, extension);
1391 if (IS_PORTABILITY_CASE) {
1392 gchar *file = mono_portability_find_file (src, TRUE);
1394 if (file == NULL)
1395 return TRUE;
1397 g_free (file);
1398 } else if (!g_file_test (src, G_FILE_TEST_IS_REGULAR)) {
1399 return TRUE;
1402 orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
1404 strcpy (target + targetlen - tail_len, extension);
1405 dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
1407 DeleteFile (dest);
1408 copy_result = CopyFile (orig, dest, FALSE);
1410 /* Fix for bug #556884 - make sure the files have the correct mode so that they can be
1411 * overwritten when updated in their original locations. */
1412 if (copy_result)
1413 copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
1415 g_free (orig);
1416 g_free (dest);
1418 return copy_result;
1421 static gint32
1422 get_cstring_hash (const char *str)
1424 int len, i;
1425 const char *p;
1426 gint32 h = 0;
1428 if (!str || !str [0])
1429 return 0;
1431 len = strlen (str);
1432 p = str;
1433 for (i = 0; i < len; i++) {
1434 h = (h << 5) - h + *p;
1435 p++;
1438 return h;
1442 * Returned memory is malloc'd. Called must free it
1444 static char *
1445 get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error)
1447 MonoAppDomainSetup *setup;
1448 char *cache_path, *appname;
1449 char *userdir;
1450 char *location;
1452 mono_error_init (error);
1454 setup = domain->setup;
1455 if (setup->cache_path != NULL && setup->application_name != NULL) {
1456 cache_path = mono_string_to_utf8_checked (setup->cache_path, error);
1457 return_val_if_nok (error, NULL);
1459 #ifndef TARGET_WIN32
1461 gint i;
1462 for (i = strlen (cache_path) - 1; i >= 0; i--)
1463 if (cache_path [i] == '\\')
1464 cache_path [i] = '/';
1466 #endif
1468 appname = mono_string_to_utf8_checked (setup->application_name, error);
1469 if (!mono_error_ok (error)) {
1470 g_free (cache_path);
1471 return NULL;
1474 location = g_build_filename (cache_path, appname, "assembly", "shadow", NULL);
1475 g_free (appname);
1476 g_free (cache_path);
1477 } else {
1478 userdir = g_strdup_printf ("%s-mono-cachepath", g_get_user_name ());
1479 location = g_build_filename (g_get_tmp_dir (), userdir, "assembly", "shadow", NULL);
1480 g_free (userdir);
1482 return location;
1485 static char *
1486 get_shadow_assembly_location (const char *filename, MonoError *error)
1488 gint32 hash = 0, hash2 = 0;
1489 char name_hash [9];
1490 char path_hash [30];
1491 char *bname = g_path_get_basename (filename);
1492 char *dirname = g_path_get_dirname (filename);
1493 char *location, *tmploc;
1494 MonoDomain *domain = mono_domain_get ();
1496 mono_error_init (error);
1498 hash = get_cstring_hash (bname);
1499 hash2 = get_cstring_hash (dirname);
1500 g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
1501 g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial);
1502 tmploc = get_shadow_assembly_location_base (domain, error);
1503 if (!mono_error_ok (error)) {
1504 g_free (bname);
1505 g_free (dirname);
1506 return NULL;
1509 location = g_build_filename (tmploc, name_hash, path_hash, bname, NULL);
1510 g_free (tmploc);
1511 g_free (bname);
1512 g_free (dirname);
1513 return location;
1516 static gboolean
1517 ensure_directory_exists (const char *filename)
1519 #ifdef HOST_WIN32
1520 gchar *dir_utf8 = g_path_get_dirname (filename);
1521 gunichar2 *p;
1522 gunichar2 *dir_utf16 = NULL;
1523 int retval;
1525 if (!dir_utf8 || !dir_utf8 [0])
1526 return FALSE;
1528 dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
1529 g_free (dir_utf8);
1531 if (!dir_utf16)
1532 return FALSE;
1534 p = dir_utf16;
1536 /* make life easy and only use one directory seperator */
1537 while (*p != '\0')
1539 if (*p == '/')
1540 *p = '\\';
1541 p++;
1544 p = dir_utf16;
1546 /* get past C:\ )*/
1547 while (*p++ != '\\')
1551 while (1) {
1552 BOOL bRet = FALSE;
1553 p = wcschr (p, '\\');
1554 if (p)
1555 *p = '\0';
1556 retval = _wmkdir (dir_utf16);
1557 if (retval != 0 && errno != EEXIST) {
1558 g_free (dir_utf16);
1559 return FALSE;
1561 if (!p)
1562 break;
1563 *p++ = '\\';
1566 g_free (dir_utf16);
1567 return TRUE;
1568 #else
1569 char *p;
1570 gchar *dir = g_path_get_dirname (filename);
1571 int retval;
1572 struct stat sbuf;
1574 if (!dir || !dir [0]) {
1575 g_free (dir);
1576 return FALSE;
1579 if (stat (dir, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
1580 g_free (dir);
1581 return TRUE;
1584 p = dir;
1585 while (*p == '/')
1586 p++;
1588 while (1) {
1589 p = strchr (p, '/');
1590 if (p)
1591 *p = '\0';
1592 retval = mkdir (dir, 0777);
1593 if (retval != 0 && errno != EEXIST) {
1594 g_free (dir);
1595 return FALSE;
1597 if (!p)
1598 break;
1599 *p++ = '/';
1602 g_free (dir);
1603 return TRUE;
1604 #endif
1607 static gboolean
1608 private_file_needs_copying (const char *src, struct stat *sbuf_src, char *dest)
1610 struct stat sbuf_dest;
1611 gchar *stat_src;
1612 gchar *real_src = mono_portability_find_file (src, TRUE);
1614 if (!real_src)
1615 stat_src = (gchar*)src;
1616 else
1617 stat_src = real_src;
1619 if (stat (stat_src, sbuf_src) == -1) {
1620 time_t tnow = time (NULL);
1622 if (real_src)
1623 g_free (real_src);
1625 memset (sbuf_src, 0, sizeof (*sbuf_src));
1626 sbuf_src->st_mtime = tnow;
1627 sbuf_src->st_atime = tnow;
1628 return TRUE;
1631 if (real_src)
1632 g_free (real_src);
1634 if (stat (dest, &sbuf_dest) == -1)
1635 return TRUE;
1637 if (sbuf_src->st_size == sbuf_dest.st_size &&
1638 sbuf_src->st_mtime == sbuf_dest.st_mtime)
1639 return FALSE;
1641 return TRUE;
1644 static gboolean
1645 shadow_copy_create_ini (const char *shadow, const char *filename)
1647 char *dir_name;
1648 char *ini_file;
1649 guint16 *u16_ini;
1650 gboolean result;
1651 guint32 n;
1652 HANDLE *handle;
1653 gchar *full_path;
1655 dir_name = g_path_get_dirname (shadow);
1656 ini_file = g_build_filename (dir_name, "__AssemblyInfo__.ini", NULL);
1657 g_free (dir_name);
1658 if (g_file_test (ini_file, G_FILE_TEST_IS_REGULAR)) {
1659 g_free (ini_file);
1660 return TRUE;
1663 u16_ini = g_utf8_to_utf16 (ini_file, strlen (ini_file), NULL, NULL, NULL);
1664 g_free (ini_file);
1665 if (!u16_ini) {
1666 return FALSE;
1668 handle = (void **)CreateFile (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
1669 NULL, CREATE_NEW, FileAttributes_Normal, NULL);
1670 g_free (u16_ini);
1671 if (handle == INVALID_HANDLE_VALUE) {
1672 return FALSE;
1675 full_path = mono_path_resolve_symlinks (filename);
1676 result = WriteFile (handle, full_path, strlen (full_path), &n, NULL);
1677 g_free (full_path);
1678 CloseHandle (handle);
1679 return result;
1682 gboolean
1683 mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
1685 MonoError error;
1686 MonoAppDomainSetup *setup;
1687 gchar *all_dirs;
1688 gchar **dir_ptr;
1689 gchar **directories;
1690 gchar *shadow_status_string;
1691 gchar *base_dir;
1692 gboolean shadow_enabled;
1693 gboolean found = FALSE;
1695 if (domain == NULL)
1696 return FALSE;
1698 setup = domain->setup;
1699 if (setup == NULL || setup->shadow_copy_files == NULL)
1700 return FALSE;
1702 shadow_status_string = mono_string_to_utf8_checked (setup->shadow_copy_files, &error);
1703 if (!mono_error_ok (&error)) {
1704 mono_error_cleanup (&error);
1705 return FALSE;
1707 shadow_enabled = !g_ascii_strncasecmp (shadow_status_string, "true", 4);
1708 g_free (shadow_status_string);
1710 if (!shadow_enabled)
1711 return FALSE;
1713 if (setup->shadow_copy_directories == NULL)
1714 return TRUE;
1716 /* Is dir_name a shadow_copy destination already? */
1717 base_dir = get_shadow_assembly_location_base (domain, &error);
1718 if (!mono_error_ok (&error)) {
1719 mono_error_cleanup (&error);
1720 return FALSE;
1723 if (strstr (dir_name, base_dir)) {
1724 g_free (base_dir);
1725 return TRUE;
1727 g_free (base_dir);
1729 all_dirs = mono_string_to_utf8_checked (setup->shadow_copy_directories, &error);
1730 if (!mono_error_ok (&error)) {
1731 mono_error_cleanup (&error);
1732 return FALSE;
1735 directories = g_strsplit (all_dirs, G_SEARCHPATH_SEPARATOR_S, 1000);
1736 dir_ptr = directories;
1737 while (*dir_ptr) {
1738 if (**dir_ptr != '\0' && !strcmp (*dir_ptr, dir_name)) {
1739 found = TRUE;
1740 break;
1742 dir_ptr++;
1744 g_strfreev (directories);
1745 g_free (all_dirs);
1746 return found;
1750 This function raises exceptions so it can cause as sorts of nasty stuff if called
1751 while holding a lock.
1752 Returns old file name if shadow copy is disabled, new shadow copy file name if successful
1753 or NULL if source file not found.
1754 FIXME bubble up the error instead of raising it here
1756 char *
1757 mono_make_shadow_copy (const char *filename, MonoError *oerror)
1759 MonoError error;
1760 gchar *sibling_source, *sibling_target;
1761 gint sibling_source_len, sibling_target_len;
1762 guint16 *orig, *dest;
1763 guint32 attrs;
1764 char *shadow;
1765 gboolean copy_result;
1766 struct stat src_sbuf;
1767 struct utimbuf utbuf;
1768 char *dir_name = g_path_get_dirname (filename);
1769 MonoDomain *domain = mono_domain_get ();
1770 char *shadow_dir;
1772 mono_error_init (oerror);
1774 set_domain_search_path (domain);
1776 if (!mono_is_shadow_copy_enabled (domain, dir_name)) {
1777 g_free (dir_name);
1778 return (char *) filename;
1781 /* Is dir_name a shadow_copy destination already? */
1782 shadow_dir = get_shadow_assembly_location_base (domain, &error);
1783 if (!mono_error_ok (&error)) {
1784 mono_error_cleanup (&error);
1785 g_free (dir_name);
1786 mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in shadow directory name).");
1787 return NULL;
1790 if (strstr (dir_name, shadow_dir)) {
1791 g_free (shadow_dir);
1792 g_free (dir_name);
1793 return (char *) filename;
1795 g_free (shadow_dir);
1796 g_free (dir_name);
1798 shadow = get_shadow_assembly_location (filename, &error);
1799 if (!mono_error_ok (&error)) {
1800 mono_error_cleanup (&error);
1801 mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in file name).");
1802 return NULL;
1805 if (ensure_directory_exists (shadow) == FALSE) {
1806 g_free (shadow);
1807 mono_error_set_execution_engine (oerror, "Failed to create shadow copy (ensure directory exists).");
1808 return NULL;
1811 if (!private_file_needs_copying (filename, &src_sbuf, shadow))
1812 return (char*) shadow;
1814 orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
1815 dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
1816 DeleteFile (dest);
1818 /* Fix for bug #17066 - make sure we can read the file. if not then don't error but rather
1819 * let the assembly fail to load. This ensures you can do Type.GetType("NS.T, NonExistantAssembly)
1820 * and not have it runtime error" */
1821 attrs = GetFileAttributes (orig);
1822 if (attrs == INVALID_FILE_ATTRIBUTES) {
1823 g_free (shadow);
1824 return (char *)filename;
1827 copy_result = CopyFile (orig, dest, FALSE);
1829 /* Fix for bug #556884 - make sure the files have the correct mode so that they can be
1830 * overwritten when updated in their original locations. */
1831 if (copy_result)
1832 copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
1834 g_free (dest);
1835 g_free (orig);
1837 if (copy_result == FALSE) {
1838 g_free (shadow);
1840 /* Fix for bug #17251 - if file not found try finding assembly by other means (it is not fatal error) */
1841 if (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND)
1842 return NULL; /* file not found, shadow copy failed */
1844 mono_error_set_execution_engine (oerror, "Failed to create shadow copy (CopyFile).");
1845 return NULL;
1848 /* attempt to copy .mdb, .config if they exist */
1849 sibling_source = g_strconcat (filename, ".config", NULL);
1850 sibling_source_len = strlen (sibling_source);
1851 sibling_target = g_strconcat (shadow, ".config", NULL);
1852 sibling_target_len = strlen (sibling_target);
1854 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
1855 if (copy_result == TRUE)
1856 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
1858 g_free (sibling_source);
1859 g_free (sibling_target);
1861 if (copy_result == FALSE) {
1862 g_free (shadow);
1863 mono_error_set_execution_engine (oerror, "Failed to create shadow copy of sibling data (CopyFile).");
1864 return NULL;
1867 /* Create a .ini file containing the original assembly location */
1868 if (!shadow_copy_create_ini (shadow, filename)) {
1869 g_free (shadow);
1870 mono_error_set_execution_engine (oerror, "Failed to create shadow copy .ini file.");
1871 return NULL;
1874 utbuf.actime = src_sbuf.st_atime;
1875 utbuf.modtime = src_sbuf.st_mtime;
1876 utime (shadow, &utbuf);
1878 return shadow;
1880 #endif /* DISABLE_SHADOW_COPY */
1882 MonoDomain *
1883 mono_domain_from_appdomain (MonoAppDomain *appdomain)
1885 if (appdomain == NULL)
1886 return NULL;
1888 return appdomain->data;
1891 static gboolean
1892 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
1893 const gchar *path3, const gchar *path4,
1894 gboolean refonly, gboolean is_private)
1896 gchar *fullpath;
1897 gboolean found = FALSE;
1899 *assembly = NULL;
1900 fullpath = g_build_filename (path1, path2, path3, path4, NULL);
1902 if (IS_PORTABILITY_SET) {
1903 gchar *new_fullpath = mono_portability_find_file (fullpath, TRUE);
1904 if (new_fullpath) {
1905 g_free (fullpath);
1906 fullpath = new_fullpath;
1907 found = TRUE;
1909 } else
1910 found = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR);
1912 if (found)
1913 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
1915 g_free (fullpath);
1916 return (*assembly != NULL);
1919 static MonoAssembly *
1920 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
1922 MonoAssembly *result = NULL;
1923 gchar **path;
1924 gchar *filename;
1925 const gchar *local_culture;
1926 gint len;
1927 gboolean is_private = FALSE;
1929 if (!culture || *culture == '\0') {
1930 local_culture = "";
1931 } else {
1932 local_culture = culture;
1935 filename = g_strconcat (name, ".dll", NULL);
1936 len = strlen (filename);
1938 for (path = search_path; *path; path++) {
1939 if (**path == '\0') {
1940 is_private = TRUE;
1941 continue; /* Ignore empty ApplicationBase */
1944 /* See test cases in bug #58992 and bug #57710 */
1945 /* 1st try: [culture]/[name].dll (culture may be empty) */
1946 strcpy (filename + len - 4, ".dll");
1947 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1948 break;
1950 /* 2nd try: [culture]/[name].exe (culture may be empty) */
1951 strcpy (filename + len - 4, ".exe");
1952 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1953 break;
1955 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
1956 strcpy (filename + len - 4, ".dll");
1957 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1958 break;
1960 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
1961 strcpy (filename + len - 4, ".exe");
1962 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1963 break;
1966 g_free (filename);
1967 return result;
1971 * Try loading the assembly from ApplicationBase and PrivateBinPath
1972 * and then from assemblies_path if any.
1973 * LOCKING: This is called from the assembly loading code, which means the caller
1974 * might hold the loader lock. Thus, this function must not acquire the domain lock.
1976 static MonoAssembly *
1977 mono_domain_assembly_preload (MonoAssemblyName *aname,
1978 gchar **assemblies_path,
1979 gpointer user_data)
1981 MonoDomain *domain = mono_domain_get ();
1982 MonoAssembly *result = NULL;
1983 gboolean refonly = GPOINTER_TO_UINT (user_data);
1985 set_domain_search_path (domain);
1987 if (domain->search_path && domain->search_path [0] != NULL) {
1988 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
1991 if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
1992 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
1995 return result;
1999 * Check whenever a given assembly was already loaded in the current appdomain.
2001 static MonoAssembly *
2002 mono_domain_assembly_search (MonoAssemblyName *aname,
2003 gpointer user_data)
2005 MonoDomain *domain = mono_domain_get ();
2006 GSList *tmp;
2007 MonoAssembly *ass;
2008 gboolean refonly = GPOINTER_TO_UINT (user_data);
2010 mono_domain_assemblies_lock (domain);
2011 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
2012 ass = (MonoAssembly *)tmp->data;
2013 /* Dynamic assemblies can't match here in MS.NET */
2014 if (assembly_is_dynamic (ass) || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
2015 continue;
2017 mono_domain_assemblies_unlock (domain);
2018 return ass;
2020 mono_domain_assemblies_unlock (domain);
2022 return NULL;
2025 MonoReflectionAssembly *
2026 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
2028 MonoError error;
2029 MonoReflectionAssembly *result;
2030 MonoDomain *domain = mono_domain_get ();
2031 char *name, *filename;
2032 MonoImageOpenStatus status = MONO_IMAGE_OK;
2033 MonoAssembly *ass;
2035 if (fname == NULL) {
2036 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
2037 mono_set_pending_exception (exc);
2038 return NULL;
2041 name = filename = mono_string_to_utf8_checked (fname, &error);
2042 if (mono_error_set_pending_exception (&error))
2043 return NULL;
2045 ass = mono_assembly_open_full (filename, &status, refOnly);
2047 if (!ass) {
2048 MonoException *exc;
2050 if (status == MONO_IMAGE_IMAGE_INVALID)
2051 exc = mono_get_exception_bad_image_format2 (NULL, fname);
2052 else
2053 exc = mono_get_exception_file_not_found2 (NULL, fname);
2054 g_free (name);
2055 mono_set_pending_exception (exc);
2056 return NULL;
2059 g_free (name);
2061 result = mono_assembly_get_object_checked (domain, ass, &error);
2062 if (!result)
2063 mono_error_set_pending_exception (&error);
2064 return result;
2067 MonoReflectionAssembly *
2068 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad,
2069 MonoArray *raw_assembly,
2070 MonoArray *raw_symbol_store, MonoObject *evidence,
2071 MonoBoolean refonly)
2073 MonoError error;
2074 MonoAssembly *ass;
2075 MonoReflectionAssembly *refass = NULL;
2076 MonoDomain *domain = ad->data;
2077 MonoImageOpenStatus status;
2078 guint32 raw_assembly_len = mono_array_length (raw_assembly);
2079 MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
2081 if (!image) {
2082 mono_set_pending_exception (mono_get_exception_bad_image_format (""));
2083 return NULL;
2086 if (raw_symbol_store != NULL)
2087 mono_debug_open_image_from_memory (image, mono_array_addr (raw_symbol_store, guint8, 0), mono_array_length (raw_symbol_store));
2089 ass = mono_assembly_load_from_full (image, "", &status, refonly);
2092 if (!ass) {
2093 mono_image_close (image);
2094 mono_set_pending_exception (mono_get_exception_bad_image_format (""));
2095 return NULL;
2098 refass = mono_assembly_get_object_checked (domain, ass, &error);
2099 if (!refass)
2100 mono_error_set_pending_exception (&error);
2101 else
2102 MONO_OBJECT_SETREF (refass, evidence, evidence);
2103 return refass;
2106 MonoReflectionAssembly *
2107 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
2109 MonoError error;
2110 MonoDomain *domain = ad->data;
2111 MonoImageOpenStatus status = MONO_IMAGE_OK;
2112 MonoAssembly *ass;
2113 MonoAssemblyName aname;
2114 MonoReflectionAssembly *refass = NULL;
2115 gchar *name;
2116 gboolean parsed;
2118 g_assert (assRef);
2120 name = mono_string_to_utf8_checked (assRef, &error);
2121 if (mono_error_set_pending_exception (&error))
2122 return NULL;
2123 parsed = mono_assembly_name_parse (name, &aname);
2124 g_free (name);
2126 if (!parsed) {
2127 /* This is a parse error... */
2128 if (!refOnly) {
2129 refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly, &error);
2130 if (!mono_error_ok (&error)) {
2131 mono_error_set_pending_exception (&error);
2132 return NULL;
2135 return refass;
2138 ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly);
2139 mono_assembly_name_free (&aname);
2141 if (!ass) {
2142 /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */
2143 if (!refOnly) {
2144 refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly, &error);
2145 if (!mono_error_ok (&error)) {
2146 mono_error_set_pending_exception (&error);
2147 return NULL;
2150 else
2151 refass = NULL;
2152 if (!refass) {
2153 return NULL;
2157 if (refass == NULL)
2158 refass = mono_assembly_get_object_checked (domain, ass, &error);
2160 if (refass == NULL)
2161 mono_error_set_pending_exception (&error);
2162 else
2163 MONO_OBJECT_SETREF (refass, evidence, evidence);
2164 return refass;
2167 void
2168 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
2170 MonoException *exc = NULL;
2171 MonoDomain * domain = mono_domain_get_by_id (domain_id);
2173 if (NULL == domain) {
2174 mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
2175 mono_set_pending_exception (exc);
2176 return;
2179 if (domain == mono_get_root_domain ()) {
2180 mono_set_pending_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
2181 return;
2185 * Unloading seems to cause problems when running NUnit/NAnt, hence
2186 * this workaround.
2188 if (g_getenv ("MONO_NO_UNLOAD"))
2189 return;
2190 #ifdef __native_client__
2191 return;
2192 #endif
2194 mono_domain_try_unload (domain, (MonoObject**)&exc);
2195 if (exc)
2196 mono_set_pending_exception (exc);
2199 gboolean
2200 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
2202 MonoDomain *domain = mono_domain_get_by_id (domain_id);
2204 if (!domain)
2205 return TRUE;
2207 return mono_domain_is_unloading (domain);
2210 void
2211 ves_icall_System_AppDomain_DoUnhandledException (MonoException *exc)
2213 mono_unhandled_exception ((MonoObject*) exc);
2216 gint32
2217 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad,
2218 MonoReflectionAssembly *refass, MonoArray *args)
2220 MonoError error;
2221 MonoImage *image;
2222 MonoMethod *method;
2224 g_assert (refass);
2225 image = refass->assembly->image;
2226 g_assert (image);
2228 method = mono_get_method_checked (image, mono_image_get_entry_point (image), NULL, NULL, &error);
2230 if (!method)
2231 g_error ("No entry point method found in %s due to %s", image->name, mono_error_get_message (&error));
2233 if (!args) {
2234 args = (MonoArray *) mono_array_new_checked (ad->data, mono_defaults.string_class, 0, &error);
2235 mono_error_assert_ok (&error);
2238 return mono_runtime_exec_main (method, (MonoArray *)args, NULL);
2241 gint32
2242 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad)
2244 return ad->data->domain_id;
2247 MonoAppDomain *
2248 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
2250 MonoDomain *old_domain = mono_domain_get();
2252 if (!mono_domain_set (ad->data, FALSE)) {
2253 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2254 return NULL;
2257 return old_domain->domain;
2260 MonoAppDomain *
2261 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
2263 MonoDomain *current_domain = mono_domain_get ();
2264 MonoDomain *domain = mono_domain_get_by_id (domainid);
2266 if (!domain || !mono_domain_set (domain, FALSE)) {
2267 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2268 return NULL;
2271 return current_domain->domain;
2274 void
2275 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
2277 mono_thread_push_appdomain_ref (ad->data);
2280 void
2281 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
2283 MonoDomain *domain = mono_domain_get_by_id (domain_id);
2285 if (!domain) {
2287 * Raise an exception to prevent the managed code from executing a pop
2288 * later.
2290 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2291 return;
2294 mono_thread_push_appdomain_ref (domain);
2297 void
2298 ves_icall_System_AppDomain_InternalPopDomainRef (void)
2300 mono_thread_pop_appdomain_ref ();
2303 MonoAppContext *
2304 ves_icall_System_AppDomain_InternalGetContext ()
2306 return mono_context_get ();
2309 MonoAppContext *
2310 ves_icall_System_AppDomain_InternalGetDefaultContext ()
2312 return mono_domain_get ()->default_context;
2315 MonoAppContext *
2316 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
2318 MonoAppContext *old_context = mono_context_get ();
2320 mono_context_set (mc);
2322 return old_context;
2325 MonoString *
2326 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
2328 MonoDomain* mono_root_domain = mono_get_root_domain ();
2329 mono_domain_lock (mono_root_domain);
2330 if (process_guid_set) {
2331 mono_domain_unlock (mono_root_domain);
2332 MonoError error;
2333 MonoString *res = NULL;
2334 res = mono_string_new_utf16_checked (mono_domain_get (), process_guid, sizeof(process_guid)/2, &error);
2335 mono_error_set_pending_exception (&error);
2336 return res;
2338 memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
2339 process_guid_set = TRUE;
2340 mono_domain_unlock (mono_root_domain);
2341 return newguid;
2344 gboolean
2345 mono_domain_is_unloading (MonoDomain *domain)
2347 if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
2348 return TRUE;
2349 else
2350 return FALSE;
2353 static void
2354 clear_cached_vtable (MonoVTable *vtable)
2356 MonoClass *klass = vtable->klass;
2357 MonoDomain *domain = vtable->domain;
2358 MonoClassRuntimeInfo *runtime_info;
2359 void *data;
2361 runtime_info = klass->runtime_info;
2362 if (runtime_info && runtime_info->max_domain >= domain->domain_id)
2363 runtime_info->domain_vtables [domain->domain_id] = NULL;
2364 if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
2365 mono_gc_free_fixed (data);
2368 static G_GNUC_UNUSED void
2369 zero_static_data (MonoVTable *vtable)
2371 MonoClass *klass = vtable->klass;
2372 void *data;
2374 if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
2375 mono_gc_bzero_aligned (data, mono_class_data_size (klass));
2378 typedef struct unload_data {
2379 gboolean done;
2380 MonoDomain *domain;
2381 char *failure_reason;
2382 gint32 refcount;
2383 } unload_data;
2385 static void
2386 unload_data_unref (unload_data *data)
2388 gint32 count;
2389 do {
2390 mono_atomic_load_acquire (count, gint32, &data->refcount);
2391 g_assert (count >= 1 && count <= 2);
2392 if (count == 1) {
2393 g_free (data);
2394 return;
2396 } while (InterlockedCompareExchange (&data->refcount, count - 1, count) != count);
2399 static void
2400 deregister_reflection_info_roots_from_list (MonoImage *image)
2402 GSList *list = image->reflection_info_unregister_classes;
2404 while (list) {
2405 MonoClass *klass = (MonoClass *)list->data;
2407 mono_class_free_ref_info (klass);
2409 list = list->next;
2412 image->reflection_info_unregister_classes = NULL;
2415 static void
2416 deregister_reflection_info_roots (MonoDomain *domain)
2418 GSList *list;
2420 mono_domain_assemblies_lock (domain);
2421 for (list = domain->domain_assemblies; list; list = list->next) {
2422 MonoAssembly *assembly = (MonoAssembly *)list->data;
2423 MonoImage *image = assembly->image;
2424 int i;
2427 * No need to take the image lock here since dynamic images are appdomain bound and
2428 * at this point the mutator is gone. Taking the image lock here would mean
2429 * promoting it from a simple lock to a complex lock, which we better avoid if
2430 * possible.
2432 if (image_is_dynamic (image))
2433 deregister_reflection_info_roots_from_list (image);
2435 for (i = 0; i < image->module_count; ++i) {
2436 MonoImage *module = image->modules [i];
2437 if (module && image_is_dynamic (module))
2438 deregister_reflection_info_roots_from_list (module);
2441 mono_domain_assemblies_unlock (domain);
2444 static guint32 WINAPI
2445 unload_thread_main (void *arg)
2447 MonoError error;
2448 unload_data *data = (unload_data*)arg;
2449 MonoDomain *domain = data->domain;
2450 MonoThread *thread;
2451 int i;
2453 /* Have to attach to the runtime so shutdown can wait for this thread */
2454 /* Force it to be attached to avoid racing during shutdown. */
2455 thread = mono_thread_attach_full (mono_get_root_domain (), TRUE);
2457 mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Domain unloader"), TRUE, &error);
2458 if (!is_ok (&error)) {
2459 data->failure_reason = g_strdup (mono_error_get_message (&error));
2460 mono_error_cleanup (&error);
2461 goto failure;
2465 * FIXME: Abort our parent thread last, so we can return a failure
2466 * indication if aborting times out.
2468 if (!mono_threads_abort_appdomain_threads (domain, -1)) {
2469 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
2470 goto failure;
2473 if (!mono_threadpool_ms_remove_domain_jobs (domain, -1)) {
2474 data->failure_reason = g_strdup_printf ("Cleanup of threadpool jobs of domain %s timed out.", domain->friendly_name);
2475 goto failure;
2478 /* Finalize all finalizable objects in the doomed appdomain */
2479 if (!mono_domain_finalize (domain, -1)) {
2480 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
2481 goto failure;
2484 /* Clear references to our vtables in class->runtime_info.
2485 * We also hold the loader lock because we're going to change
2486 * class->runtime_info.
2489 mono_loader_lock (); //FIXME why do we need the loader lock here?
2490 mono_domain_lock (domain);
2491 #ifdef HAVE_SGEN_GC
2493 * We need to make sure that we don't have any remsets
2494 * pointing into static data of the to-be-freed domain because
2495 * at the next collections they would be invalid. So what we
2496 * do is we first zero all static data and then do a minor
2497 * collection. Because all references in the static data will
2498 * now be null we won't do any unnecessary copies and after
2499 * the collection there won't be any more remsets.
2501 for (i = 0; i < domain->class_vtable_array->len; ++i)
2502 zero_static_data ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i));
2503 mono_gc_collect (0);
2504 #endif
2505 for (i = 0; i < domain->class_vtable_array->len; ++i)
2506 clear_cached_vtable ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i));
2507 deregister_reflection_info_roots (domain);
2509 mono_assembly_cleanup_domain_bindings (domain->domain_id);
2511 mono_domain_unlock (domain);
2512 mono_loader_unlock ();
2514 mono_threads_clear_cached_culture (domain);
2516 domain->state = MONO_APPDOMAIN_UNLOADED;
2518 /* printf ("UNLOADED %s.\n", domain->friendly_name); */
2520 /* remove from the handle table the items related to this domain */
2521 mono_gchandle_free_domain (domain);
2523 mono_domain_free (domain, FALSE);
2525 mono_gc_collect (mono_gc_max_generation ());
2527 mono_atomic_store_release (&data->done, TRUE);
2528 unload_data_unref (data);
2529 mono_thread_detach (thread);
2530 return 0;
2532 failure:
2533 mono_atomic_store_release (&data->done, TRUE);
2534 unload_data_unref (data);
2535 mono_thread_detach (thread);
2536 return 1;
2540 * mono_domain_unload:
2541 * @domain: The domain to unload
2543 * Unloads an appdomain. Follows the process outlined in the comment
2544 * for mono_domain_try_unload.
2546 void
2547 mono_domain_unload (MonoDomain *domain)
2549 MonoObject *exc = NULL;
2550 mono_domain_try_unload (domain, &exc);
2553 static guint32
2554 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
2556 guint32 result;
2558 MONO_ENTER_GC_SAFE;
2559 result = WaitForSingleObjectEx (handle, timeout, alertable);
2560 MONO_EXIT_GC_SAFE;
2562 return result;
2566 * mono_domain_unload:
2567 * @domain: The domain to unload
2568 * @exc: Exception information
2570 * Unloads an appdomain. Follows the process outlined in:
2571 * http://blogs.gotdotnet.com/cbrumme
2573 * If doing things the 'right' way is too hard or complex, we do it the
2574 * 'simple' way, which means do everything needed to avoid crashes and
2575 * memory leaks, but not much else.
2577 * It is required to pass a valid reference to the exc argument, upon return
2578 * from this function *exc will be set to the exception thrown, if any.
2580 * If this method is not called from an icall (embedded scenario for instance),
2581 * it must not be called with any managed frames on the stack, since the unload
2582 * process could end up trying to abort the current thread.
2584 void
2585 mono_domain_try_unload (MonoDomain *domain, MonoObject **exc)
2587 MonoError error;
2588 HANDLE thread_handle;
2589 MonoAppDomainState prev_state;
2590 MonoMethod *method;
2591 unload_data *thread_data;
2592 MonoNativeThreadId tid;
2593 MonoDomain *caller_domain = mono_domain_get ();
2595 /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, mono_native_thread_id_get ()); */
2597 /* Atomically change our state to UNLOADING */
2598 prev_state = (MonoAppDomainState)InterlockedCompareExchange ((gint32*)&domain->state,
2599 MONO_APPDOMAIN_UNLOADING_START,
2600 MONO_APPDOMAIN_CREATED);
2601 if (prev_state != MONO_APPDOMAIN_CREATED) {
2602 switch (prev_state) {
2603 case MONO_APPDOMAIN_UNLOADING_START:
2604 case MONO_APPDOMAIN_UNLOADING:
2605 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded.");
2606 return;
2607 case MONO_APPDOMAIN_UNLOADED:
2608 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded.");
2609 return;
2610 default:
2611 g_warning ("Invalid appdomain state %d", prev_state);
2612 g_assert_not_reached ();
2616 mono_domain_set (domain, FALSE);
2617 /* Notify OnDomainUnload listeners */
2618 method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1);
2619 g_assert (method);
2621 mono_runtime_try_invoke (method, domain->domain, NULL, exc, &error);
2623 if (!mono_error_ok (&error)) {
2624 if (*exc)
2625 mono_error_cleanup (&error);
2626 else
2627 *exc = (MonoObject*)mono_error_convert_to_exception (&error);
2630 if (*exc) {
2631 /* Roll back the state change */
2632 domain->state = MONO_APPDOMAIN_CREATED;
2633 mono_domain_set (caller_domain, FALSE);
2634 return;
2636 mono_domain_set (caller_domain, FALSE);
2638 thread_data = g_new0 (unload_data, 1);
2639 thread_data->domain = domain;
2640 thread_data->failure_reason = NULL;
2641 thread_data->done = FALSE;
2642 thread_data->refcount = 2; /*Must be 2: unload thread + initiator */
2644 /*The managed callback finished successfully, now we start tearing down the appdomain*/
2645 domain->state = MONO_APPDOMAIN_UNLOADING;
2647 * First we create a separate thread for unloading, since
2648 * we might have to abort some threads, including the current one.
2650 thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)unload_thread_main, thread_data, 0, CREATE_SUSPENDED, &tid);
2651 if (thread_handle == NULL)
2652 return;
2653 mono_thread_info_resume (tid);
2655 /* Wait for the thread */
2656 while (!thread_data->done && guarded_wait (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION) {
2657 if (mono_thread_internal_has_appdomain_ref (mono_thread_internal_current (), domain) && (mono_thread_interruption_requested ())) {
2658 /* The unload thread tries to abort us */
2659 /* The icall wrapper will execute the abort */
2660 CloseHandle (thread_handle);
2661 unload_data_unref (thread_data);
2662 return;
2665 CloseHandle (thread_handle);
2667 if (thread_data->failure_reason) {
2668 /* Roll back the state change */
2669 domain->state = MONO_APPDOMAIN_CREATED;
2671 g_warning ("%s", thread_data->failure_reason);
2673 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain (thread_data->failure_reason);
2675 g_free (thread_data->failure_reason);
2676 thread_data->failure_reason = NULL;
2679 unload_data_unref (thread_data);