/**
|
* \file
|
* AppDomain functions
|
*
|
* Authors:
|
* Dietmar Maurer (dietmar@ximian.com)
|
* Patrik Torstensson
|
* Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
*
|
* Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
|
* Copyright 2004-2009 Novell, Inc (http://www.novell.com)
|
* Copyright 2012 Xamarin Inc
|
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
*/
|
#undef ASSEMBLY_LOAD_DEBUG
|
#include <config.h>
|
#include <glib.h>
|
#include <string.h>
|
#include <errno.h>
|
#include <time.h>
|
#include <sys/types.h>
|
#include <sys/stat.h>
|
#ifdef HAVE_SYS_TIME_H
|
#include <sys/time.h>
|
#endif
|
#ifdef HAVE_UNISTD_H
|
#include <unistd.h>
|
#endif
|
#ifdef HAVE_UTIME_H
|
#include <utime.h>
|
#else
|
#ifdef HAVE_SYS_UTIME_H
|
#include <sys/utime.h>
|
#endif
|
#endif
|
|
#include <mono/metadata/gc-internals.h>
|
#include <mono/metadata/object.h>
|
#include <mono/metadata/appdomain-icalls.h>
|
#include <mono/metadata/domain-internals.h>
|
#include "mono/metadata/metadata-internals.h"
|
#include <mono/metadata/assembly-internals.h>
|
#include <mono/metadata/exception.h>
|
#include <mono/metadata/exception-internals.h>
|
#include <mono/metadata/threads.h>
|
#include <mono/metadata/threadpool.h>
|
#include <mono/metadata/tabledefs.h>
|
#include <mono/metadata/mono-gc.h>
|
#include <mono/metadata/marshal.h>
|
#include <mono/metadata/marshal-internals.h>
|
#include <mono/metadata/monitor.h>
|
#include <mono/metadata/mono-debug.h>
|
#include <mono/metadata/attach.h>
|
#include <mono/metadata/w32file.h>
|
#include <mono/metadata/lock-tracer.h>
|
#include <mono/metadata/console-io.h>
|
#include <mono/metadata/threads-types.h>
|
#include <mono/metadata/tokentype.h>
|
#include <mono/metadata/profiler-private.h>
|
#include <mono/metadata/reflection-internals.h>
|
#include <mono/metadata/abi-details.h>
|
#include <mono/metadata/w32socket.h>
|
#include <mono/utils/mono-uri.h>
|
#include <mono/utils/mono-logger-internals.h>
|
#include <mono/utils/mono-path.h>
|
#include <mono/utils/mono-stdlib.h>
|
#include <mono/utils/mono-io-portability.h>
|
#include <mono/utils/mono-error-internals.h>
|
#include <mono/utils/atomic.h>
|
#include <mono/utils/mono-memory-model.h>
|
#include <mono/utils/mono-threads.h>
|
#include <mono/metadata/w32handle.h>
|
#include <mono/metadata/w32error.h>
|
#include <mono/utils/w32api.h>
|
#ifdef HOST_WIN32
|
#include <direct.h>
|
#endif
|
|
typedef struct
|
{
|
int runtime_count;
|
int assemblybinding_count;
|
MonoDomain *domain;
|
gchar *filename;
|
} RuntimeConfig;
|
|
static gunichar2 process_guid [36];
|
static gboolean process_guid_set = FALSE;
|
|
static gboolean no_exec = FALSE;
|
|
static MonoAssembly *
|
mono_domain_assembly_preload (MonoAssemblyName *aname,
|
gchar **assemblies_path,
|
gpointer user_data);
|
|
static MonoAssembly *
|
mono_domain_assembly_search (MonoAssemblyName *aname,
|
gpointer user_data);
|
|
static void
|
mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
|
|
static void
|
add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
|
|
static MonoAppDomainHandle
|
mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetupHandle setup, MonoError *error);
|
|
static MonoDomain *
|
mono_domain_create_appdomain_checked (char *friendly_name, char *configuration_file, MonoError *error);
|
|
|
static void
|
mono_context_set_default_context (MonoDomain *domain);
|
|
static char *
|
get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error);
|
|
static MonoLoadFunc load_function = NULL;
|
|
/* Lazy class loading functions */
|
static GENERATE_GET_CLASS_WITH_CACHE (assembly, "System.Reflection", "Assembly");
|
|
static GENERATE_GET_CLASS_WITH_CACHE (appdomain, "System", "AppDomain");
|
|
static MonoDomain *
|
mono_domain_from_appdomain_handle (MonoAppDomainHandle appdomain);
|
|
static void
|
mono_error_set_appdomain_unloaded (MonoError *error)
|
{
|
mono_error_set_generic_error (error, "System", "AppDomainUnloadedException", "");
|
}
|
|
void
|
mono_install_runtime_load (MonoLoadFunc func)
|
{
|
load_function = func;
|
}
|
|
MonoDomain*
|
mono_runtime_load (const char *filename, const char *runtime_version)
|
{
|
g_assert (load_function);
|
return load_function (filename, runtime_version);
|
}
|
|
/**
|
* mono_runtime_set_no_exec:
|
*
|
* Instructs the runtime to operate in static mode, i.e. avoid/do not
|
* allow managed code execution. This is useful for running the AOT
|
* compiler on platforms which allow full-aot execution only. This
|
* should be called before mono_runtime_init ().
|
*/
|
void
|
mono_runtime_set_no_exec (gboolean val)
|
{
|
no_exec = val;
|
}
|
|
/**
|
* mono_runtime_get_no_exec:
|
*
|
* If true, then the runtime will not allow managed code execution.
|
*/
|
gboolean
|
mono_runtime_get_no_exec (void)
|
{
|
return no_exec;
|
}
|
|
static void
|
create_domain_objects (MonoDomain *domain)
|
{
|
MonoError error;
|
MonoDomain *old_domain = mono_domain_get ();
|
MonoString *arg;
|
MonoVTable *string_vt;
|
MonoClassField *string_empty_fld;
|
|
if (domain != old_domain) {
|
mono_thread_push_appdomain_ref (domain);
|
mono_domain_set_internal_with_options (domain, FALSE);
|
}
|
|
/*
|
* Initialize String.Empty. This enables the removal of
|
* the static cctor of the String class.
|
*/
|
string_vt = mono_class_vtable (domain, mono_defaults.string_class);
|
string_empty_fld = mono_class_get_field_from_name (mono_defaults.string_class, "Empty");
|
g_assert (string_empty_fld);
|
MonoString *empty_str = mono_string_new_checked (domain, "", &error);
|
mono_error_assert_ok (&error);
|
empty_str = mono_string_intern_checked (empty_str, &error);
|
mono_error_assert_ok (&error);
|
mono_field_static_set_value (string_vt, string_empty_fld, empty_str);
|
domain->empty_string = empty_str;
|
mono_gc_wbarrier_generic_nostore (&domain->empty_string);
|
|
/*
|
* Create an instance early since we can't do it when there is no memory.
|
*/
|
arg = mono_string_new_checked (domain, "Out of memory", &error);
|
mono_error_assert_ok (&error);
|
domain->out_of_memory_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL, &error);
|
mono_gc_wbarrier_generic_nostore (&domain->out_of_memory_ex);
|
mono_error_assert_ok (&error);
|
|
/*
|
* These two are needed because the signal handlers might be executing on
|
* an alternate stack, and Boehm GC can't handle that.
|
*/
|
arg = mono_string_new_checked (domain, "A null value was found where an object instance was required", &error);
|
mono_error_assert_ok (&error);
|
domain->null_reference_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL, &error);
|
mono_gc_wbarrier_generic_nostore (&domain->null_reference_ex);
|
mono_error_assert_ok (&error);
|
arg = mono_string_new_checked (domain, "The requested operation caused a stack overflow.", &error);
|
mono_error_assert_ok (&error);
|
domain->stack_overflow_ex = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL, &error);
|
mono_gc_wbarrier_generic_nostore (&domain->stack_overflow_ex);
|
mono_error_assert_ok (&error);
|
|
/*The ephemeron tombstone i*/
|
domain->ephemeron_tombstone = mono_object_new_checked (domain, mono_defaults.object_class, &error);
|
mono_gc_wbarrier_generic_nostore (&domain->ephemeron_tombstone);
|
mono_error_assert_ok (&error);
|
|
if (domain != old_domain) {
|
mono_thread_pop_appdomain_ref ();
|
mono_domain_set_internal_with_options (old_domain, FALSE);
|
}
|
|
/*
|
* This class is used during exception handling, so initialize it here, to prevent
|
* stack overflows while handling stack overflows.
|
*/
|
mono_class_init (mono_array_class_get (mono_defaults.int_class, 1));
|
}
|
|
/**
|
* mono_runtime_init:
|
* \param domain domain returned by \c mono_init
|
*
|
* Initialize the core AppDomain: this function will run also some
|
* IL initialization code, so it needs the execution engine to be fully
|
* operational.
|
*
|
* \c AppDomain.SetupInformation is set up in \c mono_runtime_exec_main, where
|
* we know the \c entry_assembly.
|
*
|
*/
|
void
|
mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb)
|
{
|
MonoError error;
|
mono_runtime_init_checked (domain, start_cb, attach_cb, &error);
|
mono_error_cleanup (&error);
|
}
|
|
void
|
mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb, MonoError *error)
|
{
|
MonoAppDomainSetup *setup;
|
MonoAppDomain *ad;
|
MonoClass *klass;
|
|
error_init (error);
|
|
mono_portability_helpers_init ();
|
|
mono_gc_base_init ();
|
mono_monitor_init ();
|
mono_marshal_init ();
|
|
mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
|
mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
|
mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
|
mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
|
mono_install_assembly_postload_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
|
mono_install_assembly_postload_refonly_search_hook ((MonoAssemblySearchFunc)mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
|
mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
|
|
mono_thread_init (start_cb, attach_cb);
|
|
klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
|
setup = (MonoAppDomainSetup *) mono_object_new_pinned (domain, klass, error);
|
return_if_nok (error);
|
|
klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomain");
|
|
ad = (MonoAppDomain *) mono_object_new_pinned (domain, klass, error);
|
return_if_nok (error);
|
|
ad->data = domain;
|
domain->domain = ad;
|
mono_gc_wbarrier_generic_nostore (&domain->domain);
|
domain->setup = setup;
|
mono_gc_wbarrier_generic_nostore (&domain->setup);
|
|
mono_thread_attach (domain);
|
|
mono_type_initialization_init ();
|
|
if (!mono_runtime_get_no_exec ())
|
create_domain_objects (domain);
|
|
/* GC init has to happen after thread init */
|
mono_gc_init ();
|
|
/* contexts use GC handles, so they must be initialized after the GC */
|
mono_context_init_checked (domain, error);
|
return_if_nok (error);
|
mono_context_set_default_context (domain);
|
|
#ifndef DISABLE_SOCKETS
|
mono_network_init ();
|
#endif
|
|
mono_console_init ();
|
mono_attach_init ();
|
|
mono_locks_tracer_init ();
|
|
/* mscorlib is loaded before we install the load hook */
|
mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
|
|
return;
|
}
|
|
static void
|
mono_context_set_default_context (MonoDomain *domain)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
mono_context_set_handle (MONO_HANDLE_NEW (MonoAppContext, domain->default_context));
|
HANDLE_FUNCTION_RETURN ();
|
}
|
|
|
static int
|
mono_get_corlib_version (void)
|
{
|
MonoError error;
|
MonoClass *klass;
|
MonoClassField *field;
|
MonoObject *value;
|
|
klass = mono_class_load_from_name (mono_defaults.corlib, "System", "Environment");
|
mono_class_init (klass);
|
field = mono_class_get_field_from_name (klass, "mono_corlib_version");
|
if (!field)
|
return -1;
|
if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
|
return -1;
|
value = mono_field_get_value_object_checked (mono_domain_get (), field, NULL, &error);
|
mono_error_assert_ok (&error);
|
return *(gint32*)((gchar*)value + sizeof (MonoObject));
|
}
|
|
/**
|
* mono_check_corlib_version:
|
* Checks that the corlib that is loaded matches the version of this runtime.
|
* \returns NULL if the runtime will work with the corlib, or a \c g_malloc
|
* allocated string with the error otherwise.
|
*/
|
const char*
|
mono_check_corlib_version (void)
|
{
|
int version = mono_get_corlib_version ();
|
if (version != MONO_CORLIB_VERSION)
|
return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
|
|
/* Check that the managed and unmanaged layout of MonoInternalThread matches */
|
guint32 native_offset = (guint32) MONO_STRUCT_OFFSET (MonoInternalThread, last);
|
guint32 managed_offset = mono_field_get_offset (mono_class_get_field_from_name (mono_defaults.internal_thread_class, "last"));
|
if (native_offset != managed_offset)
|
return g_strdup_printf ("expected InternalThread.last field offset %u, found %u. See InternalThread.last comment", native_offset, managed_offset);
|
|
return NULL;
|
}
|
|
/**
|
* mono_context_init:
|
* \param domain The domain where the \c System.Runtime.Remoting.Context.Context is initialized
|
* Initializes the \p domain's default \c System.Runtime.Remoting 's Context.
|
*/
|
void
|
mono_context_init (MonoDomain *domain)
|
{
|
MonoError error;
|
mono_context_init_checked (domain, &error);
|
mono_error_cleanup (&error);
|
}
|
|
void
|
mono_context_init_checked (MonoDomain *domain, MonoError *error)
|
{
|
MonoClass *klass;
|
MonoAppContext *context;
|
|
error_init (error);
|
|
klass = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
|
context = (MonoAppContext *) mono_object_new_pinned (domain, klass, error);
|
return_if_nok (error);
|
|
context->domain_id = domain->domain_id;
|
context->context_id = 0;
|
mono_threads_register_app_context (context, error);
|
mono_error_assert_ok (error);
|
domain->default_context = context;
|
}
|
|
/**
|
* mono_runtime_cleanup:
|
* \param domain unused.
|
*
|
* Internal routine.
|
*
|
* This must not be called while there are still running threads executing
|
* managed code.
|
*/
|
void
|
mono_runtime_cleanup (MonoDomain *domain)
|
{
|
mono_attach_cleanup ();
|
|
/* This ends up calling any pending pending (for at most 2 seconds) */
|
mono_gc_cleanup ();
|
|
mono_thread_cleanup ();
|
|
#ifndef DISABLE_SOCKETS
|
mono_network_cleanup ();
|
#endif
|
mono_marshal_cleanup ();
|
|
mono_type_initialization_cleanup ();
|
|
mono_monitor_cleanup ();
|
}
|
|
static MonoDomainFunc quit_function = NULL;
|
|
/**
|
* mono_install_runtime_cleanup:
|
*/
|
void
|
mono_install_runtime_cleanup (MonoDomainFunc func)
|
{
|
quit_function = func;
|
}
|
|
/**
|
* mono_runtime_quit:
|
*/
|
void
|
mono_runtime_quit ()
|
{
|
if (quit_function != NULL)
|
quit_function (mono_get_root_domain (), NULL);
|
}
|
|
/**
|
* mono_domain_create_appdomain:
|
* \param friendly_name The friendly name of the appdomain to create
|
* \param configuration_file The configuration file to initialize the appdomain with
|
* \returns a \c MonoDomain initialized with the appdomain
|
*/
|
MonoDomain *
|
mono_domain_create_appdomain (char *friendly_name, char *configuration_file)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
MonoError error;
|
MonoDomain *domain = mono_domain_create_appdomain_checked (friendly_name, configuration_file, &error);
|
mono_error_cleanup (&error);
|
HANDLE_FUNCTION_RETURN_VAL (domain);
|
}
|
|
/**
|
* mono_domain_create_appdomain_checked:
|
* \param friendly_name The friendly name of the appdomain to create
|
* \param configuration_file The configuration file to initialize the appdomain with
|
* \param error Set on error.
|
*
|
* \returns a MonoDomain initialized with the appdomain. On failure sets \p error and returns NULL.
|
*/
|
MonoDomain *
|
mono_domain_create_appdomain_checked (char *friendly_name, char *configuration_file, MonoError *error)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
error_init (error);
|
MonoDomain *result = NULL;
|
|
MonoClass *klass = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
|
MonoAppDomainSetupHandle setup = MONO_HANDLE_NEW (MonoAppDomainSetup, mono_object_new_checked (mono_domain_get (), klass, error));
|
goto_if_nok (error, leave);
|
MonoStringHandle config_file;
|
if (configuration_file != NULL) {
|
config_file = mono_string_new_handle (mono_domain_get (), configuration_file, error);
|
goto_if_nok (error, leave);
|
} else {
|
config_file = MONO_HANDLE_NEW (MonoString, NULL);
|
}
|
MONO_HANDLE_SET (setup, configuration_file, config_file);
|
|
MonoAppDomainHandle ad = mono_domain_create_appdomain_internal (friendly_name, setup, error);
|
goto_if_nok (error, leave);
|
|
result = mono_domain_from_appdomain_handle (ad);
|
leave:
|
HANDLE_FUNCTION_RETURN_VAL (result);
|
}
|
|
/**
|
* mono_domain_set_config:
|
* \param domain \c MonoDomain initialized with the appdomain we want to change
|
* \param base_dir new base directory for the appdomain
|
* \param config_file_name path to the new configuration for the app domain
|
*
|
* Used to set the system configuration for an appdomain
|
*
|
* Without using this, embedded builds will get 'System.Configuration.ConfigurationErrorsException:
|
* Error Initializing the configuration system. ---> System.ArgumentException:
|
* The 'ExeConfigFilename' argument cannot be null.' for some managed calls.
|
*/
|
void
|
mono_domain_set_config (MonoDomain *domain, const char *base_dir, const char *config_file_name)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
MonoError error;
|
mono_domain_set_config_checked (domain, base_dir, config_file_name, &error);
|
mono_error_cleanup (&error);
|
HANDLE_FUNCTION_RETURN ();
|
}
|
|
gboolean
|
mono_domain_set_config_checked (MonoDomain *domain, const char *base_dir, const char *config_file_name, MonoError *error)
|
{
|
error_init (error);
|
MonoAppDomainSetupHandle setup = MONO_HANDLE_NEW (MonoAppDomainSetup, domain->setup);
|
MonoStringHandle base_dir_str = mono_string_new_handle (domain, base_dir, error);
|
goto_if_nok (error, leave);
|
MONO_HANDLE_SET (setup, application_base, base_dir_str);
|
MonoStringHandle config_file_name_str = mono_string_new_handle (domain, config_file_name, error);
|
goto_if_nok (error, leave);
|
MONO_HANDLE_SET (setup, configuration_file, config_file_name_str);
|
leave:
|
return is_ok (error);
|
}
|
|
static MonoAppDomainSetupHandle
|
copy_app_domain_setup (MonoDomain *domain, MonoAppDomainSetupHandle setup, MonoError *error)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
MonoDomain *caller_domain;
|
MonoClass *ads_class;
|
MonoAppDomainSetupHandle result = MONO_HANDLE_NEW (MonoAppDomainSetup, NULL);
|
|
error_init (error);
|
|
caller_domain = mono_domain_get ();
|
ads_class = mono_class_load_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
|
|
MonoAppDomainSetupHandle copy = MONO_HANDLE_NEW (MonoAppDomainSetup, mono_object_new_checked (domain, ads_class, error));
|
goto_if_nok (error, leave);
|
|
mono_domain_set_internal (domain);
|
|
#define XCOPY_FIELD(dst,field,src,error) \
|
do { \
|
MonoObjectHandle src_val = MONO_HANDLE_NEW_GET (MonoObject, (src), field); \
|
MonoObjectHandle copied_val = mono_marshal_xdomain_copy_value_handle (src_val, error); \
|
goto_if_nok (error, leave); \
|
MONO_HANDLE_SET ((dst),field,copied_val); \
|
} while (0)
|
|
#define COPY_VAL(dst,field,type,src) \
|
do { \
|
MONO_HANDLE_SETVAL ((dst), field, type, MONO_HANDLE_GETVAL ((src),field)); \
|
} while (0)
|
|
XCOPY_FIELD (copy, application_base, setup, error);
|
XCOPY_FIELD (copy, application_name, setup, error);
|
XCOPY_FIELD (copy, cache_path, setup, error);
|
XCOPY_FIELD (copy, configuration_file, setup, error);
|
XCOPY_FIELD (copy, dynamic_base, setup, error);
|
XCOPY_FIELD (copy, license_file, setup, error);
|
XCOPY_FIELD (copy, private_bin_path, setup, error);
|
XCOPY_FIELD (copy, private_bin_path_probe, setup, error);
|
XCOPY_FIELD (copy, shadow_copy_directories, setup, error);
|
XCOPY_FIELD (copy, shadow_copy_files, setup, error);
|
COPY_VAL (copy, publisher_policy, MonoBoolean, setup);
|
COPY_VAL (copy, path_changed, MonoBoolean, setup);
|
COPY_VAL (copy, loader_optimization, int, setup);
|
COPY_VAL (copy, disallow_binding_redirects, MonoBoolean, setup);
|
COPY_VAL (copy, disallow_code_downloads, MonoBoolean, setup);
|
XCOPY_FIELD (copy, domain_initializer_args, setup, error);
|
COPY_VAL (copy, disallow_appbase_probe, MonoBoolean, setup);
|
XCOPY_FIELD (copy, application_trust, setup, error);
|
XCOPY_FIELD (copy, configuration_bytes, setup, error);
|
XCOPY_FIELD (copy, serialized_non_primitives, setup, error);
|
|
#undef XCOPY_FIELD
|
#undef COPY_VAL
|
|
mono_domain_set_internal (caller_domain);
|
|
MONO_HANDLE_ASSIGN (result, copy);
|
leave:
|
HANDLE_FUNCTION_RETURN_REF (MonoAppDomainSetup, result);
|
}
|
|
static MonoAppDomainHandle
|
mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetupHandle setup, MonoError *error)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
MonoAppDomainHandle result = MONO_HANDLE_NEW (MonoAppDomain, NULL);
|
MonoClass *adclass;
|
MonoDomain *data;
|
|
error_init (error);
|
|
adclass = mono_class_get_appdomain_class ();
|
|
/* FIXME: pin all those objects */
|
data = mono_domain_create();
|
|
MonoAppDomainHandle ad = MONO_HANDLE_NEW (MonoAppDomain, mono_object_new_checked (data, adclass, error));
|
goto_if_nok (error, leave);
|
MONO_HANDLE_SETVAL (ad, data, MonoDomain*, data);
|
data->domain = MONO_HANDLE_RAW (ad);
|
mono_gc_wbarrier_generic_nostore (&data->domain);
|
data->friendly_name = g_strdup (friendly_name);
|
|
MONO_PROFILER_RAISE (domain_name, (data, data->friendly_name));
|
|
MonoStringHandle app_base = MONO_HANDLE_NEW_GET (MonoString, setup, application_base);
|
if (MONO_HANDLE_IS_NULL (app_base)) {
|
/* Inherit from the root domain since MS.NET does this */
|
MonoDomain *root = mono_get_root_domain ();
|
MonoAppDomainSetupHandle root_setup = MONO_HANDLE_NEW (MonoAppDomainSetup, root->setup);
|
MonoStringHandle root_app_base = MONO_HANDLE_NEW_GET (MonoString, root_setup, application_base);
|
if (!MONO_HANDLE_IS_NULL (root_app_base)) {
|
/* N.B. new string is in the new domain */
|
uint32_t gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, root_app_base), TRUE);
|
MonoStringHandle s = mono_string_new_utf16_handle (data, mono_string_chars (MONO_HANDLE_RAW (root_app_base)), mono_string_handle_length (root_app_base), error);
|
mono_gchandle_free (gchandle);
|
if (!is_ok (error)) {
|
g_free (data->friendly_name);
|
goto leave;
|
}
|
MONO_HANDLE_SET (setup, application_base, s);
|
}
|
}
|
|
mono_context_init_checked (data, error);
|
goto_if_nok (error, leave);
|
|
data->setup = MONO_HANDLE_RAW (copy_app_domain_setup (data, setup, error));
|
mono_gc_wbarrier_generic_nostore (&data->setup);
|
if (!mono_error_ok (error)) {
|
g_free (data->friendly_name);
|
goto leave;
|
}
|
|
mono_domain_set_options_from_config (data);
|
add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
|
|
#ifndef DISABLE_SHADOW_COPY
|
/*FIXME, guard this for when the debugger is not running */
|
char *shadow_location = get_shadow_assembly_location_base (data, error);
|
if (!mono_error_ok (error)) {
|
g_free (data->friendly_name);
|
goto leave;
|
}
|
|
g_free (shadow_location);
|
#endif
|
|
create_domain_objects (data);
|
|
MONO_HANDLE_ASSIGN (result, ad);
|
leave:
|
HANDLE_FUNCTION_RETURN_REF (MonoAppDomain, result);
|
}
|
|
/**
|
* mono_domain_has_type_resolve:
|
* \param domain application domain being looked up
|
*
|
* \returns TRUE if the \c AppDomain.TypeResolve field has been set.
|
*/
|
gboolean
|
mono_domain_has_type_resolve (MonoDomain *domain)
|
{
|
static MonoClassField *field = NULL;
|
MonoObject *o;
|
|
if (field == NULL) {
|
field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
|
g_assert (field);
|
}
|
|
/*pedump doesn't create an appdomin, so the domain object doesn't exist.*/
|
if (!domain->domain)
|
return FALSE;
|
|
mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
|
return o != NULL;
|
}
|
|
/**
|
* mono_domain_try_type_resolve:
|
* \param domain application domainwhere the name where the type is going to be resolved
|
* \param name the name of the type to resolve or NULL.
|
* \param tb A \c System.Reflection.Emit.TypeBuilder, used if name is NULL.
|
*
|
* This routine invokes the internal \c System.AppDomain.DoTypeResolve and returns
|
* the assembly that matches name.
|
*
|
* If \p name is null, the value of \c ((TypeBuilder)tb).FullName is used instead
|
*
|
* \returns A \c MonoReflectionAssembly or NULL if not found
|
*/
|
MonoReflectionAssembly *
|
mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
|
{
|
MonoError error;
|
MonoReflectionAssembly *ret = mono_domain_try_type_resolve_checked (domain, name, tb, &error);
|
mono_error_cleanup (&error);
|
|
return ret;
|
}
|
|
MonoReflectionAssembly *
|
mono_domain_try_type_resolve_checked (MonoDomain *domain, char *name, MonoObject *tb, MonoError *error)
|
{
|
static MonoMethod *method = NULL;
|
MonoReflectionAssembly *ret;
|
void *params [1];
|
|
error_init (error);
|
|
g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
|
|
if (method == NULL) {
|
method = mono_class_get_method_from_name (mono_class_get_appdomain_class (), "DoTypeResolve", -1);
|
if (method == NULL) {
|
g_warning ("Method AppDomain.DoTypeResolve not found.\n");
|
return NULL;
|
}
|
}
|
|
if (name) {
|
*params = (MonoObject*)mono_string_new_checked (mono_domain_get (), name, error);
|
return_val_if_nok (error, NULL);
|
} else
|
*params = tb;
|
|
ret = (MonoReflectionAssembly *) mono_runtime_invoke_checked (method, domain->domain, params, error);
|
return_val_if_nok (error, NULL);
|
|
return ret;
|
}
|
|
/**
|
* mono_domain_owns_vtable_slot:
|
* \returns Whether \p vtable_slot is inside a vtable which belongs to \p domain.
|
*/
|
gboolean
|
mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
|
{
|
gboolean res;
|
|
mono_domain_lock (domain);
|
res = mono_mempool_contains_addr (domain->mp, vtable_slot);
|
mono_domain_unlock (domain);
|
return res;
|
}
|
|
/**
|
* mono_domain_set:
|
* \param domain domain
|
* \param force force setting.
|
*
|
* Set the current appdomain to \p domain. If \p force is set, set it even
|
* if it is being unloaded.
|
*
|
* \returns TRUE on success; FALSE if the domain is unloaded
|
*/
|
gboolean
|
mono_domain_set (MonoDomain *domain, gboolean force)
|
{
|
if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
|
return FALSE;
|
|
mono_domain_set_internal (domain);
|
|
return TRUE;
|
}
|
|
MonoObjectHandle
|
ves_icall_System_AppDomain_GetData (MonoAppDomainHandle ad, MonoStringHandle name, MonoError *error)
|
{
|
error_init (error);
|
|
if (MONO_HANDLE_IS_NULL (name)) {
|
mono_error_set_argument_null (error, "name", "");
|
return NULL_HANDLE;
|
}
|
|
g_assert (!MONO_HANDLE_IS_NULL (ad));
|
MonoDomain *add = MONO_HANDLE_GETVAL (ad, data);
|
g_assert (add);
|
|
char *str = mono_string_handle_to_utf8 (name, error);
|
return_val_if_nok (error, NULL_HANDLE);
|
|
mono_domain_lock (add);
|
|
MonoAppDomainSetupHandle ad_setup = MONO_HANDLE_NEW (MonoAppDomainSetup, add->setup);
|
MonoStringHandle o;
|
if (!strcmp (str, "APPBASE"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, application_base);
|
else if (!strcmp (str, "APP_CONFIG_FILE"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, configuration_file);
|
else if (!strcmp (str, "DYNAMIC_BASE"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, dynamic_base);
|
else if (!strcmp (str, "APP_NAME"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, application_name);
|
else if (!strcmp (str, "CACHE_BASE"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, cache_path);
|
else if (!strcmp (str, "PRIVATE_BINPATH"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, private_bin_path);
|
else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, private_bin_path_probe);
|
else if (!strcmp (str, "SHADOW_COPY_DIRS"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, shadow_copy_directories);
|
else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
|
o = MONO_HANDLE_NEW_GET (MonoString, ad_setup, shadow_copy_files);
|
else
|
o = MONO_HANDLE_NEW (MonoString, mono_g_hash_table_lookup (add->env, MONO_HANDLE_RAW (name)));
|
|
mono_domain_unlock (add);
|
g_free (str);
|
|
return MONO_HANDLE_CAST (MonoObject, o);
|
}
|
|
void
|
ves_icall_System_AppDomain_SetData (MonoAppDomainHandle ad, MonoStringHandle name, MonoObjectHandle data, MonoError *error)
|
{
|
error_init (error);
|
|
if (MONO_HANDLE_IS_NULL (name)) {
|
mono_error_set_argument_null (error, "name", "");
|
return;
|
}
|
|
g_assert (!MONO_HANDLE_IS_NULL (ad));
|
MonoDomain *add = MONO_HANDLE_GETVAL (ad, data);
|
g_assert (add);
|
|
mono_domain_lock (add);
|
|
mono_g_hash_table_insert (add->env, MONO_HANDLE_RAW (name), MONO_HANDLE_RAW (data));
|
|
mono_domain_unlock (add);
|
}
|
|
MonoAppDomainSetupHandle
|
ves_icall_System_AppDomain_getSetup (MonoAppDomainHandle ad, MonoError *error)
|
{
|
error_init (error);
|
g_assert (!MONO_HANDLE_IS_NULL (ad));
|
MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data);
|
g_assert (domain);
|
|
return MONO_HANDLE_NEW (MonoAppDomainSetup, domain->setup);
|
}
|
|
MonoStringHandle
|
ves_icall_System_AppDomain_getFriendlyName (MonoAppDomainHandle ad, MonoError *error)
|
{
|
error_init (error);
|
g_assert (!MONO_HANDLE_IS_NULL (ad));
|
MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data);
|
g_assert (domain);
|
|
return mono_string_new_handle (domain, domain->friendly_name, error);
|
}
|
|
MonoAppDomainHandle
|
ves_icall_System_AppDomain_getCurDomain (MonoError *error)
|
{
|
error_init (error);
|
MonoDomain *add = mono_domain_get ();
|
|
return MONO_HANDLE_NEW (MonoAppDomain, add->domain);
|
}
|
|
MonoAppDomainHandle
|
ves_icall_System_AppDomain_getRootDomain (MonoError *error)
|
{
|
error_init (error);
|
MonoDomain *root = mono_get_root_domain ();
|
|
return MONO_HANDLE_NEW (MonoAppDomain, root->domain);
|
}
|
|
MonoBoolean
|
ves_icall_System_CLRConfig_CheckThrowUnobservedTaskExceptions ()
|
{
|
MonoDomain *domain = mono_domain_get ();
|
|
return domain->throw_unobserved_task_exceptions;
|
}
|
|
static char*
|
get_attribute_value (const gchar **attribute_names,
|
const gchar **attribute_values,
|
const char *att_name)
|
{
|
int n;
|
for (n = 0; attribute_names [n] != NULL; n++) {
|
if (strcmp (attribute_names [n], att_name) == 0)
|
return g_strdup (attribute_values [n]);
|
}
|
return NULL;
|
}
|
|
static void
|
start_element (GMarkupParseContext *context,
|
const gchar *element_name,
|
const gchar **attribute_names,
|
const gchar **attribute_values,
|
gpointer user_data,
|
GError **error)
|
{
|
RuntimeConfig *runtime_config = (RuntimeConfig *)user_data;
|
|
if (strcmp (element_name, "runtime") == 0) {
|
runtime_config->runtime_count++;
|
return;
|
}
|
|
if (strcmp (element_name, "assemblyBinding") == 0) {
|
runtime_config->assemblybinding_count++;
|
return;
|
}
|
|
if (runtime_config->runtime_count != 1)
|
return;
|
|
if (strcmp (element_name, "ThrowUnobservedTaskExceptions") == 0) {
|
const char *value = get_attribute_value (attribute_names, attribute_values, "enabled");
|
|
if (value && g_ascii_strcasecmp (value, "true") == 0)
|
runtime_config->domain->throw_unobserved_task_exceptions = TRUE;
|
}
|
|
if (runtime_config->assemblybinding_count != 1)
|
return;
|
|
if (strcmp (element_name, "probing") != 0)
|
return;
|
|
g_free (runtime_config->domain->private_bin_path);
|
runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath");
|
if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) {
|
g_free (runtime_config->domain->private_bin_path);
|
runtime_config->domain->private_bin_path = NULL;
|
return;
|
}
|
}
|
|
static void
|
end_element (GMarkupParseContext *context,
|
const gchar *element_name,
|
gpointer user_data,
|
GError **error)
|
{
|
RuntimeConfig *runtime_config = (RuntimeConfig *)user_data;
|
if (strcmp (element_name, "runtime") == 0)
|
runtime_config->runtime_count--;
|
else if (strcmp (element_name, "assemblyBinding") == 0)
|
runtime_config->assemblybinding_count--;
|
}
|
|
static void
|
parse_error (GMarkupParseContext *context, GError *error, gpointer user_data)
|
{
|
RuntimeConfig *state = (RuntimeConfig *)user_data;
|
const gchar *msg;
|
const gchar *filename;
|
|
filename = state && state->filename ? (gchar *) state->filename : "<unknown>";
|
msg = error && error->message ? error->message : "";
|
g_warning ("Error parsing %s: %s", filename, msg);
|
}
|
|
static const GMarkupParser
|
mono_parser = {
|
start_element,
|
end_element,
|
NULL,
|
NULL,
|
parse_error
|
};
|
|
void
|
mono_domain_set_options_from_config (MonoDomain *domain)
|
{
|
MonoError error;
|
gchar *config_file_name = NULL, *text = NULL, *config_file_path = NULL;
|
gsize len;
|
GMarkupParseContext *context;
|
RuntimeConfig runtime_config;
|
gint offset;
|
|
if (!domain || !domain->setup || !domain->setup->configuration_file)
|
return;
|
|
config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error);
|
if (!mono_error_ok (&error)) {
|
mono_error_cleanup (&error);
|
goto free_and_out;
|
}
|
|
config_file_path = mono_portability_find_file (config_file_name, TRUE);
|
if (!config_file_path)
|
config_file_path = config_file_name;
|
|
if (!g_file_get_contents (config_file_path, &text, &len, NULL))
|
goto free_and_out;
|
|
runtime_config.runtime_count = 0;
|
runtime_config.assemblybinding_count = 0;
|
runtime_config.domain = domain;
|
runtime_config.filename = config_file_path;
|
|
offset = 0;
|
if (len > 3 && text [0] == '\xef' && text [1] == (gchar) '\xbb' && text [2] == '\xbf')
|
offset = 3; /* Skip UTF-8 BOM */
|
|
context = g_markup_parse_context_new (&mono_parser, (GMarkupParseFlags)0, &runtime_config, NULL);
|
if (g_markup_parse_context_parse (context, text + offset, len - offset, NULL))
|
g_markup_parse_context_end_parse (context, NULL);
|
g_markup_parse_context_free (context);
|
|
free_and_out:
|
g_free (text);
|
if (config_file_name != config_file_path)
|
g_free (config_file_name);
|
g_free (config_file_path);
|
}
|
|
MonoAppDomainHandle
|
ves_icall_System_AppDomain_createDomain (MonoStringHandle friendly_name, MonoAppDomainSetupHandle setup, MonoError *error)
|
{
|
error_init (error);
|
MonoAppDomainHandle ad = MONO_HANDLE_NEW (MonoAppDomain, NULL);
|
|
#ifdef DISABLE_APPDOMAINS
|
mono_error_set_not_supported (error, "AppDomain creation is not supported on this runtime.");
|
#else
|
char *fname;
|
|
fname = mono_string_handle_to_utf8 (friendly_name, error);
|
return_val_if_nok (error, ad);
|
ad = mono_domain_create_appdomain_internal (fname, setup, error);
|
g_free (fname);
|
#endif
|
return ad;
|
}
|
|
static gboolean
|
add_assembly_to_array (MonoDomain *domain, MonoArrayHandle dest, int dest_idx, MonoAssembly* assm, MonoError *error)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
error_init (error);
|
MonoReflectionAssemblyHandle assm_obj = mono_assembly_get_object_handle (domain, assm, error);
|
goto_if_nok (error, leave);
|
MONO_HANDLE_ARRAY_SETREF (dest, dest_idx, assm_obj);
|
leave:
|
HANDLE_FUNCTION_RETURN_VAL (is_ok (error));
|
}
|
|
MonoArrayHandle
|
ves_icall_System_AppDomain_GetAssemblies (MonoAppDomainHandle ad, MonoBoolean refonly, MonoError *error)
|
{
|
error_init (error);
|
MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data);
|
MonoAssembly* ass;
|
GSList *tmp;
|
int i;
|
GPtrArray *assemblies;
|
|
/*
|
* Make a copy of the list of assemblies because we can't hold the assemblies
|
* lock while creating objects etc.
|
*/
|
assemblies = g_ptr_array_new ();
|
/* Need to skip internal assembly builders created by remoting */
|
mono_domain_assemblies_lock (domain);
|
for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
|
ass = (MonoAssembly *)tmp->data;
|
if (refonly != ass->ref_only)
|
continue;
|
if (ass->corlib_internal)
|
continue;
|
g_ptr_array_add (assemblies, ass);
|
}
|
mono_domain_assemblies_unlock (domain);
|
|
MonoArrayHandle res = mono_array_new_handle (domain, mono_class_get_assembly_class (), assemblies->len, error);
|
goto_if_nok (error, leave);
|
for (i = 0; i < assemblies->len; ++i) {
|
if (!add_assembly_to_array (domain, res, i, (MonoAssembly *)g_ptr_array_index (assemblies, i), error))
|
goto leave;
|
}
|
|
leave:
|
g_ptr_array_free (assemblies, TRUE);
|
return res;
|
}
|
|
MonoAssembly*
|
mono_try_assembly_resolve (MonoDomain *domain, const char *fname_raw, MonoAssembly *requesting, gboolean refonly, MonoError *error)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
error_init (error);
|
MonoAssembly *result = NULL;
|
MonoStringHandle fname = mono_string_new_handle (domain, fname_raw, error);
|
goto_if_nok (error, leave);
|
result = mono_try_assembly_resolve_handle (domain, fname, requesting, refonly, error);
|
leave:
|
HANDLE_FUNCTION_RETURN_VAL (result);
|
}
|
|
MonoAssembly*
|
mono_try_assembly_resolve_handle (MonoDomain *domain, MonoStringHandle fname, MonoAssembly *requesting, gboolean refonly, MonoError *error)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
MonoAssembly *ret = NULL;
|
MonoMethod *method;
|
MonoBoolean isrefonly;
|
gpointer params [3];
|
|
error_init (error);
|
|
if (mono_runtime_get_no_exec ())
|
goto leave;
|
|
g_assert (domain != NULL && !MONO_HANDLE_IS_NULL (fname));
|
|
method = mono_class_get_method_from_name (mono_class_get_appdomain_class (), "DoAssemblyResolve", -1);
|
g_assert (method != NULL);
|
|
isrefonly = refonly ? 1 : 0;
|
MonoReflectionAssemblyHandle requesting_handle;
|
if (requesting) {
|
requesting_handle = mono_assembly_get_object_handle (domain, requesting, error);
|
goto_if_nok (error, leave);
|
}
|
params [0] = MONO_HANDLE_RAW (fname);
|
params[1] = requesting ? MONO_HANDLE_RAW (requesting_handle) : NULL;
|
params [2] = &isrefonly;
|
MonoObject *exc = NULL;
|
MonoReflectionAssemblyHandle result = MONO_HANDLE_NEW (MonoReflectionAssembly, mono_runtime_try_invoke (method, domain->domain, params, &exc, error));
|
if (!is_ok (error) || exc != NULL) {
|
if (is_ok (error))
|
mono_error_set_exception_instance (error, (MonoException*)exc);
|
goto leave;
|
}
|
ret = !MONO_HANDLE_IS_NULL (result) ? MONO_HANDLE_GETVAL (result, assembly) : NULL;
|
|
if (ret && !refonly && ret->ref_only) {
|
/* .NET Framework throws System.IO.FileNotFoundException in this case */
|
mono_error_set_file_not_found (error, "AssemblyResolveEvent handlers cannot return Assemblies loaded for reflection only");
|
ret = NULL;
|
goto leave;
|
}
|
leave:
|
HANDLE_FUNCTION_RETURN_VAL (ret);
|
}
|
|
MonoAssembly *
|
mono_domain_assembly_postload_search (MonoAssemblyName *aname, MonoAssembly *requesting,
|
gboolean refonly)
|
{
|
MonoError error;
|
MonoAssembly *assembly;
|
MonoDomain *domain = mono_domain_get ();
|
char *aname_str;
|
|
aname_str = mono_stringify_assembly_name (aname);
|
|
/* FIXME: We invoke managed code here, so there is a potential for deadlocks */
|
|
assembly = mono_try_assembly_resolve (domain, aname_str, requesting, refonly, &error);
|
g_free (aname_str);
|
mono_error_cleanup (&error);
|
|
return assembly;
|
}
|
|
/*
|
* LOCKING: assumes assemblies_lock in the domain is already locked.
|
*/
|
static void
|
add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
|
{
|
gint i;
|
GSList *tmp;
|
gboolean destroy_ht = FALSE;
|
|
if (!ass->aname.name)
|
return;
|
|
if (!ht) {
|
ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
|
destroy_ht = TRUE;
|
for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
|
g_hash_table_insert (ht, tmp->data, tmp->data);
|
}
|
}
|
|
/* FIXME: handle lazy loaded assemblies */
|
|
if (!g_hash_table_lookup (ht, ass)) {
|
mono_assembly_addref (ass);
|
g_hash_table_insert (ht, ass, ass);
|
domain->domain_assemblies = g_slist_append (domain->domain_assemblies, ass);
|
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);
|
}
|
|
if (ass->image->references) {
|
for (i = 0; i < ass->image->nreferences; i++) {
|
if (ass->image->references[i] && ass->image->references [i] != REFERENCE_MISSING) {
|
if (!g_hash_table_lookup (ht, ass->image->references [i])) {
|
add_assemblies_to_domain (domain, ass->image->references [i], ht);
|
}
|
}
|
}
|
}
|
if (destroy_ht)
|
g_hash_table_destroy (ht);
|
}
|
|
static void
|
mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
static MonoClassField *assembly_load_field;
|
static MonoMethod *assembly_load_method;
|
MonoError error;
|
MonoDomain *domain = mono_domain_get ();
|
MonoClass *klass;
|
gpointer load_value;
|
void *params [1];
|
|
if (!domain->domain)
|
/* This can happen during startup */
|
goto leave;
|
#ifdef ASSEMBLY_LOAD_DEBUG
|
fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
|
#endif
|
klass = domain->domain->mbr.obj.vtable->klass;
|
|
mono_domain_assemblies_lock (domain);
|
add_assemblies_to_domain (domain, assembly, NULL);
|
mono_domain_assemblies_unlock (domain);
|
|
if (assembly_load_field == NULL) {
|
assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
|
g_assert (assembly_load_field);
|
}
|
|
mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
|
if (load_value == NULL) {
|
/* No events waiting to be triggered */
|
goto leave;
|
}
|
|
MonoReflectionAssemblyHandle ref_assembly = mono_assembly_get_object_handle (domain, assembly, &error);
|
mono_error_assert_ok (&error);
|
|
if (assembly_load_method == NULL) {
|
assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
|
g_assert (assembly_load_method);
|
}
|
|
*params = MONO_HANDLE_RAW(ref_assembly);
|
|
mono_runtime_invoke_checked (assembly_load_method, domain->domain, params, &error);
|
mono_error_cleanup (&error);
|
leave:
|
HANDLE_FUNCTION_RETURN ();
|
}
|
|
/*
|
* LOCKING: Acquires the domain assemblies lock.
|
*/
|
static void
|
set_domain_search_path (MonoDomain *domain)
|
{
|
MonoError error;
|
MonoAppDomainSetup *setup;
|
gchar **tmp;
|
gchar *search_path = NULL;
|
gint i;
|
gint npaths = 0;
|
gchar **pvt_split = NULL;
|
GError *gerror = NULL;
|
gint appbaselen = -1;
|
|
/*
|
* We use the low-level domain assemblies lock, since this is called from
|
* assembly loads hooks, which means this thread might hold the loader lock.
|
*/
|
mono_domain_assemblies_lock (domain);
|
|
if (!domain->setup) {
|
mono_domain_assemblies_unlock (domain);
|
return;
|
}
|
|
if ((domain->search_path != NULL) && !domain->setup->path_changed) {
|
mono_domain_assemblies_unlock (domain);
|
return;
|
}
|
setup = domain->setup;
|
if (!setup->application_base) {
|
mono_domain_assemblies_unlock (domain);
|
return; /* Must set application base to get private path working */
|
}
|
|
npaths++;
|
|
if (setup->private_bin_path) {
|
search_path = mono_string_to_utf8_checked (setup->private_bin_path, &error);
|
if (!mono_error_ok (&error)) { /*FIXME maybe we should bubble up the error.*/
|
g_warning ("Could not decode AppDomain search path since it contains invalid characters");
|
mono_error_cleanup (&error);
|
mono_domain_assemblies_unlock (domain);
|
return;
|
}
|
}
|
|
if (domain->private_bin_path) {
|
if (search_path == NULL)
|
search_path = domain->private_bin_path;
|
else {
|
gchar *tmp2 = search_path;
|
search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL);
|
g_free (tmp2);
|
}
|
}
|
|
if (search_path) {
|
/*
|
* As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of
|
* directories relative to ApplicationBase separated by semicolons (see
|
* http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx)
|
* The loop below copes with the fact that some Unix applications may use ':' (or
|
* System.IO.Path.PathSeparator) as the path search separator. We replace it with
|
* ';' for the subsequent split.
|
*
|
* The issue was reported in bug #81446
|
*/
|
|
#ifndef TARGET_WIN32
|
gint slen;
|
|
slen = strlen (search_path);
|
for (i = 0; i < slen; i++)
|
if (search_path [i] == ':')
|
search_path [i] = ';';
|
#endif
|
|
pvt_split = g_strsplit (search_path, ";", 1000);
|
g_free (search_path);
|
for (tmp = pvt_split; *tmp; tmp++, npaths++);
|
}
|
|
if (!npaths) {
|
if (pvt_split)
|
g_strfreev (pvt_split);
|
/*
|
* Don't do this because the first time is called, the domain
|
* setup is not finished.
|
*
|
* domain->search_path = g_malloc (sizeof (char *));
|
* domain->search_path [0] = NULL;
|
*/
|
mono_domain_assemblies_unlock (domain);
|
return;
|
}
|
|
if (domain->search_path)
|
g_strfreev (domain->search_path);
|
|
tmp = (gchar **)g_malloc ((npaths + 1) * sizeof (gchar *));
|
tmp [npaths] = NULL;
|
|
*tmp = mono_string_to_utf8_checked (setup->application_base, &error);
|
if (!mono_error_ok (&error)) {
|
mono_error_cleanup (&error);
|
g_strfreev (pvt_split);
|
g_free (tmp);
|
|
mono_domain_assemblies_unlock (domain);
|
return;
|
}
|
|
domain->search_path = tmp;
|
|
/* FIXME: is this needed? */
|
if (strncmp (*tmp, "file://", 7) == 0) {
|
gchar *file = *tmp;
|
gchar *uri = *tmp;
|
gchar *tmpuri;
|
|
if (uri [7] != '/')
|
uri = g_strdup_printf ("file:///%s", uri + 7);
|
|
tmpuri = uri;
|
uri = mono_escape_uri_string (tmpuri);
|
*tmp = g_filename_from_uri (uri, NULL, &gerror);
|
g_free (uri);
|
|
if (tmpuri != file)
|
g_free (tmpuri);
|
|
if (gerror != NULL) {
|
g_warning ("%s\n", gerror->message);
|
g_error_free (gerror);
|
*tmp = file;
|
} else {
|
g_free (file);
|
}
|
}
|
|
for (i = 1; pvt_split && i < npaths; i++) {
|
if (g_path_is_absolute (pvt_split [i - 1])) {
|
tmp [i] = g_strdup (pvt_split [i - 1]);
|
} else {
|
tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
|
}
|
|
if (strchr (tmp [i], '.')) {
|
gchar *reduced;
|
gchar *freeme;
|
|
reduced = mono_path_canonicalize (tmp [i]);
|
if (appbaselen == -1)
|
appbaselen = strlen (tmp [0]);
|
|
if (strncmp (tmp [0], reduced, appbaselen)) {
|
g_free (reduced);
|
g_free (tmp [i]);
|
tmp [i] = g_strdup ("");
|
continue;
|
}
|
|
freeme = tmp [i];
|
tmp [i] = reduced;
|
g_free (freeme);
|
}
|
}
|
|
if (setup->private_bin_path_probe != NULL) {
|
g_free (tmp [0]);
|
tmp [0] = g_strdup ("");
|
}
|
|
domain->setup->path_changed = FALSE;
|
|
g_strfreev (pvt_split);
|
|
mono_domain_assemblies_unlock (domain);
|
}
|
|
#ifdef DISABLE_SHADOW_COPY
|
gboolean
|
mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
|
{
|
return FALSE;
|
}
|
|
char *
|
mono_make_shadow_copy (const char *filename, MonoError *error)
|
{
|
error_init (error);
|
return (char *) filename;
|
}
|
#else
|
static gboolean
|
shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
|
{
|
guint16 *orig, *dest;
|
gboolean copy_result;
|
gint32 copy_error;
|
|
strcpy (src + srclen - tail_len, extension);
|
|
if (IS_PORTABILITY_CASE) {
|
gchar *file = mono_portability_find_file (src, TRUE);
|
|
if (file == NULL)
|
return TRUE;
|
|
g_free (file);
|
} else if (!g_file_test (src, G_FILE_TEST_IS_REGULAR)) {
|
return TRUE;
|
}
|
|
orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
|
|
strcpy (target + targetlen - tail_len, extension);
|
dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
|
|
mono_w32file_delete (dest);
|
|
copy_result = mono_w32file_copy (orig, dest, TRUE, ©_error);
|
|
/* Fix for bug #556884 - make sure the files have the correct mode so that they can be
|
* overwritten when updated in their original locations. */
|
if (copy_result)
|
copy_result = mono_w32file_set_attributes (dest, FILE_ATTRIBUTE_NORMAL);
|
|
g_free (orig);
|
g_free (dest);
|
|
return copy_result;
|
}
|
|
static gint32
|
get_cstring_hash (const char *str)
|
{
|
int len, i;
|
const char *p;
|
gint32 h = 0;
|
|
if (!str || !str [0])
|
return 0;
|
|
len = strlen (str);
|
p = str;
|
for (i = 0; i < len; i++) {
|
h = (h << 5) - h + *p;
|
p++;
|
}
|
|
return h;
|
}
|
|
/*
|
* Returned memory is malloc'd. Called must free it
|
*/
|
static char *
|
get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error)
|
{
|
MonoAppDomainSetup *setup;
|
char *cache_path, *appname;
|
char *userdir;
|
char *location;
|
|
error_init (error);
|
|
setup = domain->setup;
|
if (setup->cache_path != NULL && setup->application_name != NULL) {
|
cache_path = mono_string_to_utf8_checked (setup->cache_path, error);
|
return_val_if_nok (error, NULL);
|
|
#ifndef TARGET_WIN32
|
{
|
gint i;
|
for (i = strlen (cache_path) - 1; i >= 0; i--)
|
if (cache_path [i] == '\\')
|
cache_path [i] = '/';
|
}
|
#endif
|
|
appname = mono_string_to_utf8_checked (setup->application_name, error);
|
if (!mono_error_ok (error)) {
|
g_free (cache_path);
|
return NULL;
|
}
|
|
location = g_build_filename (cache_path, appname, "assembly", "shadow", NULL);
|
g_free (appname);
|
g_free (cache_path);
|
} else {
|
userdir = g_strdup_printf ("%s-mono-cachepath", g_get_user_name ());
|
location = g_build_filename (g_get_tmp_dir (), userdir, "assembly", "shadow", NULL);
|
g_free (userdir);
|
}
|
return location;
|
}
|
|
static char *
|
get_shadow_assembly_location (const char *filename, MonoError *error)
|
{
|
gint32 hash = 0, hash2 = 0;
|
char name_hash [9];
|
char path_hash [30];
|
char *bname = g_path_get_basename (filename);
|
char *dirname = g_path_get_dirname (filename);
|
char *location, *tmploc;
|
MonoDomain *domain = mono_domain_get ();
|
|
error_init (error);
|
|
hash = get_cstring_hash (bname);
|
hash2 = get_cstring_hash (dirname);
|
g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
|
g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial);
|
tmploc = get_shadow_assembly_location_base (domain, error);
|
if (!mono_error_ok (error)) {
|
g_free (bname);
|
g_free (dirname);
|
return NULL;
|
}
|
|
location = g_build_filename (tmploc, name_hash, path_hash, bname, NULL);
|
g_free (tmploc);
|
g_free (bname);
|
g_free (dirname);
|
return location;
|
}
|
|
static gboolean
|
private_file_needs_copying (const char *src, struct stat *sbuf_src, char *dest)
|
{
|
struct stat sbuf_dest;
|
gchar *stat_src;
|
gchar *real_src = mono_portability_find_file (src, TRUE);
|
|
if (!real_src)
|
stat_src = (gchar*)src;
|
else
|
stat_src = real_src;
|
|
if (stat (stat_src, sbuf_src) == -1) {
|
time_t tnow = time (NULL);
|
|
if (real_src)
|
g_free (real_src);
|
|
memset (sbuf_src, 0, sizeof (*sbuf_src));
|
sbuf_src->st_mtime = tnow;
|
sbuf_src->st_atime = tnow;
|
return TRUE;
|
}
|
|
if (real_src)
|
g_free (real_src);
|
|
if (stat (dest, &sbuf_dest) == -1)
|
return TRUE;
|
|
if (sbuf_src->st_size == sbuf_dest.st_size &&
|
sbuf_src->st_mtime == sbuf_dest.st_mtime)
|
return FALSE;
|
|
return TRUE;
|
}
|
|
static gboolean
|
shadow_copy_create_ini (const char *shadow, const char *filename)
|
{
|
char *dir_name;
|
char *ini_file;
|
guint16 *u16_ini;
|
gboolean result;
|
guint32 n;
|
HANDLE *handle;
|
gchar *full_path;
|
|
dir_name = g_path_get_dirname (shadow);
|
ini_file = g_build_filename (dir_name, "__AssemblyInfo__.ini", NULL);
|
g_free (dir_name);
|
if (g_file_test (ini_file, G_FILE_TEST_IS_REGULAR)) {
|
g_free (ini_file);
|
return TRUE;
|
}
|
|
u16_ini = g_utf8_to_utf16 (ini_file, strlen (ini_file), NULL, NULL, NULL);
|
g_free (ini_file);
|
if (!u16_ini) {
|
return FALSE;
|
}
|
handle = (void **)mono_w32file_create (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, CREATE_NEW, FileAttributes_Normal);
|
g_free (u16_ini);
|
if (handle == INVALID_HANDLE_VALUE) {
|
return FALSE;
|
}
|
|
full_path = mono_path_resolve_symlinks (filename);
|
result = mono_w32file_write (handle, full_path, strlen (full_path), &n);
|
g_free (full_path);
|
mono_w32file_close (handle);
|
return result;
|
}
|
|
gboolean
|
mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
|
{
|
MonoError error;
|
MonoAppDomainSetup *setup;
|
gchar *all_dirs;
|
gchar **dir_ptr;
|
gchar **directories;
|
gchar *shadow_status_string;
|
gchar *base_dir;
|
gboolean shadow_enabled;
|
gboolean found = FALSE;
|
|
if (domain == NULL)
|
return FALSE;
|
|
setup = domain->setup;
|
if (setup == NULL || setup->shadow_copy_files == NULL)
|
return FALSE;
|
|
shadow_status_string = mono_string_to_utf8_checked (setup->shadow_copy_files, &error);
|
if (!mono_error_ok (&error)) {
|
mono_error_cleanup (&error);
|
return FALSE;
|
}
|
shadow_enabled = !g_ascii_strncasecmp (shadow_status_string, "true", 4);
|
g_free (shadow_status_string);
|
|
if (!shadow_enabled)
|
return FALSE;
|
|
if (setup->shadow_copy_directories == NULL)
|
return TRUE;
|
|
/* Is dir_name a shadow_copy destination already? */
|
base_dir = get_shadow_assembly_location_base (domain, &error);
|
if (!mono_error_ok (&error)) {
|
mono_error_cleanup (&error);
|
return FALSE;
|
}
|
|
if (strstr (dir_name, base_dir)) {
|
g_free (base_dir);
|
return TRUE;
|
}
|
g_free (base_dir);
|
|
all_dirs = mono_string_to_utf8_checked (setup->shadow_copy_directories, &error);
|
if (!mono_error_ok (&error)) {
|
mono_error_cleanup (&error);
|
return FALSE;
|
}
|
|
directories = g_strsplit (all_dirs, G_SEARCHPATH_SEPARATOR_S, 1000);
|
dir_ptr = directories;
|
while (*dir_ptr) {
|
if (**dir_ptr != '\0' && !strcmp (*dir_ptr, dir_name)) {
|
found = TRUE;
|
break;
|
}
|
dir_ptr++;
|
}
|
g_strfreev (directories);
|
g_free (all_dirs);
|
return found;
|
}
|
|
/*
|
This function raises exceptions so it can cause as sorts of nasty stuff if called
|
while holding a lock.
|
Returns old file name if shadow copy is disabled, new shadow copy file name if successful
|
or NULL if source file not found.
|
FIXME bubble up the error instead of raising it here
|
*/
|
char *
|
mono_make_shadow_copy (const char *filename, MonoError *oerror)
|
{
|
MonoError error;
|
gchar *sibling_source, *sibling_target;
|
gint sibling_source_len, sibling_target_len;
|
guint16 *orig, *dest;
|
guint32 attrs;
|
char *shadow;
|
gboolean copy_result;
|
struct stat src_sbuf;
|
struct utimbuf utbuf;
|
char *dir_name = g_path_get_dirname (filename);
|
MonoDomain *domain = mono_domain_get ();
|
char *shadow_dir;
|
gint32 copy_error;
|
|
error_init (oerror);
|
|
set_domain_search_path (domain);
|
|
if (!mono_is_shadow_copy_enabled (domain, dir_name)) {
|
g_free (dir_name);
|
return (char *) filename;
|
}
|
|
/* Is dir_name a shadow_copy destination already? */
|
shadow_dir = get_shadow_assembly_location_base (domain, &error);
|
if (!mono_error_ok (&error)) {
|
mono_error_cleanup (&error);
|
g_free (dir_name);
|
mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in shadow directory name).");
|
return NULL;
|
}
|
|
if (strstr (dir_name, shadow_dir)) {
|
g_free (shadow_dir);
|
g_free (dir_name);
|
return (char *) filename;
|
}
|
g_free (shadow_dir);
|
g_free (dir_name);
|
|
shadow = get_shadow_assembly_location (filename, &error);
|
if (!mono_error_ok (&error)) {
|
mono_error_cleanup (&error);
|
mono_error_set_execution_engine (oerror, "Failed to create shadow copy (invalid characters in file name).");
|
return NULL;
|
}
|
|
if (g_ensure_directory_exists (shadow) == FALSE) {
|
g_free (shadow);
|
mono_error_set_execution_engine (oerror, "Failed to create shadow copy (ensure directory exists).");
|
return NULL;
|
}
|
|
if (!private_file_needs_copying (filename, &src_sbuf, shadow))
|
return (char*) shadow;
|
|
orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
|
dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
|
mono_w32file_delete (dest);
|
|
/* Fix for bug #17066 - make sure we can read the file. if not then don't error but rather
|
* let the assembly fail to load. This ensures you can do Type.GetType("NS.T, NonExistantAssembly)
|
* and not have it runtime error" */
|
attrs = mono_w32file_get_attributes (orig);
|
if (attrs == INVALID_FILE_ATTRIBUTES) {
|
g_free (shadow);
|
return (char *)filename;
|
}
|
|
copy_result = mono_w32file_copy (orig, dest, TRUE, ©_error);
|
|
/* Fix for bug #556884 - make sure the files have the correct mode so that they can be
|
* overwritten when updated in their original locations. */
|
if (copy_result)
|
copy_result = mono_w32file_set_attributes (dest, FILE_ATTRIBUTE_NORMAL);
|
|
g_free (dest);
|
g_free (orig);
|
|
if (copy_result == FALSE) {
|
g_free (shadow);
|
|
/* Fix for bug #17251 - if file not found try finding assembly by other means (it is not fatal error) */
|
if (mono_w32error_get_last() == ERROR_FILE_NOT_FOUND || mono_w32error_get_last() == ERROR_PATH_NOT_FOUND)
|
return NULL; /* file not found, shadow copy failed */
|
|
mono_error_set_execution_engine (oerror, "Failed to create shadow copy (mono_w32file_copy).");
|
return NULL;
|
}
|
|
/* attempt to copy .mdb, .config if they exist */
|
sibling_source = g_strconcat (filename, ".config", NULL);
|
sibling_source_len = strlen (sibling_source);
|
sibling_target = g_strconcat (shadow, ".config", NULL);
|
sibling_target_len = strlen (sibling_target);
|
|
copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
|
if (copy_result)
|
copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".pdb", sibling_target, sibling_target_len, 11);
|
if (copy_result)
|
copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
|
|
g_free (sibling_source);
|
g_free (sibling_target);
|
|
if (!copy_result) {
|
g_free (shadow);
|
mono_error_set_execution_engine (oerror, "Failed to create shadow copy of sibling data (mono_w32file_copy).");
|
return NULL;
|
}
|
|
/* Create a .ini file containing the original assembly location */
|
if (!shadow_copy_create_ini (shadow, filename)) {
|
g_free (shadow);
|
mono_error_set_execution_engine (oerror, "Failed to create shadow copy .ini file.");
|
return NULL;
|
}
|
|
utbuf.actime = src_sbuf.st_atime;
|
utbuf.modtime = src_sbuf.st_mtime;
|
utime (shadow, &utbuf);
|
|
return shadow;
|
}
|
#endif /* DISABLE_SHADOW_COPY */
|
|
/**
|
* mono_domain_from_appdomain:
|
*/
|
MonoDomain *
|
mono_domain_from_appdomain (MonoAppDomain *appdomain_raw)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
MONO_HANDLE_DCL (MonoAppDomain, appdomain);
|
MonoDomain *result = mono_domain_from_appdomain_handle (appdomain);
|
HANDLE_FUNCTION_RETURN_VAL (result);
|
}
|
|
MonoDomain *
|
mono_domain_from_appdomain_handle (MonoAppDomainHandle appdomain)
|
{
|
HANDLE_FUNCTION_ENTER ();
|
MonoDomain *dom = NULL;
|
if (MONO_HANDLE_IS_NULL (appdomain))
|
goto leave;
|
|
if (mono_class_is_transparent_proxy (mono_handle_class (appdomain))) {
|
MonoTransparentProxyHandle tp = MONO_HANDLE_CAST (MonoTransparentProxy, appdomain);
|
MonoRealProxyHandle rp = MONO_HANDLE_NEW_GET (MonoRealProxy, tp, rp);
|
|
dom = mono_domain_get_by_id (MONO_HANDLE_GETVAL (rp, target_domain_id));
|
} else
|
dom = MONO_HANDLE_GETVAL (appdomain, data);
|
|
leave:
|
HANDLE_FUNCTION_RETURN_VAL (dom);
|
}
|
|
|
static gboolean
|
try_load_from (MonoAssembly **assembly,
|
const gchar *path1, const gchar *path2,
|
const gchar *path3, const gchar *path4,
|
gboolean refonly, gboolean is_private,
|
MonoAssemblyCandidatePredicate predicate, gpointer user_data)
|
{
|
gchar *fullpath;
|
gboolean found = FALSE;
|
|
*assembly = NULL;
|
fullpath = g_build_filename (path1, path2, path3, path4, NULL);
|
|
if (IS_PORTABILITY_SET) {
|
gchar *new_fullpath = mono_portability_find_file (fullpath, TRUE);
|
if (new_fullpath) {
|
g_free (fullpath);
|
fullpath = new_fullpath;
|
found = TRUE;
|
}
|
} else
|
found = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR);
|
|
if (found)
|
*assembly = mono_assembly_open_predicate (fullpath, refonly, FALSE, predicate, user_data, NULL);
|
|
g_free (fullpath);
|
return (*assembly != NULL);
|
}
|
|
static MonoAssembly *
|
real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly, MonoAssemblyCandidatePredicate predicate, gpointer user_data)
|
{
|
MonoAssembly *result = NULL;
|
gchar **path;
|
gchar *filename;
|
const gchar *local_culture;
|
gint len;
|
gboolean is_private = FALSE;
|
|
if (!culture || *culture == '\0') {
|
local_culture = "";
|
} else {
|
local_culture = culture;
|
}
|
|
filename = g_strconcat (name, ".dll", NULL);
|
len = strlen (filename);
|
|
for (path = search_path; *path; path++) {
|
if (**path == '\0') {
|
is_private = TRUE;
|
continue; /* Ignore empty ApplicationBase */
|
}
|
|
/* See test cases in bug #58992 and bug #57710 */
|
/* 1st try: [culture]/[name].dll (culture may be empty) */
|
strcpy (filename + len - 4, ".dll");
|
if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private, predicate, user_data))
|
break;
|
|
/* 2nd try: [culture]/[name].exe (culture may be empty) */
|
strcpy (filename + len - 4, ".exe");
|
if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private, predicate, user_data))
|
break;
|
|
/* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
|
strcpy (filename + len - 4, ".dll");
|
if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private, predicate, user_data))
|
break;
|
|
/* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
|
strcpy (filename + len - 4, ".exe");
|
if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private, predicate, user_data))
|
break;
|
}
|
|
g_free (filename);
|
return result;
|
}
|
|
/*
|
* Try loading the assembly from ApplicationBase and PrivateBinPath
|
* and then from assemblies_path if any.
|
* LOCKING: This is called from the assembly loading code, which means the caller
|
* might hold the loader lock. Thus, this function must not acquire the domain lock.
|
*/
|
static MonoAssembly *
|
mono_domain_assembly_preload (MonoAssemblyName *aname,
|
gchar **assemblies_path,
|
gpointer user_data)
|
{
|
MonoDomain *domain = mono_domain_get ();
|
MonoAssembly *result = NULL;
|
gboolean refonly = GPOINTER_TO_UINT (user_data);
|
|
set_domain_search_path (domain);
|
|
MonoAssemblyCandidatePredicate predicate = NULL;
|
void* predicate_ud = NULL;
|
#if !defined(DISABLE_DESKTOP_LOADER)
|
if (G_LIKELY (mono_loader_get_strict_strong_names ())) {
|
predicate = &mono_assembly_candidate_predicate_sn_same_name;
|
predicate_ud = aname;
|
}
|
#endif
|
if (domain->search_path && domain->search_path [0] != NULL) {
|
if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY)) {
|
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Domain %s search path is:", domain->friendly_name);
|
for (int i = 0; domain->search_path [i]; i++) {
|
const char *p = domain->search_path[i];
|
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "\tpath[%d] = '%s'", i, p);
|
}
|
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "End of domain %s search path.", domain->friendly_name);
|
}
|
result = real_load (domain->search_path, aname->culture, aname->name, refonly, predicate, predicate_ud);
|
}
|
|
if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
|
result = real_load (assemblies_path, aname->culture, aname->name, refonly, predicate, predicate_ud);
|
}
|
|
return result;
|
}
|
|
/*
|
* Check whenever a given assembly was already loaded in the current appdomain.
|
*/
|
static MonoAssembly *
|
mono_domain_assembly_search (MonoAssemblyName *aname,
|
gpointer user_data)
|
{
|
MonoDomain *domain = mono_domain_get ();
|
GSList *tmp;
|
MonoAssembly *ass;
|
gboolean refonly = GPOINTER_TO_UINT (user_data);
|
const gboolean strong_name = aname->public_key_token[0] != 0;
|
/* If it's not a strong name, any version that has the right simple
|
* name is good enough to satisfy the request. .NET Framework also
|
* ignores case differences in this case. */
|
const MonoAssemblyNameEqFlags eq_flags = strong_name ? MONO_ANAME_EQ_IGNORE_CASE :
|
(MONO_ANAME_EQ_IGNORE_PUBKEY | MONO_ANAME_EQ_IGNORE_VERSION | MONO_ANAME_EQ_IGNORE_CASE);
|
|
mono_domain_assemblies_lock (domain);
|
for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
|
ass = (MonoAssembly *)tmp->data;
|
/* Dynamic assemblies can't match here in MS.NET */
|
if (assembly_is_dynamic (ass) || refonly != ass->ref_only || !mono_assembly_names_equal_flags (aname, &ass->aname, eq_flags))
|
continue;
|
|
mono_domain_assemblies_unlock (domain);
|
return ass;
|
}
|
mono_domain_assemblies_unlock (domain);
|
|
return NULL;
|
}
|
|
MonoReflectionAssemblyHandle
|
ves_icall_System_Reflection_Assembly_LoadFrom (MonoStringHandle fname, MonoBoolean refOnly, MonoError *error)
|
{
|
error_init (error);
|
MonoDomain *domain = mono_domain_get ();
|
char *name, *filename;
|
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
MonoReflectionAssemblyHandle result = MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE);
|
|
name = NULL;
|
result = NULL;
|
|
if (fname == NULL) {
|
mono_error_set_argument_null (error, "assemblyFile", "");
|
goto leave;
|
}
|
|
name = filename = mono_string_handle_to_utf8 (fname, error);
|
goto_if_nok (error, leave);
|
|
MonoAssembly *ass = mono_assembly_open_predicate (filename, refOnly, TRUE, NULL, NULL, &status);
|
|
if (!ass) {
|
if (status == MONO_IMAGE_IMAGE_INVALID)
|
mono_error_set_bad_image_name (error, g_strdup (name), "");
|
else
|
mono_error_set_assembly_load (error, g_strdup (name), "%s", "");
|
goto leave;
|
}
|
|
result = mono_assembly_get_object_handle (domain, ass, error);
|
|
leave:
|
g_free (name);
|
return result;
|
}
|
|
MonoReflectionAssemblyHandle
|
ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomainHandle ad,
|
MonoArrayHandle raw_assembly,
|
MonoArrayHandle raw_symbol_store, MonoObjectHandle evidence,
|
MonoBoolean refonly,
|
MonoError *error)
|
{
|
error_init (error);
|
MonoAssembly *ass;
|
MonoReflectionAssemblyHandle refass = MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE);
|
MonoDomain *domain = MONO_HANDLE_GETVAL(ad, data);
|
MonoImageOpenStatus status;
|
guint32 raw_assembly_len = mono_array_handle_length (raw_assembly);
|
|
/* Copy the data ourselves to unpin the raw assembly byte array as soon as possible */
|
char *assembly_data = (char*) g_try_malloc (raw_assembly_len);
|
if (!assembly_data) {
|
mono_error_set_out_of_memory (error, "Could not allocate %ud bytes to copy raw assembly data", raw_assembly_len);
|
return refass;
|
}
|
uint32_t gchandle;
|
mono_byte *raw_data = (mono_byte*) MONO_ARRAY_HANDLE_PIN (raw_assembly, gchar, 0, &gchandle);
|
memcpy (assembly_data, raw_data, raw_assembly_len);
|
mono_gchandle_free (gchandle); /* unpin */
|
MONO_HANDLE_ASSIGN (raw_assembly, NULL_HANDLE); /* don't reference the data anymore */
|
|
MonoImage *image = mono_image_open_from_data_full (assembly_data, raw_assembly_len, FALSE, NULL, refonly);
|
|
if (!image) {
|
mono_error_set_bad_image_name (error, g_strdup (""), "%s", "");
|
return refass;
|
}
|
|
if (!MONO_HANDLE_IS_NULL(raw_symbol_store)) {
|
guint32 symbol_len = mono_array_handle_length (raw_symbol_store);
|
uint32_t symbol_gchandle;
|
mono_byte *raw_symbol_data = (mono_byte*) MONO_ARRAY_HANDLE_PIN (raw_symbol_store, mono_byte, 0, &symbol_gchandle);
|
mono_debug_open_image_from_memory (image, raw_symbol_data, symbol_len);
|
mono_gchandle_free (symbol_gchandle);
|
}
|
|
ass = mono_assembly_load_from_full (image, "", &status, refonly);
|
|
|
if (!ass) {
|
mono_image_close (image);
|
mono_error_set_bad_image_name (error, g_strdup (""), "%s", "");
|
return refass;
|
}
|
|
/* Clear the reference added by mono_image_open_from_data_full above */
|
mono_image_close (image);
|
|
refass = mono_assembly_get_object_handle (domain, ass, error);
|
if (!MONO_HANDLE_IS_NULL(refass))
|
MONO_HANDLE_SET (refass, evidence, evidence);
|
return refass;
|
}
|
|
MonoReflectionAssemblyHandle
|
ves_icall_System_AppDomain_LoadAssembly (MonoAppDomainHandle ad, MonoStringHandle assRef, MonoObjectHandle evidence, MonoBoolean refOnly, MonoError *error)
|
{
|
error_init (error);
|
MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data);
|
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
MonoAssembly *ass;
|
MonoAssemblyName aname;
|
gchar *name = NULL;
|
gboolean parsed;
|
|
g_assert (assRef);
|
|
name = mono_string_handle_to_utf8 (assRef, error);
|
goto_if_nok (error, fail);
|
parsed = mono_assembly_name_parse (name, &aname);
|
g_free (name);
|
|
if (!parsed) {
|
MonoReflectionAssemblyHandle refass = MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE);
|
/* This is a parse error... */
|
if (!refOnly) {
|
MonoAssembly *assm = mono_try_assembly_resolve_handle (domain, assRef, NULL, refOnly, error);
|
goto_if_nok (error, fail);
|
if (assm) {
|
refass = mono_assembly_get_object_handle (domain, assm, error);
|
goto_if_nok (error, fail);
|
}
|
}
|
return refass;
|
}
|
|
ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly);
|
mono_assembly_name_free (&aname);
|
|
if (!ass) {
|
/* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */
|
if (!refOnly) {
|
ass = mono_try_assembly_resolve_handle (domain, assRef, NULL, refOnly, error);
|
goto_if_nok (error, fail);
|
}
|
if (!ass)
|
goto fail;
|
}
|
|
g_assert (ass);
|
MonoReflectionAssemblyHandle refass = mono_assembly_get_object_handle (domain, ass, error);
|
goto_if_nok (error, fail);
|
|
MONO_HANDLE_SET (refass, evidence, evidence);
|
|
return refass;
|
fail:
|
return MONO_HANDLE_CAST (MonoReflectionAssembly, NULL_HANDLE);
|
}
|
|
void
|
ves_icall_System_AppDomain_InternalUnload (gint32 domain_id, MonoError *error)
|
{
|
error_init (error);
|
MonoDomain * domain = mono_domain_get_by_id (domain_id);
|
|
if (NULL == domain) {
|
mono_error_set_execution_engine (error, "Failed to unload domain, domain id not found");
|
return;
|
}
|
|
if (domain == mono_get_root_domain ()) {
|
mono_error_set_generic_error (error, "System", "CannotUnloadAppDomainException", "The default appdomain can not be unloaded.");
|
return;
|
}
|
|
/*
|
* Unloading seems to cause problems when running NUnit/NAnt, hence
|
* this workaround.
|
*/
|
if (g_hasenv ("MONO_NO_UNLOAD"))
|
return;
|
|
MonoException *exc = NULL;
|
mono_domain_try_unload (domain, (MonoObject**)&exc, NULL);
|
if (exc)
|
mono_error_set_exception_instance (error, exc);
|
}
|
|
gboolean
|
ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id, MonoError *error)
|
{
|
error_init (error);
|
MonoDomain *domain = mono_domain_get_by_id (domain_id);
|
|
if (!domain)
|
return TRUE;
|
|
return mono_domain_is_unloading (domain);
|
}
|
|
void
|
ves_icall_System_AppDomain_DoUnhandledException (MonoExceptionHandle exc, MonoError *error)
|
{
|
error_init (error);
|
mono_unhandled_exception_checked (MONO_HANDLE_CAST (MonoObject, exc), error);
|
mono_error_assert_ok (error);
|
}
|
|
gint32
|
ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomainHandle ad,
|
MonoReflectionAssemblyHandle refass, MonoArrayHandle args,
|
MonoError *error)
|
{
|
error_init (error);
|
MonoImage *image;
|
MonoMethod *method;
|
|
g_assert (!MONO_HANDLE_IS_NULL (refass));
|
MonoAssembly *assembly = MONO_HANDLE_GETVAL (refass, assembly);
|
image = assembly->image;
|
g_assert (image);
|
|
method = mono_get_method_checked (image, mono_image_get_entry_point (image), NULL, NULL, error);
|
|
if (!method)
|
g_error ("No entry point method found in %s due to %s", image->name, mono_error_get_message (error));
|
|
if (MONO_HANDLE_IS_NULL (args)) {
|
MonoDomain *domain = MONO_HANDLE_GETVAL (ad, data);
|
MONO_HANDLE_ASSIGN (args , mono_array_new_handle (domain, mono_defaults.string_class, 0, error));
|
mono_error_assert_ok (error);
|
}
|
|
int res = mono_runtime_exec_main_checked (method, MONO_HANDLE_RAW (args), error);
|
return res;
|
}
|
|
gint32
|
ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad)
|
{
|
return ad->data->domain_id;
|
}
|
|
MonoAppDomainHandle
|
ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomainHandle ad, MonoError* error)
|
{
|
error_init (error);
|
MonoDomain *old_domain = mono_domain_get ();
|
|
if (!mono_domain_set (MONO_HANDLE_GETVAL (ad, data), FALSE)) {
|
mono_error_set_appdomain_unloaded (error);
|
return MONO_HANDLE_CAST (MonoAppDomain, NULL_HANDLE);
|
}
|
|
return MONO_HANDLE_NEW (MonoAppDomain, old_domain->domain);
|
}
|
|
MonoAppDomainHandle
|
ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid, MonoError *error)
|
{
|
MonoDomain *current_domain = mono_domain_get ();
|
MonoDomain *domain = mono_domain_get_by_id (domainid);
|
|
if (!domain || !mono_domain_set (domain, FALSE)) {
|
mono_error_set_appdomain_unloaded (error);
|
return MONO_HANDLE_CAST (MonoAppDomain, NULL_HANDLE);
|
}
|
|
return MONO_HANDLE_NEW (MonoAppDomain, current_domain->domain);
|
}
|
|
void
|
ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomainHandle ad, MonoError *error)
|
{
|
error_init (error);
|
mono_thread_push_appdomain_ref (MONO_HANDLE_GETVAL (ad, data));
|
}
|
|
void
|
ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id, MonoError *error)
|
{
|
error_init (error);
|
MonoDomain *domain = mono_domain_get_by_id (domain_id);
|
|
if (!domain) {
|
/*
|
* Raise an exception to prevent the managed code from executing a pop
|
* later.
|
*/
|
mono_error_set_appdomain_unloaded (error);
|
return;
|
}
|
|
mono_thread_push_appdomain_ref (domain);
|
}
|
|
void
|
ves_icall_System_AppDomain_InternalPopDomainRef (MonoError *error)
|
{
|
error_init (error);
|
mono_thread_pop_appdomain_ref ();
|
}
|
|
MonoAppContextHandle
|
ves_icall_System_AppDomain_InternalGetContext (MonoError *error)
|
{
|
error_init (error);
|
return mono_context_get_handle ();
|
}
|
|
MonoAppContextHandle
|
ves_icall_System_AppDomain_InternalGetDefaultContext (MonoError *error)
|
{
|
error_init (error);
|
return MONO_HANDLE_NEW (MonoAppContext, mono_domain_get ()->default_context);
|
}
|
|
MonoAppContextHandle
|
ves_icall_System_AppDomain_InternalSetContext (MonoAppContextHandle mc, MonoError *error)
|
{
|
error_init (error);
|
MonoAppContextHandle old_context = mono_context_get_handle ();
|
|
mono_context_set_handle (mc);
|
|
return old_context;
|
}
|
|
MonoStringHandle
|
ves_icall_System_AppDomain_InternalGetProcessGuid (MonoStringHandle newguid, MonoError *error)
|
{
|
error_init (error);
|
MonoDomain* mono_root_domain = mono_get_root_domain ();
|
mono_domain_lock (mono_root_domain);
|
if (process_guid_set) {
|
mono_domain_unlock (mono_root_domain);
|
return mono_string_new_utf16_handle (mono_domain_get (), process_guid, sizeof(process_guid)/2, error);
|
}
|
uint32_t gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, newguid), TRUE);
|
memcpy (process_guid, mono_string_chars(MONO_HANDLE_RAW (newguid)), sizeof(process_guid));
|
mono_gchandle_free (gchandle);
|
process_guid_set = TRUE;
|
mono_domain_unlock (mono_root_domain);
|
return newguid;
|
}
|
|
/**
|
* mono_domain_is_unloading:
|
*/
|
gboolean
|
mono_domain_is_unloading (MonoDomain *domain)
|
{
|
if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
|
return TRUE;
|
else
|
return FALSE;
|
}
|
|
static void
|
clear_cached_vtable (MonoVTable *vtable)
|
{
|
MonoClass *klass = vtable->klass;
|
MonoDomain *domain = vtable->domain;
|
MonoClassRuntimeInfo *runtime_info;
|
void *data;
|
|
runtime_info = klass->runtime_info;
|
if (runtime_info && runtime_info->max_domain >= domain->domain_id)
|
runtime_info->domain_vtables [domain->domain_id] = NULL;
|
if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
|
mono_gc_free_fixed (data);
|
}
|
|
static G_GNUC_UNUSED void
|
zero_static_data (MonoVTable *vtable)
|
{
|
MonoClass *klass = vtable->klass;
|
void *data;
|
|
if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
|
mono_gc_bzero_aligned (data, mono_class_data_size (klass));
|
}
|
|
typedef struct unload_data {
|
gboolean done;
|
MonoDomain *domain;
|
char *failure_reason;
|
gint32 refcount;
|
} unload_data;
|
|
static void
|
unload_data_unref (unload_data *data)
|
{
|
gint32 count;
|
do {
|
mono_atomic_load_acquire (count, gint32, &data->refcount);
|
g_assert (count >= 1 && count <= 2);
|
if (count == 1) {
|
g_free (data);
|
return;
|
}
|
} while (mono_atomic_cas_i32 (&data->refcount, count - 1, count) != count);
|
}
|
|
static void
|
deregister_reflection_info_roots_from_list (MonoImage *image)
|
{
|
GSList *list = image->reflection_info_unregister_classes;
|
|
while (list) {
|
MonoClass *klass = (MonoClass *)list->data;
|
|
mono_class_free_ref_info (klass);
|
|
list = list->next;
|
}
|
|
image->reflection_info_unregister_classes = NULL;
|
}
|
|
static void
|
deregister_reflection_info_roots (MonoDomain *domain)
|
{
|
GSList *list;
|
|
mono_domain_assemblies_lock (domain);
|
for (list = domain->domain_assemblies; list; list = list->next) {
|
MonoAssembly *assembly = (MonoAssembly *)list->data;
|
MonoImage *image = assembly->image;
|
int i;
|
|
/*
|
* No need to take the image lock here since dynamic images are appdomain bound and
|
* at this point the mutator is gone. Taking the image lock here would mean
|
* promoting it from a simple lock to a complex lock, which we better avoid if
|
* possible.
|
*/
|
if (image_is_dynamic (image))
|
deregister_reflection_info_roots_from_list (image);
|
|
for (i = 0; i < image->module_count; ++i) {
|
MonoImage *module = image->modules [i];
|
if (module && image_is_dynamic (module))
|
deregister_reflection_info_roots_from_list (module);
|
}
|
}
|
mono_domain_assemblies_unlock (domain);
|
}
|
|
static gsize WINAPI
|
unload_thread_main (void *arg)
|
{
|
MonoError error;
|
unload_data *data = (unload_data*)arg;
|
MonoDomain *domain = data->domain;
|
MonoInternalThread *internal;
|
int i;
|
|
internal = mono_thread_internal_current ();
|
|
MonoString *thread_name_str = mono_string_new_checked (mono_domain_get (), "Domain unloader", &error);
|
if (is_ok (&error))
|
mono_thread_set_name_internal (internal, thread_name_str, TRUE, FALSE, &error);
|
if (!is_ok (&error)) {
|
data->failure_reason = g_strdup (mono_error_get_message (&error));
|
mono_error_cleanup (&error);
|
goto failure;
|
}
|
|
/*
|
* FIXME: Abort our parent thread last, so we can return a failure
|
* indication if aborting times out.
|
*/
|
if (!mono_threads_abort_appdomain_threads (domain, -1)) {
|
data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
|
goto failure;
|
}
|
|
if (!mono_threadpool_remove_domain_jobs (domain, -1)) {
|
data->failure_reason = g_strdup_printf ("Cleanup of threadpool jobs of domain %s timed out.", domain->friendly_name);
|
goto failure;
|
}
|
|
/* Finalize all finalizable objects in the doomed appdomain */
|
if (!mono_domain_finalize (domain, -1)) {
|
data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
|
goto failure;
|
}
|
|
/* Clear references to our vtables in class->runtime_info.
|
* We also hold the loader lock because we're going to change
|
* class->runtime_info.
|
*/
|
|
mono_loader_lock (); //FIXME why do we need the loader lock here?
|
mono_domain_lock (domain);
|
/*
|
* We need to make sure that we don't have any remsets
|
* pointing into static data of the to-be-freed domain because
|
* at the next collections they would be invalid. So what we
|
* do is we first zero all static data and then do a minor
|
* collection. Because all references in the static data will
|
* now be null we won't do any unnecessary copies and after
|
* the collection there won't be any more remsets.
|
*/
|
for (i = 0; i < domain->class_vtable_array->len; ++i)
|
zero_static_data ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i));
|
mono_gc_collect (0);
|
for (i = 0; i < domain->class_vtable_array->len; ++i)
|
clear_cached_vtable ((MonoVTable *)g_ptr_array_index (domain->class_vtable_array, i));
|
deregister_reflection_info_roots (domain);
|
|
mono_assembly_cleanup_domain_bindings (domain->domain_id);
|
|
mono_domain_unlock (domain);
|
mono_loader_unlock ();
|
|
domain->state = MONO_APPDOMAIN_UNLOADED;
|
|
/* printf ("UNLOADED %s.\n", domain->friendly_name); */
|
|
/* remove from the handle table the items related to this domain */
|
mono_gchandle_free_domain (domain);
|
|
mono_domain_free (domain, FALSE);
|
|
mono_gc_collect (mono_gc_max_generation ());
|
|
mono_atomic_store_release (&data->done, TRUE);
|
unload_data_unref (data);
|
return 0;
|
|
failure:
|
mono_atomic_store_release (&data->done, TRUE);
|
unload_data_unref (data);
|
return 1;
|
}
|
|
/**
|
* mono_domain_unload:
|
* \param domain The domain to unload
|
*
|
* Unloads an appdomain. Follows the process outlined in the comment
|
* for \c mono_domain_try_unload.
|
*/
|
void
|
mono_domain_unload (MonoDomain *domain)
|
{
|
MonoObject *exc = NULL;
|
mono_domain_try_unload (domain, &exc, NULL);
|
}
|
|
static MonoThreadInfoWaitRet
|
guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
|
{
|
MonoThreadInfoWaitRet result;
|
|
MONO_ENTER_GC_SAFE;
|
result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
|
MONO_EXIT_GC_SAFE;
|
|
return result;
|
}
|
|
/**
|
* mono_domain_try_unload:
|
* \param domain The domain to unload
|
* \param exc Exception information
|
* \param callback Passes exception information back to caller before domain is unloaded
|
*
|
* NOTE: If the callback param is not null the domain unload will continue after executing the callback.
|
*
|
* Unloads an appdomain. Follows the process outlined in:
|
* http://blogs.gotdotnet.com/cbrumme
|
*
|
* If doing things the 'right' way is too hard or complex, we do it the
|
* 'simple' way, which means do everything needed to avoid crashes and
|
* memory leaks, but not much else.
|
*
|
* It is required to pass a valid reference to the exc argument, upon return
|
* from this function *exc will be set to the exception thrown, if any.
|
*
|
* If this method is not called from an icall (embedded scenario for instance),
|
* it must not be called with any managed frames on the stack, since the unload
|
* process could end up trying to abort the current thread.
|
*/
|
void
|
mono_domain_try_unload (MonoDomain *domain, MonoObject **exc, MonoUnityExceptionFunc callback)
|
{
|
MonoError error;
|
MonoThreadHandle *thread_handle;
|
MonoAppDomainState prev_state;
|
MonoMethod *method;
|
unload_data *thread_data;
|
MonoInternalThread *internal;
|
MonoDomain *caller_domain = mono_domain_get ();
|
|
/* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, mono_native_thread_id_get ()); */
|
|
/* Atomically change our state to UNLOADING */
|
prev_state = (MonoAppDomainState)mono_atomic_cas_i32 ((gint32*)&domain->state,
|
MONO_APPDOMAIN_UNLOADING_START,
|
MONO_APPDOMAIN_CREATED);
|
if (prev_state != MONO_APPDOMAIN_CREATED) {
|
switch (prev_state) {
|
case MONO_APPDOMAIN_UNLOADING_START:
|
case MONO_APPDOMAIN_UNLOADING:
|
*exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded.");
|
return;
|
case MONO_APPDOMAIN_UNLOADED:
|
*exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded.");
|
return;
|
default:
|
g_warning ("Invalid appdomain state %d", prev_state);
|
g_assert_not_reached ();
|
}
|
}
|
|
mono_domain_set (domain, FALSE);
|
/* Notify OnDomainUnload listeners */
|
method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1);
|
g_assert (method);
|
|
mono_runtime_try_invoke (method, domain->domain, NULL, exc, &error);
|
|
if (!mono_error_ok (&error)) {
|
if (*exc)
|
mono_error_cleanup (&error);
|
else
|
*exc = (MonoObject*)mono_error_convert_to_exception (&error);
|
}
|
|
if (*exc) {
|
if (callback != NULL)
|
callback (*exc);
|
else {
|
/* Roll back the state change */
|
domain->state = MONO_APPDOMAIN_CREATED;
|
mono_domain_set (caller_domain, FALSE);
|
return;
|
}
|
}
|
mono_domain_set (caller_domain, FALSE);
|
|
thread_data = g_new0 (unload_data, 1);
|
thread_data->domain = domain;
|
thread_data->failure_reason = NULL;
|
thread_data->done = FALSE;
|
thread_data->refcount = 2; /*Must be 2: unload thread + initiator */
|
|
/*The managed callback finished successfully, now we start tearing down the appdomain*/
|
domain->state = MONO_APPDOMAIN_UNLOADING;
|
/*
|
* First we create a separate thread for unloading, since
|
* we might have to abort some threads, including the current one.
|
*
|
* Have to attach to the runtime so shutdown can wait for this thread.
|
*
|
* Force it to be attached to avoid racing during shutdown.
|
*/
|
internal = mono_thread_create_internal (mono_get_root_domain (), unload_thread_main, thread_data, MONO_THREAD_CREATE_FLAGS_FORCE_CREATE, &error);
|
mono_error_assert_ok (&error);
|
|
thread_handle = mono_threads_open_thread_handle (internal->handle);
|
|
/* Wait for the thread */
|
while (!thread_data->done && guarded_wait (thread_handle, MONO_INFINITE_WAIT, TRUE) == MONO_THREAD_INFO_WAIT_RET_ALERTED) {
|
if (mono_thread_internal_has_appdomain_ref (mono_thread_internal_current (), domain) && (mono_thread_interruption_requested ())) {
|
/* The unload thread tries to abort us */
|
/* The icall wrapper will execute the abort */
|
mono_threads_close_thread_handle (thread_handle);
|
unload_data_unref (thread_data);
|
return;
|
}
|
}
|
|
mono_threads_close_thread_handle (thread_handle);
|
|
if (thread_data->failure_reason) {
|
/* Roll back the state change */
|
domain->state = MONO_APPDOMAIN_CREATED;
|
|
g_warning ("%s", thread_data->failure_reason);
|
|
*exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain (thread_data->failure_reason);
|
|
g_free (thread_data->failure_reason);
|
thread_data->failure_reason = NULL;
|
}
|
|
unload_data_unref (thread_data);
|
}
|