diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc index 5ba34d5dcd5..c854628891b 100644 --- a/src/monodroid/jni/embedded-assemblies.cc +++ b/src/monodroid/jni/embedded-assemblies.cc @@ -130,21 +130,45 @@ EmbeddedAssemblies::get_assembly_data (const MonoBundledAssembly *e, char*& asse } } +#if defined (NET6) MonoAssembly* -#if defined (NET6) && defined (NET6_ALC_WORKS) -EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error) -#else // !(def NET6 && def NET6_ALC_WORKS) -EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only) -#endif // def NET6 && def NET6_ALC_WORKS +EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, [[maybe_unused]] MonoError *error) { -#if defined (NET6) - // NET6 doesn't support reference-only loads, define the variable here to minimize ifdefs in the code below -#if defined (NET6_ALC_WORKS) - constexpr bool ref_only = false; -#else // def NET6_ALC_WORKS - ref_only = false; -#endif // ndef NET6_ALC_WORKS + auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* { + return mono_image_open_from_data_alc ( + alc_gchandle, + assembly_data, + assembly_data_size, + 0 /* need_copy */, + nullptr /* status */, + name + ); + }; + + return open_from_bundles (aname, loader, false /* ref_only */); +} #endif // def NET6 + +MonoAssembly* +EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only) +{ + auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* { + return mono_image_open_from_data_with_name ( + assembly_data, + assembly_data_size, + 0, + nullptr, + ref_only, + name + ); + }; + + return open_from_bundles (aname, loader, ref_only); +} + +MonoAssembly* +EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, std::function loader, bool ref_only) +{ const char *culture = mono_assembly_name_get_culture (aname); const char *asmname = mono_assembly_name_get_name (aname); @@ -185,19 +209,7 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only) } get_assembly_data (e, assembly_data, assembly_data_size); - -#if defined (NET6) && defined (NET6_ALC_WORKS) - image = mono_image_open_from_data_alc ( - alc_gchandle, - assembly_data, - assembly_data_size, - 0 /* need_copy */, - nullptr /* status */, - name.get () - ); -#else // (def NET6 && def NET6_ALC_WORKS) - image = mono_image_open_from_data_with_name (assembly_data, assembly_data_size, 0, nullptr, ref_only, name.get ()); -#endif // !(def NET6 && def NET6_ALC_WORKS) + image = loader (assembly_data, assembly_data_size, name.get ()); if (image == nullptr) { continue; } @@ -215,51 +227,49 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only) log_info_nocheck (LOG_ASSEMBLY, "open_from_bundles: loaded assembly: %p\n", a); } -#if defined (NET6) && defined (NET6_ALC_WORKS) - if (error != nullptr) { - error->error_code = a == nullptr ? MONO_ERROR_NONE : MONO_ERROR_FILE_NOT_FOUND; - } -#endif // def NET6 && def NET6_ALC_WORKS return a; } -#if defined (NET6) && defined (NET6_ALC_WORKS) +#if defined (NET6) MonoAssembly* EmbeddedAssemblies::open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data, MonoError *error) { - log_warn (LOG_DEFAULT, __PRETTY_FUNCTION__); return embeddedAssemblies.open_from_bundles (aname, alc_gchandle, error); } -#else // def NET6 && def NET6_ALC_WORKS +#else // def NET6 +MonoAssembly* +EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data) +{ + return embeddedAssemblies.open_from_bundles (aname, true); +} +#endif // ndef NET6 + MonoAssembly* EmbeddedAssemblies::open_from_bundles_full (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data) { return embeddedAssemblies.open_from_bundles (aname, false); } -MonoAssembly* -EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data) +void +EmbeddedAssemblies::install_preload_hooks_for_appdomains () { - return embeddedAssemblies.open_from_bundles (aname, true); + mono_install_assembly_preload_hook (open_from_bundles_full, nullptr); +#if !defined (NET6) + mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr); +#endif // ndef NET6 } -#endif // !(def NET6 && def NET6_ALC_WORKS) +#if defined (NET6) void -EmbeddedAssemblies::install_preload_hooks () +EmbeddedAssemblies::install_preload_hooks_for_alc () { -#if defined (NET6) && defined (NET6_ALC_WORKS) mono_install_assembly_preload_hook_v3 ( open_from_bundles, nullptr /* user_data */, 0 /* append */ ); -#else // def NET6 && def NET6_ALC_WORKS - mono_install_assembly_preload_hook (open_from_bundles_full, nullptr); -#if !defined (NET6) // Reference-only loads don't exist in NET6 - mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr); -#endif // !def NET6 -#endif // !(def NET6 && def NET6_ALC_WORKS) } +#endif // def NET6 template const Entry* diff --git a/src/monodroid/jni/embedded-assemblies.hh b/src/monodroid/jni/embedded-assemblies.hh index 204b14d1a0a..88eb07056e8 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -50,7 +51,10 @@ namespace xamarin::android::internal { #endif const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid); - void install_preload_hooks (); + void install_preload_hooks_for_appdomains (); +#if defined (NET6) + void install_preload_hooks_for_alc (); +#endif // def NET6 MonoReflectionType* typemap_java_to_managed (MonoString *java_type); /* returns current number of *all* assemblies found from all invocations */ @@ -93,11 +97,12 @@ namespace xamarin::android::internal { MonoReflectionType* typemap_java_to_managed (const char *java_type_name); size_t register_from (const char *apk_file, monodroid_should_register should_register); void gather_bundled_assemblies_from_apk (const char* apk, monodroid_should_register should_register); -#if defined (NET6) && defined (NET6_ALC_WORKS) +#if defined (NET6) MonoAssembly* open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error); -#else // def NET6 && def NET6_ALC_WORKS +#endif // def NET6 MonoAssembly* open_from_bundles (MonoAssemblyName* aname, bool ref_only); -#endif // !(def NET6 && def NET6_ALC_WORKS) + MonoAssembly* open_from_bundles (MonoAssemblyName* aname, std::function loader, bool ref_only); + #if defined (DEBUG) || !defined (ANDROID) template bool typemap_read_header (int dir_fd, const char *file_type, const char *dir_path, const char *file_path, uint32_t expected_magic, H &header, size_t &file_size, int &fd); @@ -111,12 +116,12 @@ namespace xamarin::android::internal { bool register_debug_symbols_for_assembly (const char *entry_name, MonoBundledAssembly *assembly, const mono_byte *debug_contents, int debug_size); static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, uint32_t size, const char* filename, const char* apk); -#if defined (NET6) && defined (NET6_ALC_WORKS) - static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error); -#else // def NET6 && def NET6_ALC_WORKS static MonoAssembly* open_from_bundles_full (MonoAssemblyName *aname, char **assemblies_path, void *user_data); +#if defined (NET6) + static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error); +#else // def NET6 static MonoAssembly* open_from_bundles_refonly (MonoAssemblyName *aname, char **assemblies_path, void *user_data); -#endif // !(def NET6 && def NET6_ALC_WORKS) +#endif // ndef NET6 static void get_assembly_data (const MonoBundledAssembly *e, char*& assembly_data, uint32_t& assembly_data_size); void zip_load_entries (int fd, const char *apk_name, monodroid_should_register should_register); diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index 0a416a77036..938009ee57c 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -64,8 +64,11 @@ namespace xamarin::android::internal true >; + using load_assemblies_context_type = MonoAssemblyLoadContextGCHandle; static constexpr pinvoke_library_map::size_type LIBRARY_MAP_INITIAL_BUCKET_COUNT = 1; -#endif // def NET6 +#else // def NET6 + using load_assemblies_context_type = MonoDomain*; +#endif // ndef NET6 #if defined (DEBUG) && !defined (WINDOWS) struct RuntimeOptions { @@ -107,6 +110,9 @@ namespace xamarin::android::internal }; private: + static constexpr char MONO_ANDROID_ASSEMBLY_NAME[] = "Mono.Android"; + static constexpr char JAVA_INTEROP_ASSEMBLY_NAME[] = "Java.Interop"; + static constexpr size_t SMALL_STRING_PARSE_BUFFER_LEN = 50; static constexpr bool is_running_on_desktop = #if ANDROID @@ -222,7 +228,11 @@ namespace xamarin::android::internal void disable_external_signal_handlers (); void lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info); void load_assembly (MonoDomain *domain, jstring_wrapper &assembly); - void load_assemblies (MonoDomain *domain, bool preload, jstring_array_wrapper &assemblies); +#if defined (NET6) + void load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly); +#endif // ndef NET6 + void load_assemblies (load_assemblies_context_type ctx, bool preload, jstring_array_wrapper &assemblies); + void set_debug_options (); void parse_gdb_options (); void mono_runtime_init (dynamic_local_string& runtime_args); @@ -302,6 +312,8 @@ namespace xamarin::android::internal int current_context_id = -1; #if defined (NET6) + MonoAssemblyLoadContextGCHandle default_alc = nullptr; + static std::mutex pinvoke_map_write_lock; static pinvoke_api_map xa_pinvoke_map; static pinvoke_library_map other_pinvoke_map; diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 47dbad2322c..39e13cf73b2 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -837,7 +837,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_string ("RootDomain"), const_cast ("mobile")); +#if !defined (NET6) } else { MonoDomain* root_domain = mono_get_root_domain (); @@ -909,6 +912,7 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks domain = utils.monodroid_create_appdomain (root_domain, domain_name.get (), /*shadow_copy:*/ 1, /*shadow_directory:*/ androidSystem.get_override_dir (0)); } +#endif // ndef NET6 if constexpr (is_running_on_desktop) { if (is_root_domain) { @@ -1002,7 +1006,12 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass Class_getName = env->GetMethodID (init.grefClass, "getName", "()Ljava/lang/String;"); init.Class_forName = env->GetStaticMethodID (init.grefClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); - MonoAssembly *assm = utils.monodroid_load_assembly (domain, "Mono.Android"); + MonoAssembly *assm; +#if defined (NET6) + assm = utils.monodroid_load_assembly (default_alc, MONO_ANDROID_ASSEMBLY_NAME); +#else // def NET6 + assm = utils.monodroid_load_assembly (domain, MONO_ANDROID_ASSEMBLY_NAME); +#endif // ndef NET6 MonoImage *image = mono_assembly_get_image (assm); uint32_t i = 0; @@ -1020,7 +1029,13 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass exit (FATAL_EXIT_MISSING_INIT); } - MonoAssembly *ji_assm = utils.monodroid_load_assembly (domain, "Java.Interop"); + MonoAssembly *ji_assm; +#if defined (NET6) + ji_assm = utils.monodroid_load_assembly (default_alc, JAVA_INTEROP_ASSEMBLY_NAME); +#else // def NET6 + ji_assm = utils.monodroid_load_assembly (domain, JAVA_INTEROP_ASSEMBLY_NAME); +#endif // ndef NET6 + MonoImage *ji_image = mono_assembly_get_image (ji_assm); for ( ; i < OSBridge::NUM_XA_GC_BRIDGE_TYPES + OSBridge::NUM_JI_GC_BRIDGE_TYPES; ++i) { lookup_bridge_info (domain, ji_image, &osBridge.get_java_gc_bridge_type (i), &osBridge.get_java_gc_bridge_info (i)); @@ -1072,7 +1087,12 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass MonoClass* MonodroidRuntime::get_android_runtime_class (MonoDomain *domain) { - MonoAssembly *assm = utils.monodroid_load_assembly (domain, "Mono.Android"); + MonoAssembly *assm; +#if defined (NET6) + assm = utils.monodroid_load_assembly (default_alc, MONO_ANDROID_ASSEMBLY_NAME); +#else // def NET6 + assm = utils.monodroid_load_assembly (domain, MONO_ANDROID_ASSEMBLY_NAME); +#endif // ndef NET6 MonoImage *image = mono_assembly_get_image (assm); MonoClass *runtime = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "JNIEnv"); @@ -1615,42 +1635,65 @@ MonodroidRuntime::disable_external_signal_handlers (void) } } +#if defined (NET6) inline void -MonodroidRuntime::load_assembly (MonoDomain *domain, jstring_wrapper &assembly) +MonodroidRuntime::load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly) { + log_warn (LOG_ASSEMBLY, __PRETTY_FUNCTION__); + timing_period total_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) total_time.mark_start (); const char *assm_name = assembly.get_cstr (); - MonoAssemblyName *aname; + MonoAssemblyName *aname = mono_assembly_name_new (assm_name); - aname = mono_assembly_name_new (assm_name); + MonoImageOpenStatus open_status; + mono_assembly_load_full_alc (alc_handle, aname, nullptr /* basedir */, &open_status); + + mono_assembly_name_free (aname); + + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + total_time.mark_end (); + TIMING_LOG_INFO (total_time, "Assembly load (ALC): %s", assm_name); + } +} +#endif // NET6 + +inline void +MonodroidRuntime::load_assembly (MonoDomain *domain, jstring_wrapper &assembly) +{ + timing_period total_time; + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) + total_time.mark_start (); + + const char *assm_name = assembly.get_cstr (); + MonoAssemblyName *aname = mono_assembly_name_new (assm_name); #ifndef ANDROID if (designerAssemblies.has_assemblies () && designerAssemblies.try_load_assembly (domain, aname) != nullptr) { log_debug (LOG_ASSEMBLY, "Dynamically opened assembly %s", mono_assembly_name_get_name (aname)); } else #endif - if (domain != mono_domain_get ()) { - MonoDomain *current = mono_domain_get (); - mono_domain_set (domain, FALSE); - mono_assembly_load_full (aname, NULL, NULL, 0); - mono_domain_set (current, FALSE); - } else { - mono_assembly_load_full (aname, NULL, NULL, 0); - } + if (domain != mono_domain_get ()) { + MonoDomain *current = mono_domain_get (); + mono_domain_set (domain, FALSE); + mono_assembly_load_full (aname, NULL, NULL, 0); + mono_domain_set (current, FALSE); + } else { + mono_assembly_load_full (aname, NULL, NULL, 0); + } mono_assembly_name_free (aname); if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); - TIMING_LOG_INFO (total_time, "Assembly load: %s preloaded", assm_name); + TIMING_LOG_INFO (total_time, "Assembly load (domain): %s", assm_name); } } inline void -MonodroidRuntime::load_assemblies (MonoDomain *domain, bool preload, jstring_array_wrapper &assemblies) +MonodroidRuntime::load_assemblies (load_assemblies_context_type ctx, bool preload, jstring_array_wrapper &assemblies) { timing_period total_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) @@ -1658,7 +1701,7 @@ MonodroidRuntime::load_assemblies (MonoDomain *domain, bool preload, jstring_arr for (size_t i = 0; i < assemblies.get_length (); ++i) { jstring_wrapper &assembly = assemblies [i]; - load_assembly (domain, assembly); + load_assembly (ctx, assembly); // only load the first "main" assembly if we are not preloading. if (!preload) break; @@ -1692,12 +1735,25 @@ MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass } } +#if defined (NET6) + default_alc = mono_alc_get_default_gchandle (); + abort_unless (default_alc != nullptr, "Default AssemblyLoadContext not found"); + + embeddedAssemblies.install_preload_hooks_for_alc (); + log_debug (LOG_ASSEMBLY, "ALC hooks installed"); +#endif // def NET6 + #ifndef ANDROID if (assembliesBytes != nullptr) designerAssemblies.add_or_update_from_java (domain, env, assemblies, assembliesBytes, assembliesPaths); #endif bool preload = (androidSystem.is_assembly_preload_enabled () || (is_running_on_desktop && force_preload_assemblies)); - load_assemblies (domain, preload, assemblies); + +#if defined (NET6) + load_assemblies (default_alc, preload, assemblies); +#else // def NET6 + load_assemblies (domain, preload, assemblies); +#endif // ndef NET6 init_android_runtime (domain, env, runtimeClass, loader); osBridge.add_monodroid_domain (domain); @@ -2197,7 +2253,7 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag MonoMethod *register_jni_natives = registerType; if constexpr (is_running_on_desktop) { - MonoClass *runtime = utils.monodroid_get_class_from_name (domain, "Mono.Android", "Android.Runtime", "JNIEnv"); + MonoClass *runtime = utils.monodroid_get_class_from_name (domain, MONO_ANDROID_ASSEMBLY_NAME, "Android.Runtime", "JNIEnv"); register_jni_natives = mono_class_get_method_from_name (runtime, "RegisterJniNatives", 5); } utils.monodroid_runtime_invoke (domain, register_jni_natives, nullptr, args, nullptr); diff --git a/src/monodroid/jni/util.cc b/src/monodroid/jni/util.cc index 5c2f879c210..3f6dec3ba4d 100644 --- a/src/monodroid/jni/util.cc +++ b/src/monodroid/jni/util.cc @@ -173,6 +173,24 @@ Util::monodroid_store_package_name (const char *name) log_info (LOG_DEFAULT, "Generated hash 0x%s for package name %s", package_property_suffix, name); } +#if defined (NET6) +MonoAssembly* +Util::monodroid_load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, const char *basename) +{ + MonoImageOpenStatus status; + MonoAssemblyName *aname = mono_assembly_name_new (basename); + MonoAssembly *assm = mono_assembly_load_full_alc (alc_handle, aname, nullptr, &status); + + mono_assembly_name_free (aname); + + if (assm == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { + log_fatal (LOG_DEFAULT, "Unable to find assembly '%s'.", basename); + exit (FATAL_EXIT_MISSING_ASSEMBLY); + } + return assm; +} +#endif // def NET6 + MonoAssembly * Util::monodroid_load_assembly (MonoDomain *domain, const char *basename) { diff --git a/src/monodroid/jni/util.hh b/src/monodroid/jni/util.hh index 15b2fa02938..173f6375edf 100644 --- a/src/monodroid/jni/util.hh +++ b/src/monodroid/jni/util.hh @@ -39,6 +39,10 @@ constexpr int FALSE = 0; #include "basic-utilities.hh" #endif +#if defined (NET6) +#include +#endif // def NET6 + #include "java-interop-util.h" #include "logger.hh" @@ -81,6 +85,9 @@ namespace xamarin::android int monodroid_getpagesize (); void monodroid_store_package_name (const char *name); MonoAssembly *monodroid_load_assembly (MonoDomain *domain, const char *basename); +#if defined (NET6) + MonoAssembly *monodroid_load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, const char *basename); +#endif MonoObject *monodroid_runtime_invoke (MonoDomain *domain, MonoMethod *method, void *obj, void **params, MonoObject **exc); MonoClass *monodroid_get_class_from_name (MonoDomain *domain, const char* assembly, const char *_namespace, const char *type); MonoDomain *monodroid_create_appdomain (MonoDomain *parent_domain, const char *friendly_name, int shadow_copy, const char *shadow_directories);